From 6a443620e73ff76540843a450693da61ff361086 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Thu, 21 May 2020 01:59:35 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20IFreeSql.InsertOrUpdat?= =?UTF-8?q?e=20=E6=96=B9=E6=B3=95=20#316?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DbContext/DbContextScopedFreeSql.cs | 7 +- FreeSql.DbContext/FreeSql.DbContext.xml | 16 - .../Curd/MySqlInsertOrUpdateTest.cs | 167 +++++++++ .../Dameng/Curd/DamengInsertOrUpdateTest.cs | 323 +++++++++++++++++ .../MySql/Curd/MySqlInsertOrUpdateTest.cs | 167 +++++++++ .../MySql/Curd/OnDuplicateKeyUpdateTest.cs | 195 +++++++++++ .../Oracle/Curd/OracleInsertOrUpdateTest.cs | 323 +++++++++++++++++ .../PostgreSQL/Curd/OnConflictDoUpdateTest.cs | 158 +++++++++ .../Curd/PostgreSQLInsertOrUpdateTest.cs | 205 +++++++++++ .../Curd/SqlServerInsertOrUpdateTest.cs | 323 +++++++++++++++++ .../Dameng/Curd/DamengInsertOrUpdateTest.cs | 323 +++++++++++++++++ .../MySql/Curd/MySqlInsertOrUpdateTest.cs | 167 +++++++++ .../Oracle/Curd/OracleInsertOrUpdateTest.cs | 323 +++++++++++++++++ .../Curd/PostgreSQLInsertOrUpdateTest.cs | 205 +++++++++++ .../Curd/SqlServerInsertOrUpdateTest.cs | 325 ++++++++++++++++++ .../Sqlite/Curd/SqliteInsertOrUpdateTest.cs | 165 +++++++++ FreeSql/FreeSql.xml | 210 ++++------- FreeSql/Interface/Curd/IInsertOrUpdate.cs | 66 ++++ FreeSql/Interface/IAop.cs | 4 +- FreeSql/Interface/IFreeSql.cs | 13 + .../CommonProvider/InsertOrUpdateProvider.cs | 222 ++++++++++++ .../Internal/CommonProvider/InsertProvider.cs | 30 +- .../Internal/CommonProvider/UpdateProvider.cs | 38 +- .../Curd/DamengInsertOrUpdate.cs | 43 +++ .../FreeSql.Provider.Dameng/DamengProvider.cs | 1 + .../MsAccessProvider.cs | 1 + .../Curd/MySqlInsertOrUpdate.cs | 29 ++ .../Curd/OnDuplicateKeyUpdate.cs | 5 - .../FreeSql.Provider.MySql/MySqlProvider.cs | 1 + .../Dameng/Curd/OdbcDamengInsertOrUpdate.cs | 43 +++ .../Dameng/OdbcDamengProvider.cs | 1 + .../Default/OdbcProvider.cs | 1 + .../MySql/Curd/OdbcMySqlInsert.cs | 20 ++ .../MySql/Curd/OdbcMySqlInsertOrUpdate.cs | 29 ++ .../Curd/OdbcMySqlOnDuplicateKeyUpdate.cs | 166 +++++++++ .../MySql/Curd/OdbcMySqlUpdate.cs | 8 +- .../MySql/OdbcMySqlProvider.cs | 1 + .../Oracle/Curd/OdbcOracleInsertOrUpdate.cs | 43 +++ .../Oracle/OdbcOracleProvider.cs | 1 + .../PostgreSQL/Curd/OdbcPostgreSQLInsert.cs | 13 + .../Curd/OdbcPostgreSQLInsertOrUpdate.cs | 33 ++ .../Curd/OdbcPostgreSQLOnConflictDoUpdate.cs | 207 +++++++++++ .../PostgreSQL/Curd/OdbcPostgreSQLUpdate.cs | 10 + .../PostgreSQL/OdbcPostgreSQLProvider.cs | 1 + .../SqlServer/Curd/OdbcSqlServerInsert.cs | 2 - .../Curd/OdbcSqlServerInsertOrUpdate.cs | 43 +++ .../SqlServer/OdbcSqlServerProvider.cs | 1 + .../Curd/OracleInsertOrUpdate.cs | 43 +++ .../FreeSql.Provider.Oracle/OracleProvider.cs | 1 + .../Curd/OnConflictDoUpdate.cs | 3 - .../Curd/PostgreSQLInsertOrUpdate.cs | 33 ++ .../PostgreSQLProvider.cs | 1 + .../Curd/SqlServerInsert.cs | 2 - .../Curd/SqlServerInsertOrUpdate.cs | 43 +++ .../SqlServerProvider.cs | 1 + .../Curd/SqliteInsertOrUpdate.cs | 30 ++ .../FreeSql.Provider.Sqlite/SqliteProvider.cs | 1 + 57 files changed, 4627 insertions(+), 209 deletions(-) create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Dameng/Curd/DamengInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/MySqlInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/OnDuplicateKeyUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Oracle/Curd/OracleInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/OnConflictDoUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests.Provider.Odbc/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/Dameng/Curd/DamengInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/Oracle/Curd/OracleInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteInsertOrUpdateTest.cs create mode 100644 FreeSql/Interface/Curd/IInsertOrUpdate.cs create mode 100644 FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs create mode 100644 Providers/FreeSql.Provider.Dameng/Curd/DamengInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.MySql/Curd/MySqlInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/Dameng/Curd/OdbcDamengInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlOnDuplicateKeyUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/Oracle/Curd/OdbcOracleInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs create mode 100644 Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Oracle/Curd/OracleInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.Sqlite/Curd/SqliteInsertOrUpdate.cs diff --git a/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs b/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs index 2aabe397..d82d4224 100644 --- a/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs +++ b/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs @@ -76,6 +76,11 @@ namespace FreeSql public IInsert Insert(T1[] source) where T1 : class => Insert().AppendData(source); public IInsert Insert(List source) where T1 : class => Insert().AppendData(source); public IInsert Insert(IEnumerable source) where T1 : class => Insert().AppendData(source); - + public IInsertOrUpdate InsertOrUpdate() where T1 : class + { + var db = _resolveDbContext?.Invoke(); + db?.FlushCommand(); + return _originalFsql.InsertOrUpdate().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction()); + } } } diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 4854f49c..132d875e 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -125,13 +125,6 @@ 清空状态数据 - - - 根据 lambda 条件删除数据 - - - - 添加 @@ -486,14 +479,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertOrUpdateTest.cs new file mode 100644 index 00000000..6198c553 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertOrUpdateTest.cs @@ -0,0 +1,167 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.MySqlConnector +{ + public class MySqlInsertOrUpdateTest + { + + IFreeSql fsql => g.mysql; + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '01') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '011') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(2, '02') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '01'), (2, '02'), (3, '03'), (4, '04') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(5, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(8, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '01') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '011') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(2, '02', '02') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '01'), (2, '02', '02'), (3, '03', '03'), (4, '04', '04') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(5, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '001'), (2, '02', '002'), (3, '03', '003'), (4, '04', '004') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(8, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '01', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '011', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(2, '02', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '01', 0, now(3)), (2, '02', 0, now(3)), (3, '03', 0, now(3)), (4, '04', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(6, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '001', 0, now(3)), (2, '002', 0, now(3)), (3, '003', 0, now(3)), (4, '004', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(8, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Dameng/Curd/DamengInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Dameng/Curd/DamengInsertOrUpdateTest.cs new file mode 100644 index 00000000..e3832aaa --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Dameng/Curd/DamengInsertOrUpdateTest.cs @@ -0,0 +1,323 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.Dameng +{ + public class DamengInsertOrUpdateTest + { + IFreeSql fsql => g.dameng; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 2 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '011' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 2 as ID, '02' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02' FROM dual +UNION ALL + SELECT 3, '03' FROM dual +UNION ALL + SELECT 4, '04' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '001' as NAME FROM dual +UNION ALL + SELECT 2, '002' FROM dual +UNION ALL + SELECT 3, '003' FROM dual +UNION ALL + SELECT 4, '004' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '02' as ID2, '011' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 2 as ID1, '02' as ID2, '02' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02', '02' FROM dual +UNION ALL + SELECT 3, '03', '03' FROM dual +UNION ALL + SELECT 4, '04', '04' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '001' as NAME FROM dual +UNION ALL + SELECT 2, '02', '002' FROM dual +UNION ALL + SELECT 3, '03', '003' FROM dual +UNION ALL + SELECT 4, '04', '004' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '011' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 2 as ID, '02' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '02', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '03', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '04', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '001' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '002', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '003', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '004', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/MySqlInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/MySqlInsertOrUpdateTest.cs new file mode 100644 index 00000000..3b9fc212 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/MySqlInsertOrUpdateTest.cs @@ -0,0 +1,167 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.MySql +{ + public class MySqlInsertOrUpdateTest + { + + IFreeSql fsql => g.mysql; + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '01') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '011') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(2, '02') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '01'), (2, '02'), (3, '03'), (4, '04') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '01') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '011') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(2, '02', '02') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '01'), (2, '02', '02'), (3, '03', '03'), (4, '04', '04') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '001'), (2, '02', '002'), (3, '03', '003'), (4, '04', '004') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '01', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '011', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(2, '02', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '01', 0, now(3)), (2, '02', 0, now(3)), (3, '03', 0, now(3)), (4, '04', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '001', 0, now(3)), (2, '002', 0, now(3)), (3, '003', 0, now(3)), (4, '004', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/OnDuplicateKeyUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/OnDuplicateKeyUpdateTest.cs new file mode 100644 index 00000000..1bf715b6 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/MySql/Curd/OnDuplicateKeyUpdateTest.cs @@ -0,0 +1,195 @@ +using FreeSql.DataAnnotations; +using FreeSql.Odbc.MySql; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.MySql +{ + public class OnDuplicateKeyUpdateTest + { + class TestOnDuplicateKeyUpdateInfo + { + [Column(IsIdentity = true)] + public int id { get; set; } + public string title { get; set; } + public DateTime time { get; set; } + } + + [Fact] + public void ExecuteAffrows() + { + g.mysql.Delete(new[] { 100, 101, 102 }).ExecuteAffrows(); + var odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 100, title = "title-100", time = DateTime.Parse("2000-01-01") }).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(100, 'title-100', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`title` = VALUES(`title`), +`time` = VALUES(`time`)", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 100, title = "title-100", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 101, title = "title-101", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 102, title = "title-102", time = DateTime.Parse("2000-01-01") } + }).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(100, 'title-100', '2000-01-01 00:00:00.000'), (101, 'title-101', '2000-01-01 00:00:00.000'), (102, 'title-102', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`title` = VALUES(`title`), +`time` = VALUES(`time`)", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + + [Fact] + public void IgnoreColumns() + { + g.mysql.Delete(new[] { 200, 201, 202 }).ExecuteAffrows(); + var odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(200, 'title-200') +ON DUPLICATE KEY UPDATE +`title` = VALUES(`title`), +`time` = '2000-01-01 00:00:00.000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 201, title = "title-201", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 202, title = "title-202", time = DateTime.Parse("2000-01-01") } + }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(200, 'title-200'), (201, 'title-201'), (202, 'title-202') +ON DUPLICATE KEY UPDATE +`title` = VALUES(`title`), +`time` = CASE `id` +WHEN 200 THEN '2000-01-01 00:00:00.000' +WHEN 201 THEN '2000-01-01 00:00:00.000' +WHEN 202 THEN '2000-01-01 00:00:00.000' END", odku2.ToSql()); + odku2.ExecuteAffrows(); + + + g.mysql.Delete(new[] { 200, 201, 202 }).ExecuteAffrows(); + odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()).IgnoreColumns(a => a.title); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(200, 'title-200') +ON DUPLICATE KEY UPDATE +`time` = '2000-01-01 00:00:00.000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 201, title = "title-201", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 202, title = "title-202", time = DateTime.Parse("2000-01-01") } + }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()).IgnoreColumns(a => a.title); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(200, 'title-200'), (201, 'title-201'), (202, 'title-202') +ON DUPLICATE KEY UPDATE +`time` = CASE `id` +WHEN 200 THEN '2000-01-01 00:00:00.000' +WHEN 201 THEN '2000-01-01 00:00:00.000' +WHEN 202 THEN '2000-01-01 00:00:00.000' END", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + + [Fact] + public void UpdateColumns() + { + g.mysql.Delete(new[] { 300, 301, 302 }).ExecuteAffrows(); + var odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(300, 'title-300') +ON DUPLICATE KEY UPDATE +`title` = VALUES(`title`), +`time` = '2000-01-01 00:00:00.000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 301, title = "title-301", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 302, title = "title-302", time = DateTime.Parse("2000-01-01") } + }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(300, 'title-300'), (301, 'title-301'), (302, 'title-302') +ON DUPLICATE KEY UPDATE +`title` = VALUES(`title`), +`time` = CASE `id` +WHEN 300 THEN '2000-01-01 00:00:00.000' +WHEN 301 THEN '2000-01-01 00:00:00.000' +WHEN 302 THEN '2000-01-01 00:00:00.000' END", odku2.ToSql()); + odku2.ExecuteAffrows(); + + + g.mysql.Delete(new[] { 300, 301, 302 }).ExecuteAffrows(); + odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()).UpdateColumns(a => a.time); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(300, 'title-300') +ON DUPLICATE KEY UPDATE +`time` = '2000-01-01 00:00:00.000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 301, title = "title-301", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 302, title = "title-302", time = DateTime.Parse("2000-01-01") } + }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()).UpdateColumns(a => a.time); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`) VALUES(300, 'title-300'), (301, 'title-301'), (302, 'title-302') +ON DUPLICATE KEY UPDATE +`time` = CASE `id` +WHEN 300 THEN '2000-01-01 00:00:00.000' +WHEN 301 THEN '2000-01-01 00:00:00.000' +WHEN 302 THEN '2000-01-01 00:00:00.000' END", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + + [Fact] + public void Set() + { + g.mysql.Delete(new[] { 400, 401, 402 }).ExecuteAffrows(); + var odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }).NoneParameter().InsertIdentity()).Set(a => a.time, DateTime.Parse("2020-1-1")); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(400, 'title-400', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`time` = '2020-01-01 00:00:00.000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 401, title = "title-401", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 402, title = "title-402", time = DateTime.Parse("2000-01-01") } + }).NoneParameter().InsertIdentity()).Set(a => a.time, DateTime.Parse("2020-1-1")); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(400, 'title-400', '2000-01-01 00:00:00.000'), (401, 'title-401', '2000-01-01 00:00:00.000'), (402, 'title-402', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`time` = '2020-01-01 00:00:00.000'", odku2.ToSql()); + odku2.ExecuteAffrows(); + + + var dt2020 = DateTime.Parse("2020-1-1"); + g.mysql.Delete(new[] { 400, 401, 402 }).ExecuteAffrows(); + odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }).NoneParameter().InsertIdentity()).Set(a => a.time == dt2020); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(400, 'title-400', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`time` = '2020-01-01 00:00:00.000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 401, title = "title-401", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 402, title = "title-402", time = DateTime.Parse("2000-01-01") } + }).NoneParameter().InsertIdentity()).Set(a => a.time == dt2020); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(400, 'title-400', '2000-01-01 00:00:00.000'), (401, 'title-401', '2000-01-01 00:00:00.000'), (402, 'title-402', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`time` = '2020-01-01 00:00:00.000'", odku2.ToSql()); + odku2.ExecuteAffrows(); + + + g.mysql.Delete(new[] { 400, 401, 402 }).ExecuteAffrows(); + odku1 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new TestOnDuplicateKeyUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }).NoneParameter().InsertIdentity()).Set(a => new { time = dt2020, title = a.title + "123" }); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(400, 'title-400', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`time` = '2020-01-01 00:00:00.000', `title` = concat(`title`, '123')", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + odku2 = new OdbcMySqlOnDuplicateKeyUpdate(g.mysql.Insert(new[] { + new TestOnDuplicateKeyUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 401, title = "title-401", time = DateTime.Parse("2000-01-01") }, + new TestOnDuplicateKeyUpdateInfo { id = 402, title = "title-402", time = DateTime.Parse("2000-01-01") } + }).NoneParameter().InsertIdentity()).Set(a => new { time = dt2020, title = a.title + "123" }); + Assert.Equal(@"INSERT INTO `TestOnDuplicateKeyUpdateInfo`(`id`, `title`, `time`) VALUES(400, 'title-400', '2000-01-01 00:00:00.000'), (401, 'title-401', '2000-01-01 00:00:00.000'), (402, 'title-402', '2000-01-01 00:00:00.000') +ON DUPLICATE KEY UPDATE +`time` = '2020-01-01 00:00:00.000', `title` = concat(`title`, '123')", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Oracle/Curd/OracleInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Oracle/Curd/OracleInsertOrUpdateTest.cs new file mode 100644 index 00000000..156bebbf --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/Oracle/Curd/OracleInsertOrUpdateTest.cs @@ -0,0 +1,323 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.Oracle +{ + public class OracleInsertOrUpdateTest + { + IFreeSql fsql => g.oracle; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 2 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '011' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 2 as ID, '02' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02' FROM dual +UNION ALL + SELECT 3, '03' FROM dual +UNION ALL + SELECT 4, '04' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '001' as NAME FROM dual +UNION ALL + SELECT 2, '002' FROM dual +UNION ALL + SELECT 3, '003' FROM dual +UNION ALL + SELECT 4, '004' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '02' as ID2, '011' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 2 as ID1, '02' as ID2, '02' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02', '02' FROM dual +UNION ALL + SELECT 3, '03', '03' FROM dual +UNION ALL + SELECT 4, '04', '04' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '001' as NAME FROM dual +UNION ALL + SELECT 2, '02', '002' FROM dual +UNION ALL + SELECT 3, '03', '003' FROM dual +UNION ALL + SELECT 4, '04', '004' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '011' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 2 as ID, '02' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '02', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '03', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '04', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '001' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '002', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '003', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '004', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/OnConflictDoUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/OnConflictDoUpdateTest.cs new file mode 100644 index 00000000..9ce29662 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/OnConflictDoUpdateTest.cs @@ -0,0 +1,158 @@ +using FreeSql.DataAnnotations; +using FreeSql.Odbc.PostgreSQL; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.PostgreSQL +{ + public class OnConflictDoUpdateTest + { + class TestOnConflictDoUpdateInfo + { + [Column(IsIdentity = true)] + public int id { get; set; } + public string title { get; set; } + public DateTime? time { get; set; } + } + + [Fact] + public void ExecuteAffrows() + { + g.pgsql.Delete(new[] { 100, 101, 102 }).ExecuteAffrows(); + var odku1 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new TestOnConflictDoUpdateInfo { id = 100, title = "title-100", time = DateTime.Parse("2000-01-01") }).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"", ""time"") VALUES(100, 'title-100', '2000-01-01 00:00:00.000000') +ON CONFLICT(""id"") DO UPDATE SET +""title"" = EXCLUDED.""title"", +""time"" = EXCLUDED.""time""", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new[] { + new TestOnConflictDoUpdateInfo { id = 100, title = "title-100", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 101, title = "title-101", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 102, title = "title-102", time = DateTime.Parse("2000-01-01") } + }).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"", ""time"") VALUES(100, 'title-100', '2000-01-01 00:00:00.000000'), (101, 'title-101', '2000-01-01 00:00:00.000000'), (102, 'title-102', '2000-01-01 00:00:00.000000') +ON CONFLICT(""id"") DO UPDATE SET +""title"" = EXCLUDED.""title"", +""time"" = EXCLUDED.""time""", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + + [Fact] + public void IgnoreColumns() + { + g.pgsql.Delete(new[] { 200, 201, 202 }).ExecuteAffrows(); + var odku1 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new TestOnConflictDoUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(200, 'title-200') +ON CONFLICT(""id"") DO UPDATE SET +""title"" = EXCLUDED.""title"", +""time"" = '2000-01-01 00:00:00.000000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new[] { + new TestOnConflictDoUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 201, title = "title-201", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 202, title = "title-202", time = DateTime.Parse("2000-01-01") } + }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(200, 'title-200'), (201, 'title-201'), (202, 'title-202') +ON CONFLICT(""id"") DO UPDATE SET +""title"" = EXCLUDED.""title"", +""time"" = CASE EXCLUDED.""id"" +WHEN 200 THEN '2000-01-01 00:00:00.000000' +WHEN 201 THEN '2000-01-01 00:00:00.000000' +WHEN 202 THEN '2000-01-01 00:00:00.000000' END::timestamp", odku2.ToSql()); + odku2.ExecuteAffrows(); + + + g.pgsql.Delete(new[] { 200, 201, 202 }).ExecuteAffrows(); + odku1 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new TestOnConflictDoUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()).IgnoreColumns(a => a.title); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(200, 'title-200') +ON CONFLICT(""id"") DO UPDATE SET +""time"" = '2000-01-01 00:00:00.000000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + odku2 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new[] { + new TestOnConflictDoUpdateInfo { id = 200, title = "title-200", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 201, title = "title-201", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 202, title = "title-202", time = DateTime.Parse("2000-01-01") } + }).IgnoreColumns(a => a.time).NoneParameter().InsertIdentity()).IgnoreColumns(a => a.title); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(200, 'title-200'), (201, 'title-201'), (202, 'title-202') +ON CONFLICT(""id"") DO UPDATE SET +""time"" = CASE EXCLUDED.""id"" +WHEN 200 THEN '2000-01-01 00:00:00.000000' +WHEN 201 THEN '2000-01-01 00:00:00.000000' +WHEN 202 THEN '2000-01-01 00:00:00.000000' END::timestamp", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + + [Fact] + public void UpdateColumns() + { + g.pgsql.Delete(new[] { 300, 301, 302 }).ExecuteAffrows(); + var odku1 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new TestOnConflictDoUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(300, 'title-300') +ON CONFLICT(""id"") DO UPDATE SET +""title"" = EXCLUDED.""title"", +""time"" = '2000-01-01 00:00:00.000000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new[] { + new TestOnConflictDoUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 301, title = "title-301", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 302, title = "title-302", time = DateTime.Parse("2000-01-01") } + }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(300, 'title-300'), (301, 'title-301'), (302, 'title-302') +ON CONFLICT(""id"") DO UPDATE SET +""title"" = EXCLUDED.""title"", +""time"" = CASE EXCLUDED.""id"" +WHEN 300 THEN '2000-01-01 00:00:00.000000' +WHEN 301 THEN '2000-01-01 00:00:00.000000' +WHEN 302 THEN '2000-01-01 00:00:00.000000' END::timestamp", odku2.ToSql()); + odku2.ExecuteAffrows(); + + + g.pgsql.Delete(new[] { 300, 301, 302 }).ExecuteAffrows(); + odku1 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new TestOnConflictDoUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()).UpdateColumns(a => a.time); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(300, 'title-300') +ON CONFLICT(""id"") DO UPDATE SET +""time"" = '2000-01-01 00:00:00.000000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + odku2 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new[] { + new TestOnConflictDoUpdateInfo { id = 300, title = "title-300", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 301, title = "title-301", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 302, title = "title-302", time = DateTime.Parse("2000-01-01") } + }).InsertColumns(a => a.title).NoneParameter().InsertIdentity()).UpdateColumns(a => a.time); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"") VALUES(300, 'title-300'), (301, 'title-301'), (302, 'title-302') +ON CONFLICT(""id"") DO UPDATE SET +""time"" = CASE EXCLUDED.""id"" +WHEN 300 THEN '2000-01-01 00:00:00.000000' +WHEN 301 THEN '2000-01-01 00:00:00.000000' +WHEN 302 THEN '2000-01-01 00:00:00.000000' END::timestamp", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + + [Fact] + public void Set() + { + g.pgsql.Delete(new[] { 400, 401, 402 }).ExecuteAffrows(); + var odku1 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new TestOnConflictDoUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }).NoneParameter().InsertIdentity()).Set(a => a.time, DateTime.Parse("2020-1-1")); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"", ""time"") VALUES(400, 'title-400', '2000-01-01 00:00:00.000000') +ON CONFLICT(""id"") DO UPDATE SET +""time"" = '2020-01-01 00:00:00.000000'", odku1.ToSql()); + Assert.Equal(1, odku1.ExecuteAffrows()); + + var odku2 = new OdbcPostgreSQLOnConflictDoUpdate(g.pgsql.Insert(new[] { + new TestOnConflictDoUpdateInfo { id = 400, title = "title-400", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 401, title = "title-401", time = DateTime.Parse("2000-01-01") }, + new TestOnConflictDoUpdateInfo { id = 402, title = "title-402", time = DateTime.Parse("2000-01-01") } + }).NoneParameter().InsertIdentity()).Set(a => a.time, DateTime.Parse("2020-1-1")); + Assert.Equal(@"INSERT INTO ""testonconflictdoupdateinfo""(""id"", ""title"", ""time"") VALUES(400, 'title-400', '2000-01-01 00:00:00.000000'), (401, 'title-401', '2000-01-01 00:00:00.000000'), (402, 'title-402', '2000-01-01 00:00:00.000000') +ON CONFLICT(""id"") DO UPDATE SET +""time"" = '2020-01-01 00:00:00.000000'", odku2.ToSql()); + odku2.ExecuteAffrows(); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs new file mode 100644 index 00000000..90ce4b5b --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs @@ -0,0 +1,205 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.PostgreSQL +{ + public class PostgreSQLInsertOrUpdateTest + { + IFreeSql fsql => g.pgsql; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(2) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1), (2), (3), (4) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1), (2), (3), (4) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '01') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '011') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(2, '02') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '01'), (2, '02'), (3, '03'), (4, '04') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '01') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '02', '011') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(2, '02', '02') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '01'), (2, '02', '02'), (3, '03', '03'), (4, '04', '04') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '001'), (2, '02', '002'), (3, '03', '003'), (4, '04', '004') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '01', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '011', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(2, '02', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '01', 0, current_timestamp), (2, '02', 0, current_timestamp), (3, '03', 0, current_timestamp), (4, '04', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '001', 0, current_timestamp), (2, '002', 0, current_timestamp), (3, '003', 0, current_timestamp), (4, '004', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs new file mode 100644 index 00000000..0c00dbb5 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs @@ -0,0 +1,323 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Odbc.SqlServer +{ + public class SqlServerInsertOrUpdateTest + { + IFreeSql fsql => g.sqlserver; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 2 as id ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id +UNION ALL + SELECT 2 +UNION ALL + SELECT 3 +UNION ALL + SELECT 4 ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id +UNION ALL + SELECT 2 +UNION ALL + SELECT 3 +UNION ALL + SELECT 4 ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'01' as name ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'011' as name ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 2 as id, N'02' as name ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'01' as name +UNION ALL + SELECT 2, N'02' +UNION ALL + SELECT 3, N'03' +UNION ALL + SELECT 4, N'04' ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'001' as name +UNION ALL + SELECT 2, N'002' +UNION ALL + SELECT 3, N'003' +UNION ALL + SELECT 4, N'004' ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'01' as id2, N'01' as name ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'02' as id2, N'011' as name ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 2 as id1, N'02' as id2, N'02' as name ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'01' as id2, N'01' as name +UNION ALL + SELECT 2, N'02', N'02' +UNION ALL + SELECT 3, N'03', N'03' +UNION ALL + SELECT 4, N'04', N'04' ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'01' as id2, N'001' as name +UNION ALL + SELECT 2, N'02', N'002' +UNION ALL + SELECT 3, N'03', N'003' +UNION ALL + SELECT 4, N'04', N'004' ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'01' as name, 0 as version, getdate() as CreateTime ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'011' as name, 0 as version, getdate() as CreateTime ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 2 as id, N'02' as name, 0 as version, getdate() as CreateTime ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'01' as name, 0 as version, getdate() as CreateTime +UNION ALL + SELECT 2, N'02', 0, getdate() +UNION ALL + SELECT 3, N'03', 0, getdate() +UNION ALL + SELECT 4, N'04', 0, getdate() ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'001' as name, 0 as version, getdate() as CreateTime +UNION ALL + SELECT 2, N'002', 0, getdate() +UNION ALL + SELECT 3, N'003', 0, getdate() +UNION ALL + SELECT 4, N'004', 0, getdate() ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/Dameng/Curd/DamengInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/Dameng/Curd/DamengInsertOrUpdateTest.cs new file mode 100644 index 00000000..54e4c38a --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/Dameng/Curd/DamengInsertOrUpdateTest.cs @@ -0,0 +1,323 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Dameng +{ + public class DamengInsertOrUpdateTest + { + IFreeSql fsql => g.dameng; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 2 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + iou.ExecuteAffrows(); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '011' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 2 as ID, '02' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02' FROM dual +UNION ALL + SELECT 3, '03' FROM dual +UNION ALL + SELECT 4, '04' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '001' as NAME FROM dual +UNION ALL + SELECT 2, '002' FROM dual +UNION ALL + SELECT 3, '003' FROM dual +UNION ALL + SELECT 4, '004' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '02' as ID2, '011' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 2 as ID1, '02' as ID2, '02' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02', '02' FROM dual +UNION ALL + SELECT 3, '03', '03' FROM dual +UNION ALL + SELECT 4, '04', '04' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '001' as NAME FROM dual +UNION ALL + SELECT 2, '02', '002' FROM dual +UNION ALL + SELECT 3, '03', '003' FROM dual +UNION ALL + SELECT 4, '04', '004' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '011' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 2 as ID, '02' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '02', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '03', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '04', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '001' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '002', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '003', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '004', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + iou.ExecuteAffrows(); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs new file mode 100644 index 00000000..3cfe1a62 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs @@ -0,0 +1,167 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.MySql +{ + public class MySqlInsertOrUpdateTest + { + + IFreeSql fsql => g.mysql; + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '01') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '011') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(2, '02') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '01'), (2, '02'), (3, '03'), (4, '04') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(5, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(8, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '01') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '011') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(2, '02', '02') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '01'), (2, '02', '02'), (3, '03', '03'), (4, '04', '04') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(5, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou03`(`id1`, `id2`, `name`) VALUES(1, '01', '001'), (2, '02', '002'), (3, '03', '003'), (4, '04', '004') +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`)", sql); + Assert.Equal(8, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '01', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '011', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(2, '02', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '01', 0, now(3)), (2, '02', 0, now(3)), (3, '03', 0, now(3)), (4, '04', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(6, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO `tbiou04`(`id`, `name`, `version`, `CreateTime`) VALUES(1, '001', 0, now(3)), (2, '002', 0, now(3)), (3, '003', 0, now(3)), (4, '004', 0, now(3)) +ON DUPLICATE KEY UPDATE +`name` = VALUES(`name`), +`version` = `version` + 1", sql); + Assert.Equal(8, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/Oracle/Curd/OracleInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/Oracle/Curd/OracleInsertOrUpdateTest.cs new file mode 100644 index 00000000..e645b7fc --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/Oracle/Curd/OracleInsertOrUpdateTest.cs @@ -0,0 +1,323 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Oracle +{ + public class OracleInsertOrUpdateTest + { + IFreeSql fsql => g.oracle; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 2 as ID FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID FROM dual +UNION ALL + SELECT 2 FROM dual +UNION ALL + SELECT 3 FROM dual +UNION ALL + SELECT 4 FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '011' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 2 as ID, '02' as NAME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02' FROM dual +UNION ALL + SELECT 3, '03' FROM dual +UNION ALL + SELECT 4, '04' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '001' as NAME FROM dual +UNION ALL + SELECT 2, '002' FROM dual +UNION ALL + SELECT 3, '003' FROM dual +UNION ALL + SELECT 4, '004' FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '02' as ID2, '011' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 2 as ID1, '02' as ID2, '02' as NAME FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME FROM dual +UNION ALL + SELECT 2, '02', '02' FROM dual +UNION ALL + SELECT 3, '03', '03' FROM dual +UNION ALL + SELECT 4, '04', '04' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '001' as NAME FROM dual +UNION ALL + SELECT 2, '02', '002' FROM dual +UNION ALL + SELECT 3, '03', '003' FROM dual +UNION ALL + SELECT 4, '04', '004' FROM dual ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '011' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 2 as ID, '02' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '02', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '03', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '04', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '001' as NAME, 0 as VERSION, systimestamp as CREATETIME FROM dual +UNION ALL + SELECT 2, '002', 0, systimestamp FROM dual +UNION ALL + SELECT 3, '003', 0, systimestamp FROM dual +UNION ALL + SELECT 4, '004', 0, systimestamp FROM dual ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs new file mode 100644 index 00000000..1b1e853b --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertOrUpdateTest.cs @@ -0,0 +1,205 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.PostgreSQL +{ + public class PostgreSQLInsertOrUpdateTest + { + IFreeSql fsql => g.pgsql; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(2) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1), (2), (3), (4) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou01""(""id"") VALUES(1), (2), (3), (4) +ON CONFLICT(""id"") DO NOTHING", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '01') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '011') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(2, '02') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '01'), (2, '02'), (3, '03'), (4, '04') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou02""(""id"", ""name"") VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004') +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '01') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '02', '011') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(2, '02', '02') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '01'), (2, '02', '02'), (3, '03', '03'), (4, '04', '04') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '001'), (2, '02', '002'), (3, '03', '003'), (4, '04', '004') +ON CONFLICT(""id1"", ""id2"") DO UPDATE SET +""name"" = EXCLUDED.""name""", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '01', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '011', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(2, '02', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '01', 0, current_timestamp), (2, '02', 0, current_timestamp), (3, '03', 0, current_timestamp), (4, '04', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"INSERT INTO ""tbiou04""(""id"", ""name"", ""version"", ""createtime"") VALUES(1, '001', 0, current_timestamp), (2, '002', 0, current_timestamp), (3, '003', 0, current_timestamp), (4, '004', 0, current_timestamp) +ON CONFLICT(""id"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""version"" = ""tbiou04"".""version"" + 1", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs new file mode 100644 index 00000000..7858a27d --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertOrUpdateTest.cs @@ -0,0 +1,325 @@ +using FreeSql.DataAnnotations; +using FreeSql.Tests.DataContext.SqlServer; +using SaleIDO.Entity.Storeage; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.SqlServer +{ + public class SqlServerInsertOrUpdateTest + { + IFreeSql fsql => g.sqlserver; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 2 as id ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id +UNION ALL + SELECT 2 +UNION ALL + SELECT 3 +UNION ALL + SELECT 4 ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou01] t1 +USING (SELECT 1 as id +UNION ALL + SELECT 2 +UNION ALL + SELECT 3 +UNION ALL + SELECT 4 ) t2 ON (t1.[id] = t2.id) +WHEN NOT MATCHED THEN + insert ([id]) + values (t2.id);", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'01' as name ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'011' as name ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 2 as id, N'02' as name ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'01' as name +UNION ALL + SELECT 2, N'02' +UNION ALL + SELECT 3, N'03' +UNION ALL + SELECT 4, N'04' ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou02] t1 +USING (SELECT 1 as id, N'001' as name +UNION ALL + SELECT 2, N'002' +UNION ALL + SELECT 3, N'003' +UNION ALL + SELECT 4, N'004' ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id], [name]) + values (t2.id, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'01' as id2, N'01' as name ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'02' as id2, N'011' as name ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 2 as id1, N'02' as id2, N'02' as name ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'01' as id2, N'01' as name +UNION ALL + SELECT 2, N'02', N'02' +UNION ALL + SELECT 3, N'03', N'03' +UNION ALL + SELECT 4, N'04', N'04' ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou03] t1 +USING (SELECT 1 as id1, N'01' as id2, N'001' as name +UNION ALL + SELECT 2, N'02', N'002' +UNION ALL + SELECT 3, N'03', N'003' +UNION ALL + SELECT 4, N'04', N'004' ) t2 ON (t1.[id1] = t2.id1 AND t1.[id2] = t2.id2) +WHEN MATCHED THEN + update set [name] = t2.name +WHEN NOT MATCHED THEN + insert ([id1], [id2], [name]) + values (t2.id1, t2.id2, t2.name);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'01' as name, 0 as version, getdate() as CreateTime ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'011' as name, 0 as version, getdate() as CreateTime ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 2 as id, N'02' as name, 0 as version, getdate() as CreateTime ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'01' as name, 0 as version, getdate() as CreateTime +UNION ALL + SELECT 2, N'02', 0, getdate() +UNION ALL + SELECT 3, N'03', 0, getdate() +UNION ALL + SELECT 4, N'04', 0, getdate() ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO [tbiou04] t1 +USING (SELECT 1 as id, N'001' as name, 0 as version, getdate() as CreateTime +UNION ALL + SELECT 2, N'002', 0, getdate() +UNION ALL + SELECT 3, N'003', 0, getdate() +UNION ALL + SELECT 4, N'004', 0, getdate() ) t2 ON (t1.[id] = t2.id) +WHEN MATCHED THEN + update set [name] = t2.name, [version] = t1.[version] + 1 +WHEN NOT MATCHED THEN + insert ([id], [name], [version], [CreateTime]) + values (t2.id, t2.name, t2.version, t2.CreateTime);", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteInsertOrUpdateTest.cs new file mode 100644 index 00000000..0412c2fa --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteInsertOrUpdateTest.cs @@ -0,0 +1,165 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Sqlite +{ + public class SqliteInsertOrUpdateTest + { + IFreeSql fsql => g.sqlite; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou01""(""id"") VALUES(1)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou01""(""id"") VALUES(1)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou01""(""id"") VALUES(2)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou01""(""id"") VALUES(1), (2), (3), (4)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou01""(""id"") VALUES(1), (2), (3), (4)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou02""(""id"", ""name"") VALUES(1, '01')", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou02""(""id"", ""name"") VALUES(1, '011')", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou02""(""id"", ""name"") VALUES(2, '02')", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou02""(""id"", ""name"") VALUES(1, '01'), (2, '02'), (3, '03'), (4, '04')", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou02""(""id"", ""name"") VALUES(1, '001'), (2, '002'), (3, '003'), (4, '004')", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '01')", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '02', '011')", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(2, '02', '02')", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '01'), (2, '02', '02'), (3, '03', '03'), (4, '04', '04')", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou03""(""id1"", ""id2"", ""name"") VALUES(1, '01', '001'), (2, '02', '002'), (3, '03', '003'), (4, '04', '004')", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou04""(""id"", ""name"", ""version"", ""CreateTime"") VALUES(1, '01', 0, datetime(current_timestamp,'localtime'))", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou04""(""id"", ""name"", ""version"", ""CreateTime"") VALUES(1, '011', 0, datetime(current_timestamp,'localtime'))", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou04""(""id"", ""name"", ""version"", ""CreateTime"") VALUES(2, '02', 0, datetime(current_timestamp,'localtime'))", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou04""(""id"", ""name"", ""version"", ""CreateTime"") VALUES(1, '01', 0, datetime(current_timestamp,'localtime')), (2, '02', 0, datetime(current_timestamp,'localtime')), (3, '03', 0, datetime(current_timestamp,'localtime')), (4, '04', 0, datetime(current_timestamp,'localtime'))", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"REPLACE INTO ""tbiou04""(""id"", ""name"", ""version"", ""CreateTime"") VALUES(1, '001', 0, datetime(current_timestamp,'localtime')), (2, '002', 0, datetime(current_timestamp,'localtime')), (3, '003', 0, datetime(current_timestamp,'localtime')), (4, '004', 0, datetime(current_timestamp,'localtime'))", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 32fe7308..1b705557 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1011,6 +1011,60 @@ + + + 指定事务对象 + + + + + + + 指定事务对象 + + + + + + + 添加或更新,设置实体 + + 实体 + + + + + 添加或更新,设置实体集合 + + 实体集合 + + + + + 设置表名规则,可用于分库/分表,参数1:默认表名;返回值:新表名; + + + + + + + 动态Type,在使用 Update<object> 后使用本方法,指定实体类型 + + + + + + + 返回即将执行的SQL语句 + + + + + + 执行SQL语句,返回影响的行数 + + + 自动产生 as1, as2, as3 .... 字段别名 @@ -2337,137 +2391,6 @@ - - - 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 - - - - - - - - - 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > ?age", new { age = 25 }) - - - - - - - 查询 - - - - - - - 查询,ExecuteArrayAsync("select * from user where age > ?age", new { age = 25 }) - - - - - - - - 查询 - - - - - - - 查询,ExecuteDataSetAsync("select * from user where age > ?age; select 2", new { age = 25 }) - - - - - - - - 查询 - - - - - - - 查询,ExecuteDataTableAsync("select * from user where age > ?age", new { age = 25 }) - - - - - - - - 在【主库】执行 - - - - - - - - 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > ?age", new { age = 25 }) - - - - - - - - 在【主库】执行 - - - - - - - - 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > ?age", new { age = 25 }) - - - - - - - - 执行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 }) - - - - - - - - - 执行SQL返回对象集合,Query<User>("select * from user where age > ?age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - 执行SQL返回对象集合,Query<User>("select * from user where age > ?age; select * from address", new { age = 25 }) - - - - - - 可自定义解析表达式 @@ -3063,12 +2986,6 @@ 超时 - - - 获取资源 - - - 使用完毕后,归还资源 @@ -3139,12 +3056,6 @@ 资源对象 - - - 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 - - 资源对象 - 归还对象给对象池的时候触发 @@ -3685,6 +3596,19 @@ + + + 插入或更新数据 + MySql: on duplicate key update + PostgreSQL: on conflict do update + SqlServer: merge into + Oracle: merge into + Sqlite: replace into + Dameng: merge into + + + + 修改数据 diff --git a/FreeSql/Interface/Curd/IInsertOrUpdate.cs b/FreeSql/Interface/Curd/IInsertOrUpdate.cs new file mode 100644 index 00000000..c6db9222 --- /dev/null +++ b/FreeSql/Interface/Curd/IInsertOrUpdate.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace FreeSql +{ + public interface IInsertOrUpdate where T1 : class + { + + /// + /// 指定事务对象 + /// + /// + /// + IInsertOrUpdate WithTransaction(DbTransaction transaction); + /// + /// 指定事务对象 + /// + /// + /// + IInsertOrUpdate WithConnection(DbConnection connection); + + /// + /// 添加或更新,设置实体 + /// + /// 实体 + /// + IInsertOrUpdate SetSource(T1 source); + /// + /// 添加或更新,设置实体集合 + /// + /// 实体集合 + /// + IInsertOrUpdate SetSource(IEnumerable source); + + /// + /// 设置表名规则,可用于分库/分表,参数1:默认表名;返回值:新表名; + /// + /// + /// + IInsertOrUpdate AsTable(Func tableRule); + /// + /// 动态Type,在使用 Update<object> 后使用本方法,指定实体类型 + /// + /// + /// + IInsertOrUpdate AsType(Type entityType); + /// + /// 返回即将执行的SQL语句 + /// + /// + string ToSql(); + /// + /// 执行SQL语句,返回影响的行数 + /// + /// + int ExecuteAffrows(); + +#if net40 +#else + Task ExecuteAffrowsAsync(); +#endif + } +} \ No newline at end of file diff --git a/FreeSql/Interface/IAop.cs b/FreeSql/Interface/IAop.cs index b9914ff3..bef5f515 100644 --- a/FreeSql/Interface/IAop.cs +++ b/FreeSql/Interface/IAop.cs @@ -201,7 +201,7 @@ namespace FreeSql.Aop /// public DbParameter[] DbParms { get; } } - public enum CurdType { Select, Delete, Update, Insert } + public enum CurdType { Select, Delete, Update, Insert, InsertOrUpdate } public class CurdAfterEventArgs : CurdBeforeEventArgs { public CurdAfterEventArgs(CurdBeforeEventArgs before, Exception exception, object executeResult) : @@ -324,7 +324,7 @@ namespace FreeSql.Aop private object _value; public bool IsChanged { get; private set; } } - public enum AuditValueType { Update, Insert } + public enum AuditValueType { Update, Insert, InsertOrUpdate } #endregion #region CommandBefore/After diff --git a/FreeSql/Interface/IFreeSql.cs b/FreeSql/Interface/IFreeSql.cs index 2709de31..72230929 100644 --- a/FreeSql/Interface/IFreeSql.cs +++ b/FreeSql/Interface/IFreeSql.cs @@ -44,6 +44,19 @@ public interface IFreeSql : IDisposable /// IInsert Insert(IEnumerable source) where T1 : class; + /// + /// 插入或更新数据 + /// MySql: on duplicate key update + /// PostgreSQL: on conflict do update + /// SqlServer: merge into + /// Oracle: merge into + /// Sqlite: replace into + /// Dameng: merge into + /// + /// + /// + IInsertOrUpdate InsertOrUpdate() where T1 : class; + /// /// 修改数据 /// diff --git a/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs b/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs new file mode 100644 index 00000000..42fe4bee --- /dev/null +++ b/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs @@ -0,0 +1,222 @@ +using FreeSql.Extensions.EntityUtil; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.Internal.CommonProvider +{ + + public abstract partial class InsertOrUpdateProvider : IInsertOrUpdate where T1 : class + { + protected IFreeSql _orm; + protected CommonUtils _commonUtils; + protected CommonExpression _commonExpression; + protected List _source = new List(); + protected Dictionary _auditValueChangedDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + protected TableInfo _table; + protected Func _tableRule; + protected DbParameter[] _params; + protected DbTransaction _transaction; + protected DbConnection _connection; + + public InsertOrUpdateProvider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + { + _orm = orm; + _commonUtils = commonUtils; + _commonExpression = commonExpression; + _table = _commonUtils.GetTableByEntity(typeof(T1)); + if (_orm.CodeFirst.IsAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); + } + + protected void ClearData() + { + _source.Clear(); + _auditValueChangedDict.Clear(); + } + + public IInsertOrUpdate WithTransaction(DbTransaction transaction) + { + _transaction = transaction; + _connection = _transaction?.Connection; + return this; + } + public IInsertOrUpdate WithConnection(DbConnection connection) + { + if (_transaction?.Connection != connection) _transaction = null; + _connection = connection; + return this; + } + + public static void AuditDataValue(object sender, IEnumerable data, IFreeSql orm, TableInfo table, Dictionary changedDict) + { + if (data?.Any() != true) return; + if (orm.Aop.AuditValueHandler == null) return; + foreach (var d in data) + { + if (d == null) continue; + foreach (var col in table.Columns.Values) + { + object val = col.GetMapValue(d); + var auditArgs = new Aop.AuditValueEventArgs(Aop.AuditValueType.InsertOrUpdate, col, table.Properties[col.CsName], val); + orm.Aop.AuditValueHandler(sender, auditArgs); + if (auditArgs.IsChanged) + { + col.SetMapValue(d, val = auditArgs.Value); + if (changedDict != null && changedDict.ContainsKey(col.Attribute.Name) == false) + changedDict.Add(col.Attribute.Name, true); + } + } + } + } + public static void AuditDataValue(object sender, T1 data, IFreeSql orm, TableInfo table, Dictionary changedDict) + { + if (orm.Aop.AuditValueHandler == null) return; + if (data == null) return; + if (typeof(T1) == typeof(object) && new[] { table.Type, table.TypeLazy }.Contains(data.GetType()) == false) + throw new Exception($"操作的数据类型({data.GetType().DisplayCsharp()}) 与 AsType({table.Type.DisplayCsharp()}) 不一致,请检查。"); + foreach (var col in table.Columns.Values) + { + object val = col.GetMapValue(data); + var auditArgs = new Aop.AuditValueEventArgs(Aop.AuditValueType.InsertOrUpdate, col, table.Properties[col.CsName], val); + orm.Aop.AuditValueHandler(sender, auditArgs); + if (auditArgs.IsChanged) + { + col.SetMapValue(data, val = auditArgs.Value); + if (changedDict != null && changedDict.ContainsKey(col.Attribute.Name) == false) + changedDict.Add(col.Attribute.Name, true); + } + } + } + + public IInsertOrUpdate SetSource(T1 source) => this.SetSource(new[] { source }); + public IInsertOrUpdate SetSource(IEnumerable source) + { + if (source == null || source.Any() == false) return this; + AuditDataValue(this, source, _orm, _table, _auditValueChangedDict); + _source.AddRange(source.Where(a => a != null)); + return this; + } + + protected string TableRuleInvoke() + { + if (_tableRule == null) return _table.DbName; + var newname = _tableRule(_table.DbName); + if (newname == _table.DbName) return _table.DbName; + if (string.IsNullOrEmpty(newname)) return _table.DbName; + if (_orm.CodeFirst.IsSyncStructureToLower) newname = newname.ToLower(); + if (_orm.CodeFirst.IsSyncStructureToUpper) newname = newname.ToUpper(); + if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table.Type, newname); + return newname; + } + public IInsertOrUpdate AsTable(Func tableRule) + { + _tableRule = tableRule; + return this; + } + public IInsertOrUpdate AsType(Type entityType) + { + if (entityType == typeof(object)) throw new Exception("IInsertOrUpdate.AsType 参数不支持指定为 object"); + if (entityType == _table.Type) return this; + var newtb = _commonUtils.GetTableByEntity(entityType); + _table = newtb ?? throw new Exception("IInsertOrUpdate.AsType 参数错误,请传入正确的实体类型"); + if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); + return this; + } + + public void WriteSourceSelectUnionAll(StringBuilder sb) + { + var specialParams = new List(); + var didx = 0; + foreach (var d in _source) + { + if (didx > 0) sb.Append(" \r\nUNION ALL\r\n "); + sb.Append("SELECT "); + var colidx2 = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx2 > 0) sb.Append(", "); + if (string.IsNullOrEmpty(col.DbInsertValue) == false) + sb.Append(col.DbInsertValue); + else + { + object val = col.GetMapValue(d); + sb.Append(_commonUtils.GetNoneParamaterSqlValue(specialParams, col.Attribute.MapType, val)); + } + if (didx == 0) sb.Append(" as ").Append(col.Attribute.Name); + ++colidx2; + } + switch (_orm.Ado.DataType) + { + case DataType.OdbcOracle: + case DataType.Oracle: + case DataType.OdbcDameng: + case DataType.Dameng: + sb.Append(" FROM dual"); + break; + } + ++didx; + } + if (specialParams.Any()) _params = specialParams.ToArray(); + } + + public abstract string ToSql(); + public int ExecuteAffrows() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.InsertOrUpdate, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var affrows = 0; + Exception exception = null; + try + { + affrows = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, affrows); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return affrows; + } +#if net40 +#else + async public Task ExecuteAffrowsAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.InsertOrUpdate, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var affrows = 0; + Exception exception = null; + try + { + affrows = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, affrows); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return affrows; + } +#endif + } +} diff --git a/FreeSql/Internal/CommonProvider/InsertProvider.cs b/FreeSql/Internal/CommonProvider/InsertProvider.cs index a88dbd19..6883a596 100644 --- a/FreeSql/Internal/CommonProvider/InsertProvider.cs +++ b/FreeSql/Internal/CommonProvider/InsertProvider.cs @@ -15,20 +15,20 @@ namespace FreeSql.Internal.CommonProvider public abstract partial class InsertProvider : IInsert where T1 : class { - protected IFreeSql _orm; - protected CommonUtils _commonUtils; - protected CommonExpression _commonExpression; - protected List _source = new List(); - protected Dictionary _ignore = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - protected Dictionary _auditValueChangedDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - protected TableInfo _table; - protected Func _tableRule; - protected bool _noneParameter, _insertIdentity; - protected int _batchValuesLimit, _batchParameterLimit; - protected bool _batchAutoTransaction = true; - protected DbParameter[] _params; - protected DbTransaction _transaction; - protected DbConnection _connection; + public IFreeSql _orm; + public CommonUtils _commonUtils; + public CommonExpression _commonExpression; + public List _source = new List(); + public Dictionary _ignore = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + public Dictionary _auditValueChangedDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + public TableInfo _table; + public Func _tableRule; + public bool _noneParameter, _insertIdentity; + public int _batchValuesLimit, _batchParameterLimit; + public bool _batchAutoTransaction = true; + public DbParameter[] _params; + public DbTransaction _transaction; + public DbConnection _connection; public InsertProvider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) { @@ -509,7 +509,7 @@ namespace FreeSql.Internal.CommonProvider ++colidx; } sb.Append(") "); - if (isValues) sb.Append(isValues ? "VALUES" : "SELECT "); + if (isValues) sb.Append("VALUES"); _params = _noneParameter ? new DbParameter[0] : new DbParameter[colidx * _source.Count]; var specialParams = new List(); var didx = 0; diff --git a/FreeSql/Internal/CommonProvider/UpdateProvider.cs b/FreeSql/Internal/CommonProvider/UpdateProvider.cs index e6307337..df335d50 100644 --- a/FreeSql/Internal/CommonProvider/UpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/UpdateProvider.cs @@ -15,25 +15,25 @@ namespace FreeSql.Internal.CommonProvider public abstract partial class UpdateProvider : IUpdate where T1 : class { - protected IFreeSql _orm; - protected CommonUtils _commonUtils; - protected CommonExpression _commonExpression; - protected List _source = new List(); - protected Dictionary _ignore = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - protected Dictionary _auditValueChangedDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - protected TableInfo _table; - protected Func _tableRule; - protected StringBuilder _where = new StringBuilder(); - protected List _whereGlobalFilter; - protected StringBuilder _set = new StringBuilder(); - protected StringBuilder _setIncr = new StringBuilder(); - protected List _params = new List(); - protected List _paramsSource = new List(); - protected bool _noneParameter; - protected int _batchRowsLimit, _batchParameterLimit; - protected bool _batchAutoTransaction = true; - protected DbTransaction _transaction; - protected DbConnection _connection; + public IFreeSql _orm; + public CommonUtils _commonUtils; + public CommonExpression _commonExpression; + public List _source = new List(); + public Dictionary _ignore = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + public Dictionary _auditValueChangedDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + public TableInfo _table; + public Func _tableRule; + public StringBuilder _where = new StringBuilder(); + public List _whereGlobalFilter; + public StringBuilder _set = new StringBuilder(); + public StringBuilder _setIncr = new StringBuilder(); + public List _params = new List(); + public List _paramsSource = new List(); + public bool _noneParameter; + public int _batchRowsLimit, _batchParameterLimit; + public bool _batchAutoTransaction = true; + public DbTransaction _transaction; + public DbConnection _connection; public UpdateProvider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) { diff --git a/Providers/FreeSql.Provider.Dameng/Curd/DamengInsertOrUpdate.cs b/Providers/FreeSql.Provider.Dameng/Curd/DamengInsertOrUpdate.cs new file mode 100644 index 00000000..52f2e955 --- /dev/null +++ b/Providers/FreeSql.Provider.Dameng/Curd/DamengInsertOrUpdate.cs @@ -0,0 +1,43 @@ +using FreeSql.Internal; +using System; +using System.Linq; +using System.Text; + +namespace FreeSql.Dameng.Curd +{ + + class DamengInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public DamengInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\n") + .Append("USING ("); + WriteSourceSelectUnionAll(sb); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values; + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(")"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Dameng/DamengProvider.cs b/Providers/FreeSql.Provider.Dameng/DamengProvider.cs index e8166066..c6903675 100644 --- a/Providers/FreeSql.Provider.Dameng/DamengProvider.cs +++ b/Providers/FreeSql.Provider.Dameng/DamengProvider.cs @@ -24,6 +24,7 @@ namespace FreeSql.Dameng public IUpdate Update(object dywhere) where T1 : class => new DamengUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new DamengDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new DamengDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new DamengInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.MsAccess/MsAccessProvider.cs b/Providers/FreeSql.Provider.MsAccess/MsAccessProvider.cs index 8c75f086..1160eec7 100644 --- a/Providers/FreeSql.Provider.MsAccess/MsAccessProvider.cs +++ b/Providers/FreeSql.Provider.MsAccess/MsAccessProvider.cs @@ -24,6 +24,7 @@ namespace FreeSql.MsAccess public IUpdate Update(object dywhere) where T1 : class => new MsAccessUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new MsAccessDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new MsAccessDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => throw new NotImplementedException(); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.MySql/Curd/MySqlInsertOrUpdate.cs b/Providers/FreeSql.Provider.MySql/Curd/MySqlInsertOrUpdate.cs new file mode 100644 index 00000000..6d530b2f --- /dev/null +++ b/Providers/FreeSql.Provider.MySql/Curd/MySqlInsertOrUpdate.cs @@ -0,0 +1,29 @@ +using FreeSql.Internal; +using System.Linq; + +namespace FreeSql.MySql.Curd +{ + + class MySqlInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public MySqlInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = _source; + var sql = new OnDuplicateKeyUpdate(insert).ToSql(); + _params = insert._params; + return sql; + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.MySql/Curd/OnDuplicateKeyUpdate.cs b/Providers/FreeSql.Provider.MySql/Curd/OnDuplicateKeyUpdate.cs index 883630c8..66118ee1 100644 --- a/Providers/FreeSql.Provider.MySql/Curd/OnDuplicateKeyUpdate.cs +++ b/Providers/FreeSql.Provider.MySql/Curd/OnDuplicateKeyUpdate.cs @@ -1,11 +1,6 @@ using FreeSql.Aop; -using FreeSql.Internal; -using FreeSql.Internal.Model; using System; -using System.Collections.Generic; using System.Data; -using System.Data.Common; -using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; diff --git a/Providers/FreeSql.Provider.MySql/MySqlProvider.cs b/Providers/FreeSql.Provider.MySql/MySqlProvider.cs index f1b146d0..c1d533fc 100644 --- a/Providers/FreeSql.Provider.MySql/MySqlProvider.cs +++ b/Providers/FreeSql.Provider.MySql/MySqlProvider.cs @@ -50,6 +50,7 @@ namespace FreeSql.MySql public IUpdate Update(object dywhere) where T1 : class => new MySqlUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new MySqlDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new MySqlDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new MySqlInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Odbc/Dameng/Curd/OdbcDamengInsertOrUpdate.cs b/Providers/FreeSql.Provider.Odbc/Dameng/Curd/OdbcDamengInsertOrUpdate.cs new file mode 100644 index 00000000..c5826cdc --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/Dameng/Curd/OdbcDamengInsertOrUpdate.cs @@ -0,0 +1,43 @@ +using FreeSql.Internal; +using System; +using System.Linq; +using System.Text; + +namespace FreeSql.Odbc.Dameng +{ + + class OdbcDamengInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public OdbcDamengInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\n") + .Append("USING ("); + WriteSourceSelectUnionAll(sb); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values; + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(")"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengProvider.cs b/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengProvider.cs index 77e114ae..3adf7d01 100644 --- a/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengProvider.cs +++ b/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengProvider.cs @@ -23,6 +23,7 @@ namespace FreeSql.Odbc.Dameng public IUpdate Update(object dywhere) where T1 : class => new OdbcDamengUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OdbcDamengDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OdbcDamengDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new OdbcDamengInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Odbc/Default/OdbcProvider.cs b/Providers/FreeSql.Provider.Odbc/Default/OdbcProvider.cs index f53e1791..6459f13f 100644 --- a/Providers/FreeSql.Provider.Odbc/Default/OdbcProvider.cs +++ b/Providers/FreeSql.Provider.Odbc/Default/OdbcProvider.cs @@ -26,6 +26,7 @@ namespace FreeSql.Odbc.Default public IUpdate Update(object dywhere) where T1 : class => new OdbcUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OdbcDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OdbcDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => throw new NotImplementedException(); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsert.cs b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsert.cs index a99cfaf4..94f6a20b 100644 --- a/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsert.cs +++ b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsert.cs @@ -1,4 +1,5 @@ using FreeSql.Internal; +using FreeSql.Internal.Model; using FreeSql.Internal.ObjectPool; using System; using System.Collections.Generic; @@ -17,11 +18,30 @@ namespace FreeSql.Odbc.MySql { } + 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 InternalSource => _source; + internal Dictionary InternalIgnore => _ignore; + internal void InternalClearData() => ClearData(); + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); public override List ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + public override string ToSql() + { + if (InternalIsIgnoreInto == false) return base.ToSqlValuesOrSelectUnionAll(); + var sql = base.ToSqlValuesOrSelectUnionAll(); + return $"INSERT IGNORE INTO {sql.Substring(12)}"; + } + protected override long RawExecuteIdentity() { var sql = this.ToSql(); diff --git a/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsertOrUpdate.cs b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsertOrUpdate.cs new file mode 100644 index 00000000..c8908805 --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlInsertOrUpdate.cs @@ -0,0 +1,29 @@ +using FreeSql.Internal; +using System.Linq; + +namespace FreeSql.Odbc.MySql +{ + + class OdbcMySqlInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public OdbcMySqlInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = _source; + var sql = new OdbcMySqlOnDuplicateKeyUpdate(insert).ToSql(); + _params = insert._params; + return sql; + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlOnDuplicateKeyUpdate.cs b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlOnDuplicateKeyUpdate.cs new file mode 100644 index 00000000..cc4649fb --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlOnDuplicateKeyUpdate.cs @@ -0,0 +1,166 @@ +using FreeSql.Aop; +using System; +using System.Data; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.Odbc.MySql +{ + public class OdbcMySqlOnDuplicateKeyUpdate where T1 : class + { + internal OdbcMySqlInsert _mysqlInsert; + internal OdbcMySqlUpdate _mysqlUpdatePriv; + internal OdbcMySqlUpdate _mysqlUpdate => _mysqlUpdatePriv ?? (_mysqlUpdatePriv = new OdbcMySqlUpdate(_mysqlInsert.InternalOrm, _mysqlInsert.InternalCommonUtils, _mysqlInsert.InternalCommonExpression, null).NoneParameter().SetSource(_mysqlInsert.InternalSource) as OdbcMySqlUpdate); + + public OdbcMySqlOnDuplicateKeyUpdate(IInsert insert) + { + _mysqlInsert = insert as OdbcMySqlInsert; + if (_mysqlInsert == null) throw new Exception("OnDuplicateKeyUpdate 是 FreeSql.Provider.Odbc/MySql 特有的功能"); + } + + protected void ClearData() + { + _mysqlInsert.InternalClearData(); + _mysqlUpdatePriv = null; + } + + public OdbcMySqlOnDuplicateKeyUpdate IgnoreColumns(Expression> columns) + { + _mysqlUpdate.IgnoreColumns(columns); + return this; + } + public OdbcMySqlOnDuplicateKeyUpdate UpdateColumns(Expression> columns) + { + _mysqlUpdate.UpdateColumns(columns); + return this; + } + public OdbcMySqlOnDuplicateKeyUpdate IgnoreColumns(string[] columns) + { + _mysqlUpdate.IgnoreColumns(columns); + return this; + } + public OdbcMySqlOnDuplicateKeyUpdate UpdateColumns(string[] columns) + { + _mysqlUpdate.UpdateColumns(columns); + return this; + } + + public OdbcMySqlOnDuplicateKeyUpdate Set(Expression> column, TMember value) + { + _mysqlUpdate.Set(column, value); + return this; + } + public OdbcMySqlOnDuplicateKeyUpdate Set(Expression> exp) + { + _mysqlUpdate.Set(exp); + return this; + } + public OdbcMySqlOnDuplicateKeyUpdate SetRaw(string sql) + { + _mysqlUpdate.SetRaw(sql); + return this; + } + + public string ToSql() + { + var sb = new StringBuilder(); + sb.Append(_mysqlInsert.ToSql()).Append("\r\nON DUPLICATE KEY UPDATE\r\n"); + + var sbSetEmpty = _mysqlUpdate.InternalSbSet.Length == 0; + var sbSetIncrEmpty = _mysqlUpdate.InternalSbSetIncr.Length == 0; + if (sbSetEmpty == false || sbSetIncrEmpty == false) + { + if (sbSetEmpty == false) sb.Append(_mysqlUpdate.InternalSbSet.ToString().Substring(2)); + if (sbSetIncrEmpty == false) sb.Append(sbSetEmpty ? _mysqlUpdate.InternalSbSetIncr.ToString().Substring(2) : _mysqlUpdate.InternalSbSetIncr.ToString()); + } + else + { + var colidx = 0; + foreach (var col in _mysqlInsert.InternalTable.Columns.Values) + { + if (col.Attribute.IsPrimary || _mysqlUpdate.InternalIgnore.ContainsKey(col.Attribute.Name)) continue; + + if (colidx > 0) sb.Append(", \r\n"); + + if (col.Attribute.IsVersion == true) + { + var field = _mysqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name); + sb.Append(field).Append(" = ").Append(field).Append(" + 1"); + } + else if (_mysqlInsert.InternalIgnore.ContainsKey(col.Attribute.Name)) + { + var caseWhen = _mysqlUpdate.InternalWhereCaseSource(col.CsName, sqlval => sqlval).Trim(); + sb.Append(caseWhen); + if (caseWhen.EndsWith(" END")) _mysqlUpdate.InternalToSqlCaseWhenEnd(sb, col); + } + else + { + var field = _mysqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name); + sb.Append(field).Append(" = VALUES(").Append(field).Append(")"); + } + ++colidx; + } + } + + return sb.ToString(); + } + + public long ExecuteAffrows() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new CurdBeforeEventArgs(_mysqlInsert.InternalTable.Type, _mysqlInsert.InternalTable, CurdType.Insert, sql, _mysqlInsert.InternalParams); + _mysqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_mysqlInsert, before); + long ret = 0; + Exception exception = null; + try + { + ret = _mysqlInsert.InternalOrm.Ado.ExecuteNonQuery(_mysqlInsert.InternalConnection, _mysqlInsert.InternalTransaction, CommandType.Text, sql, _mysqlInsert.InternalParams); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new CurdAfterEventArgs(before, exception, ret); + _mysqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_mysqlInsert, after); + ClearData(); + } + return ret; + } + +#if net40 +#else + async public Task ExecuteAffrowsAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new CurdBeforeEventArgs(_mysqlInsert.InternalTable.Type, _mysqlInsert.InternalTable, CurdType.Insert, sql, _mysqlInsert.InternalParams); + _mysqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_mysqlInsert, before); + long ret = 0; + Exception exception = null; + try + { + ret = await _mysqlInsert.InternalOrm.Ado.ExecuteNonQueryAsync(_mysqlInsert.InternalConnection, _mysqlInsert.InternalTransaction, CommandType.Text, sql, _mysqlInsert.InternalParams); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new CurdAfterEventArgs(before, exception, ret); + _mysqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_mysqlInsert, after); + ClearData(); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlUpdate.cs b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlUpdate.cs index 2b12d47c..d86a8c93 100644 --- a/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlUpdate.cs +++ b/Providers/FreeSql.Provider.Odbc/MySql/Curd/OdbcMySqlUpdate.cs @@ -18,10 +18,16 @@ namespace FreeSql.Odbc.MySql { } + internal StringBuilder InternalSbSet => _set; + internal StringBuilder InternalSbSetIncr => _setIncr; + internal Dictionary InternalIgnore => _ignore; + internal void InternalResetSource(List source) => _source = source; + internal string InternalWhereCaseSource(string CsName, Func thenValue) => WhereCaseSource(CsName, thenValue); + internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col); + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); public override List ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); - protected override List RawExecuteUpdated() { var sql = this.ToSql(); diff --git a/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlProvider.cs b/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlProvider.cs index a0bb6347..e59064d9 100644 --- a/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlProvider.cs +++ b/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlProvider.cs @@ -28,6 +28,7 @@ namespace FreeSql.Odbc.MySql public IUpdate Update(object dywhere) where T1 : class => new OdbcMySqlUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OdbcMySqlDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OdbcMySqlDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new OdbcMySqlInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Odbc/Oracle/Curd/OdbcOracleInsertOrUpdate.cs b/Providers/FreeSql.Provider.Odbc/Oracle/Curd/OdbcOracleInsertOrUpdate.cs new file mode 100644 index 00000000..0c151cc3 --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/Oracle/Curd/OdbcOracleInsertOrUpdate.cs @@ -0,0 +1,43 @@ +using FreeSql.Internal; +using System; +using System.Linq; +using System.Text; + +namespace FreeSql.Odbc.Oracle +{ + + class OdbcOracleInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public OdbcOracleInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\n") + .Append("USING ("); + WriteSourceSelectUnionAll(sb); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values; + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(")"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleProvider.cs b/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleProvider.cs index 47b16b74..1a35a2d2 100644 --- a/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleProvider.cs +++ b/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleProvider.cs @@ -23,6 +23,7 @@ namespace FreeSql.Odbc.Oracle public IUpdate Update(object dywhere) where T1 : class => new OdbcOracleUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OdbcOracleDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OdbcOracleDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new OdbcOracleInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsert.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsert.cs index 4d457731..e8367ac5 100644 --- a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsert.cs +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsert.cs @@ -1,7 +1,9 @@ using FreeSql.Internal; +using FreeSql.Internal.Model; using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -16,6 +18,17 @@ namespace FreeSql.Odbc.PostgreSQL { } + 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 InternalSource => _source; + internal Dictionary InternalIgnore => _ignore; + internal void InternalClearData() => ClearData(); + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); public override List ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs new file mode 100644 index 00000000..ec291e6b --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLInsertOrUpdate.cs @@ -0,0 +1,33 @@ +using FreeSql.Internal; +using System.Linq; + +namespace FreeSql.Odbc.PostgreSQL +{ + + class OdbcPostgreSQLInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public OdbcPostgreSQLInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = _source; + var ocdu = new OdbcPostgreSQLOnConflictDoUpdate(insert); + ocdu.IgnoreColumns(_table.Columns.Values.Where(a => a.Attribute.CanUpdate == false).Select(a => a.Attribute.Name).ToArray()); + if (_table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true).Any() == false) + ocdu.DoNothing(); + var sql = ocdu.ToSql(); + _params = insert._params; + return sql; + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs new file mode 100644 index 00000000..4d59b8af --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs @@ -0,0 +1,207 @@ +using FreeSql.Aop; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.Odbc.PostgreSQL +{ + public class OdbcPostgreSQLOnConflictDoUpdate where T1 : class + { + internal OdbcPostgreSQLInsert _pgsqlInsert; + internal OdbcPostgreSQLUpdate _pgsqlUpdatePriv; + internal OdbcPostgreSQLUpdate _pgsqlUpdate => _pgsqlUpdatePriv ?? + (_pgsqlUpdatePriv = new OdbcPostgreSQLUpdate(_pgsqlInsert.InternalOrm, _pgsqlInsert.InternalCommonUtils, _pgsqlInsert.InternalCommonExpression, null) { InternalTableAlias = "EXCLUDED" } + .NoneParameter().SetSource(_pgsqlInsert.InternalSource) as OdbcPostgreSQLUpdate); + ColumnInfo[] _columns; + bool _doNothing; + + public OdbcPostgreSQLOnConflictDoUpdate(IInsert insert, Expression> columns = null) + { + _pgsqlInsert = insert as OdbcPostgreSQLInsert; + if (_pgsqlInsert == null) throw new Exception("OnConflictDoUpdate 是 FreeSql.Provider.Odbc/PostgreSQL 特有的功能"); + + if (columns != null) + { + var colsList = new List(); + var cols = _pgsqlInsert.InternalCommonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, columns?.Body, false, null).ToDictionary(a => a, a => true); + foreach (var col in _pgsqlInsert.InternalTable.Columns.Values) + if (cols.ContainsKey(col.Attribute.Name)) + colsList.Add(col); + _columns = colsList.ToArray(); + } + if (_columns == null || _columns.Any() == false) + _columns = _pgsqlInsert.InternalTable.Primarys; + if (_columns.Any() == false) throw new Exception("OnConflictDoUpdate 功能要求实体类必须设置 IsPrimary 属性"); + } + + protected void ClearData() + { + _pgsqlInsert.InternalClearData(); + _pgsqlUpdatePriv = null; + } + + public OdbcPostgreSQLOnConflictDoUpdate IgnoreColumns(Expression> columns) + { + _pgsqlUpdate.IgnoreColumns(columns); + return this; + } + public OdbcPostgreSQLOnConflictDoUpdate UpdateColumns(Expression> columns) + { + _pgsqlUpdate.UpdateColumns(columns); + return this; + } + public OdbcPostgreSQLOnConflictDoUpdate IgnoreColumns(string[] columns) + { + _pgsqlUpdate.IgnoreColumns(columns); + return this; + } + public OdbcPostgreSQLOnConflictDoUpdate UpdateColumns(string[] columns) + { + _pgsqlUpdate.UpdateColumns(columns); + return this; + } + + public OdbcPostgreSQLOnConflictDoUpdate Set(Expression> column, TMember value) + { + _pgsqlUpdate.Set(column, value); + return this; + } + //由于表达式解析问题,ON CONFLICT("id") DO UPDATE SET 需要指定表别名,如 Set(a => a.Clicks + 1) 解析会失败 + //暂时不开放这个功能,如有需要使用 SetRaw("click = t.click + 1") 替代该操作 + //public OnConflictDoUpdate Set(Expression> exp) + //{ + // _pgsqlUpdate.Set(exp); + // return this; + //} + public OdbcPostgreSQLOnConflictDoUpdate SetRaw(string sql) + { + _pgsqlUpdate.SetRaw(sql); + return this; + } + + public OdbcPostgreSQLOnConflictDoUpdate DoNothing() + { + _doNothing = true; + return this; + } + + public string ToSql() + { + var sb = new StringBuilder(); + sb.Append(_pgsqlInsert.ToSql()).Append("\r\nON CONFLICT("); + for (var a = 0; a < _columns.Length; a++) + { + if (a > 0) sb.Append(", "); + sb.Append(_pgsqlInsert.InternalCommonUtils.QuoteSqlName(_columns[a].Attribute.Name)); + } + if (_doNothing) + { + sb.Append(") DO NOTHING"); + } + else + { + sb.Append(") DO UPDATE SET\r\n"); + + var sbSetEmpty = _pgsqlUpdate.InternalSbSet.Length == 0; + var sbSetIncrEmpty = _pgsqlUpdate.InternalSbSetIncr.Length == 0; + if (sbSetEmpty == false || sbSetIncrEmpty == false) + { + if (sbSetEmpty == false) sb.Append(_pgsqlUpdate.InternalSbSet.ToString().Substring(2)); + if (sbSetIncrEmpty == false) sb.Append(sbSetEmpty ? _pgsqlUpdate.InternalSbSetIncr.ToString().Substring(2) : _pgsqlUpdate.InternalSbSetIncr.ToString()); + } + else + { + var colidx = 0; + foreach (var col in _pgsqlInsert.InternalTable.Columns.Values) + { + if (col.Attribute.IsPrimary || _pgsqlUpdate.InternalIgnore.ContainsKey(col.Attribute.Name)) continue; + + if (colidx > 0) sb.Append(", \r\n"); + + if (col.Attribute.IsVersion == true) + { + var field = _pgsqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name); + sb.Append(field).Append(" = ").Append(_pgsqlInsert.InternalCommonUtils.QuoteSqlName(_pgsqlInsert.InternalTable.DbName)).Append(".").Append(field).Append(" + 1"); + } + else if (_pgsqlInsert.InternalIgnore.ContainsKey(col.Attribute.Name)) + { + var caseWhen = _pgsqlUpdate.InternalWhereCaseSource(col.CsName, sqlval => sqlval).Trim(); + sb.Append(caseWhen); + if (caseWhen.EndsWith(" END")) _pgsqlUpdate.InternalToSqlCaseWhenEnd(sb, col); + } + else + { + var field = _pgsqlInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name); + sb.Append(field).Append(" = EXCLUDED.").Append(field); + } + ++colidx; + } + } + } + + return sb.ToString(); + } + + public long ExecuteAffrows() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new CurdBeforeEventArgs(_pgsqlInsert.InternalTable.Type, _pgsqlInsert.InternalTable, CurdType.Insert, sql, _pgsqlInsert.InternalParams); + _pgsqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_pgsqlInsert, before); + long ret = 0; + Exception exception = null; + try + { + ret = _pgsqlInsert.InternalOrm.Ado.ExecuteNonQuery(_pgsqlInsert.InternalConnection, _pgsqlInsert.InternalTransaction, CommandType.Text, sql, _pgsqlInsert.InternalParams); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new CurdAfterEventArgs(before, exception, ret); + _pgsqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_pgsqlInsert, after); + ClearData(); + } + return ret; + } + +#if net40 +#else + async public Task ExecuteAffrowsAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new CurdBeforeEventArgs(_pgsqlInsert.InternalTable.Type, _pgsqlInsert.InternalTable, CurdType.Insert, sql, _pgsqlInsert.InternalParams); + _pgsqlInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_pgsqlInsert, before); + long ret = 0; + Exception exception = null; + try + { + ret = await _pgsqlInsert.InternalOrm.Ado.ExecuteNonQueryAsync(_pgsqlInsert.InternalConnection, _pgsqlInsert.InternalTransaction, CommandType.Text, sql, _pgsqlInsert.InternalParams); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new CurdAfterEventArgs(before, exception, ret); + _pgsqlInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_pgsqlInsert, after); + ClearData(); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLUpdate.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLUpdate.cs index f6e79b76..d83fff64 100644 --- a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLUpdate.cs +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLUpdate.cs @@ -18,6 +18,14 @@ namespace FreeSql.Odbc.PostgreSQL { } + internal string InternalTableAlias; + internal StringBuilder InternalSbSet => _set; + internal StringBuilder InternalSbSetIncr => _setIncr; + internal Dictionary InternalIgnore => _ignore; + internal void InternalResetSource(List source) => _source = source; + internal string InternalWhereCaseSource(string CsName, Func thenValue) => WhereCaseSource(CsName, thenValue); + internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col); + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); public override List ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); @@ -65,6 +73,7 @@ namespace FreeSql.Odbc.PostgreSQL if (_table.Primarys.Length == 1) { var pk = _table.Primarys.First(); + if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append("."); caseWhen.Append(_commonUtils.QuoteReadColumn(pk.CsType, pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))); return; } @@ -73,6 +82,7 @@ namespace FreeSql.Odbc.PostgreSQL foreach (var pk in _table.Primarys) { if (pkidx > 0) caseWhen.Append(" || '+' || "); + if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append("."); caseWhen.Append(_commonUtils.QuoteReadColumn(pk.CsType, pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::varchar"); ++pkidx; } diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLProvider.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLProvider.cs index dee8daae..181803a4 100644 --- a/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLProvider.cs +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLProvider.cs @@ -27,6 +27,7 @@ namespace FreeSql.Odbc.PostgreSQL public IUpdate Update(object dywhere) where T1 : class => new OdbcPostgreSQLUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OdbcPostgreSQLDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OdbcPostgreSQLDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new OdbcPostgreSQLInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsert.cs b/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsert.cs index 4491d2ab..4fc3e5f0 100644 --- a/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsert.cs +++ b/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsert.cs @@ -1,9 +1,7 @@ using FreeSql.Internal; -using FreeSql.Internal.ObjectPool; using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Text; using System.Threading.Tasks; diff --git a/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsertOrUpdate.cs b/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsertOrUpdate.cs new file mode 100644 index 00000000..85f74a23 --- /dev/null +++ b/Providers/FreeSql.Provider.Odbc/SqlServer/Curd/OdbcSqlServerInsertOrUpdate.cs @@ -0,0 +1,43 @@ +using FreeSql.Internal; +using System; +using System.Linq; +using System.Text; + +namespace FreeSql.Odbc.SqlServer +{ + + class OdbcSqlServerInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public OdbcSqlServerInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\n") + .Append("USING ("); + WriteSourceSelectUnionAll(sb); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values; + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(");"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerProvider.cs b/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerProvider.cs index 5d3fd7bf..62ec8650 100644 --- a/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerProvider.cs +++ b/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerProvider.cs @@ -23,6 +23,7 @@ namespace FreeSql.Odbc.SqlServer public IUpdate Update(object dywhere) where T1 : class => new OdbcSqlServerUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OdbcSqlServerDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OdbcSqlServerDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new OdbcSqlServerInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Oracle/Curd/OracleInsertOrUpdate.cs b/Providers/FreeSql.Provider.Oracle/Curd/OracleInsertOrUpdate.cs new file mode 100644 index 00000000..981e33f9 --- /dev/null +++ b/Providers/FreeSql.Provider.Oracle/Curd/OracleInsertOrUpdate.cs @@ -0,0 +1,43 @@ +using FreeSql.Internal; +using System; +using System.Linq; +using System.Text; + +namespace FreeSql.Oracle.Curd +{ + + class OracleInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public OracleInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\n") + .Append("USING ("); + WriteSourceSelectUnionAll(sb); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values; + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(")"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Oracle/OracleProvider.cs b/Providers/FreeSql.Provider.Oracle/OracleProvider.cs index 9a7bebdc..9b5716cf 100644 --- a/Providers/FreeSql.Provider.Oracle/OracleProvider.cs +++ b/Providers/FreeSql.Provider.Oracle/OracleProvider.cs @@ -24,6 +24,7 @@ namespace FreeSql.Oracle public IUpdate Update(object dywhere) where T1 : class => new OracleUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new OracleDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new OracleDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new OracleInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs b/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs index c4d87aed..2fbdf279 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs +++ b/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs @@ -1,14 +1,11 @@ using FreeSql.Aop; -using FreeSql.Internal; using FreeSql.Internal.Model; using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Linq; using System.Linq.Expressions; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; namespace FreeSql.PostgreSQL.Curd diff --git a/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs b/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs new file mode 100644 index 00000000..87280563 --- /dev/null +++ b/Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsertOrUpdate.cs @@ -0,0 +1,33 @@ +using FreeSql.Internal; +using System.Linq; + +namespace FreeSql.PostgreSQL.Curd +{ + + class PostgreSQLInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public PostgreSQLInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = _source; + var ocdu = new OnConflictDoUpdate(insert); + ocdu.IgnoreColumns(_table.Columns.Values.Where(a => a.Attribute.CanUpdate == false).Select(a => a.Attribute.Name).ToArray()); + if (_table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true).Any() == false) + ocdu.DoNothing(); + var sql = ocdu.ToSql(); + _params = insert._params; + return sql; + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLProvider.cs b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLProvider.cs index 8471653f..bb1944bb 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLProvider.cs +++ b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLProvider.cs @@ -77,6 +77,7 @@ namespace FreeSql.PostgreSQL public IUpdate Update(object dywhere) where T1 : class => new PostgreSQLUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new PostgreSQLDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new PostgreSQLDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new PostgreSQLInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsert.cs b/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsert.cs index ee942b3b..0017f5c5 100644 --- a/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsert.cs +++ b/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsert.cs @@ -1,10 +1,8 @@ using FreeSql.Internal; -using FreeSql.Internal.ObjectPool; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Linq; using System.Text; using System.Threading.Tasks; diff --git a/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsertOrUpdate.cs b/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsertOrUpdate.cs new file mode 100644 index 00000000..5e4870d7 --- /dev/null +++ b/Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsertOrUpdate.cs @@ -0,0 +1,43 @@ +using FreeSql.Internal; +using System; +using System.Linq; +using System.Text; + +namespace FreeSql.SqlServer.Curd +{ + + class SqlServerInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public SqlServerInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\n") + .Append("USING ("); + WriteSourceSelectUnionAll(sb); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values; + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(");"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.SqlServer/SqlServerProvider.cs b/Providers/FreeSql.Provider.SqlServer/SqlServerProvider.cs index afaf697b..2eec51aa 100644 --- a/Providers/FreeSql.Provider.SqlServer/SqlServerProvider.cs +++ b/Providers/FreeSql.Provider.SqlServer/SqlServerProvider.cs @@ -24,6 +24,7 @@ namespace FreeSql.SqlServer public IUpdate Update(object dywhere) where T1 : class => new SqlServerUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new SqlServerDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new SqlServerDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new SqlServerInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; } diff --git a/Providers/FreeSql.Provider.Sqlite/Curd/SqliteInsertOrUpdate.cs b/Providers/FreeSql.Provider.Sqlite/Curd/SqliteInsertOrUpdate.cs new file mode 100644 index 00000000..4c6838cb --- /dev/null +++ b/Providers/FreeSql.Provider.Sqlite/Curd/SqliteInsertOrUpdate.cs @@ -0,0 +1,30 @@ +using FreeSql.Internal; +using System.Linq; + +namespace FreeSql.Sqlite.Curd +{ + + class SqliteInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public SqliteInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = _source; + var sql = insert.ToSql(); + if (sql.StartsWith("INSERT INTO ") == false) return null; + _params = insert._params; + return $"REPLACE INTO {sql.Substring("INSERT INTO ".Length)}"; + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Sqlite/SqliteProvider.cs b/Providers/FreeSql.Provider.Sqlite/SqliteProvider.cs index 8782b6d6..2c18f9b5 100644 --- a/Providers/FreeSql.Provider.Sqlite/SqliteProvider.cs +++ b/Providers/FreeSql.Provider.Sqlite/SqliteProvider.cs @@ -24,6 +24,7 @@ namespace FreeSql.Sqlite public IUpdate Update(object dywhere) where T1 : class => new SqliteUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public IDelete Delete() where T1 : class => new SqliteDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); public IDelete Delete(object dywhere) where T1 : class => new SqliteDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new SqliteInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); public IAdo Ado { get; } public IAop Aop { get; }