Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 12814ad

Browse files
committed
Add support for SqlConflict using command filter
1 parent aa4107a commit 12814ad

File tree

7 files changed

+146
-0
lines changed

7 files changed

+146
-0
lines changed

src/ServiceStack.OrmLite.MySql/MySqlDialectProviderBase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ public override string GetColumnDefinition(FieldDefinition fieldDef)
200200
return ret;
201201
}
202202

203+
public override string SqlConflict(string sql, string conflictResolution)
204+
{
205+
var parts = sql.SplitOnFirst(' ');
206+
return parts[0] + " " + conflictResolution + " " + parts[1];
207+
}
208+
203209
public override string SqlCurrency(string fieldOrValue, string currencySymbol) =>
204210
SqlConcat(new[] { "'" + currencySymbol + "'", "cast(" + fieldOrValue + " as decimal(15,2))" });
205211

src/ServiceStack.OrmLite.PostgreSQL/PostgreSQLDialectProvider.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,14 @@ public override void PrepareStoredProcedureStatement<T>(IDbCommand cmd, T obj)
448448
SetParameterValues<T>(cmd, obj);
449449
}
450450

451+
public override string SqlConflict(string sql, string conflictResolution)
452+
{
453+
//https://www.postgresql.org/docs/current/static/sql-insert.html
454+
return sql + " ON CONFLICT " + (conflictResolution == ConflictResolution.Ignore
455+
? " DO NOTHING"
456+
: conflictResolution);
457+
}
458+
451459
public override string SqlConcat(IEnumerable<object> args) => string.Join(" || ", args);
452460

453461
public override string SqlCurrency(string fieldOrValue, string currencySymbol) => currencySymbol == "$"

src/ServiceStack.OrmLite.Sqlite/SqliteOrmLiteDialectProviderBase.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,13 @@ public override string GetColumnDefinition(FieldDefinition fieldDef)
204204
return ret;
205205
}
206206

207+
public override string SqlConflict(string sql, string conflictResolution)
208+
{
209+
// http://www.sqlite.org/lang_conflict.html
210+
var parts = sql.SplitOnFirst(' ');
211+
return parts[0] + " OR " + conflictResolution + " " + parts[1];
212+
}
213+
207214
public override string SqlConcat(IEnumerable<object> args) => string.Join(" || ", args);
208215

209216
public override string SqlCurrency(string fieldOrValue, string currencySymbol) => SqlConcat(new []{ "'" + currencySymbol + "'", "printf(\"%.2f\", " + fieldOrValue + ")" });

src/ServiceStack.OrmLite/IOrmLiteDialectProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ string ToCreateIndexStatement<T>(Expression<Func<T, object>> field,
207207
string ToInsertStatement<T>(IDbCommand dbCmd, T item, ICollection<string> insertFields = null);
208208
string MergeParamsIntoSql(string sql, IEnumerable<IDbDataParameter> dbParams);
209209

210+
string SqlConflict(string sql, string conflictResolution);
211+
210212
string SqlConcat(IEnumerable<object> args);
211213
string SqlCurrency(string fieldOrValue);
212214
string SqlCurrency(string fieldOrValue, string currencySymbol);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Data;
3+
using ServiceStack.Text;
4+
5+
namespace ServiceStack.OrmLite
6+
{
7+
public class ConflictResolution
8+
{
9+
public const string Ignore = "IGNORE";
10+
}
11+
12+
public static class OrmLiteConflictResolutions
13+
{
14+
public static void OnConflictIgnore(this IDbCommand dbCmd) => dbCmd.OnConflict(ConflictResolution.Ignore);
15+
16+
public static void OnConflict(this IDbCommand dbCmd, string conflictResolution)
17+
{
18+
var pos = dbCmd.CommandText?.IndexOf(' ') ?? -1;
19+
if (pos == -1)
20+
throw new NotSupportedException("Cannot specify ON CONFLICT resolution on Invalid SQL starting with: " + dbCmd.CommandText.SubstringWithElipsis(0, 50));
21+
22+
var sqlConflict = dbCmd.GetDialectProvider().SqlConflict(dbCmd.CommandText, conflictResolution);
23+
dbCmd.CommandText = sqlConflict;
24+
}
25+
}
26+
}

src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,8 @@ protected virtual string ToDropColumnStatement(Type modelType, string columnName
15231523
$"DROP COLUMN {provider.GetQuotedColumnName(columnName)};";
15241524
}
15251525

1526+
public virtual string SqlConflict(string sql, string conflictResolution) => sql; //NOOP
1527+
15261528
public virtual string SqlConcat(IEnumerable<object> args) => $"CONCAT({string.Join(", ", args)})";
15271529

15281530
public virtual string SqlCurrency(string fieldOrValue) => SqlCurrency(fieldOrValue, "$");
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System.Threading.Tasks;
2+
using NUnit.Framework;
3+
using ServiceStack.Common.Tests.Models;
4+
using ServiceStack.Logging;
5+
6+
namespace ServiceStack.OrmLite.Tests
7+
{
8+
public class ConflictResolutionTests : OrmLiteTestBase
9+
{
10+
//public ConflictResolutionTests() : base(Dialect.Sqlite) { }
11+
//[SetUp] public void SetUp() => LogManager.LogFactory = new ConsoleLogFactory(debugEnabled:true);
12+
13+
[Test]
14+
public void Can_change_conflict_resolutiion_with_Insert()
15+
{
16+
using (var db = OpenDbConnection())
17+
{
18+
db.DropAndCreateTable<ModelWithIdAndName>();
19+
20+
var row = new ModelWithIdAndName(1);
21+
db.Insert(row, dbCmd => dbCmd.OnConflictIgnore());
22+
23+
//Equivalent to:
24+
db.Insert(row, dbCmd => dbCmd.OnConflict(ConflictResolution.Ignore));
25+
}
26+
}
27+
28+
[Test]
29+
public async Task Can_change_conflict_resolutiion_with_Insert_Async()
30+
{
31+
using (var db = OpenDbConnection())
32+
{
33+
db.DropAndCreateTable<ModelWithIdAndName>();
34+
35+
var row = new ModelWithIdAndName(1);
36+
await db.InsertAsync(row, dbCmd => dbCmd.OnConflictIgnore());
37+
38+
//Equivalent to:
39+
await db.InsertAsync(row, dbCmd => dbCmd.OnConflict(ConflictResolution.Ignore));
40+
}
41+
}
42+
43+
[Test]
44+
public void Can_change_conflict_resolutiion_with_Insert_AutoIncrement()
45+
{
46+
using (var db = OpenDbConnection())
47+
{
48+
db.DropAndCreateTable<ModelWithIdAndName>();
49+
50+
var row = new ModelWithIdAndName(1);
51+
var insertId = db.Insert(row, dbCmd => dbCmd.OnConflictIgnore(), selectIdentity: true);
52+
Assert.That(insertId, Is.GreaterThan(0));
53+
}
54+
}
55+
56+
[Test]
57+
public async Task Can_change_conflict_resolutiion_with_Insert_AutoIncrement_Async()
58+
{
59+
using (var db = OpenDbConnection())
60+
{
61+
db.DropAndCreateTable<ModelWithIdAndName>();
62+
63+
var row = new ModelWithIdAndName(1);
64+
var insertId = await db.InsertAsync(row, dbCmd => dbCmd.OnConflictIgnore(), selectIdentity: true);
65+
Assert.That(insertId, Is.GreaterThan(0));
66+
}
67+
}
68+
69+
[Test]
70+
public void Can_change_conflict_resolutiion_with_InsertAll()
71+
{
72+
using (var db = OpenDbConnection())
73+
{
74+
var rows = 5.Times(i => new ModelWithIdAndName(i));
75+
76+
db.DropAndCreateTable<ModelWithIdAndName>();
77+
78+
db.InsertAll(rows, dbCmd => dbCmd.OnConflictIgnore());
79+
}
80+
}
81+
82+
[Test]
83+
public async Task Can_change_conflict_resolutiion_with_InsertAll_Async()
84+
{
85+
using (var db = OpenDbConnection())
86+
{
87+
var rows = 5.Times(i => new ModelWithIdAndName(i));
88+
89+
db.DropAndCreateTable<ModelWithIdAndName>();
90+
91+
await db.InsertAllAsync(rows, dbCmd => dbCmd.OnConflictIgnore());
92+
}
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)