-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #158 from dadyarri/support-oracle
Add Oracle DBMS Support
- Loading branch information
Showing
15 changed files
with
287 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/FlexLabs.EntityFrameworkCore.Upsert/Runners/OracleUpsertCommandRunner.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Text; | ||
using FlexLabs.EntityFrameworkCore.Upsert.Internal; | ||
|
||
namespace FlexLabs.EntityFrameworkCore.Upsert.Runners | ||
{ | ||
/// <summary> | ||
/// Upsert command runner for the Oracle.EntityFrameworkCore provider | ||
/// </summary> | ||
public class OracleUpsertCommandRunner : RelationalUpsertCommandRunner | ||
{ | ||
/// <inheritdoc /> | ||
public override bool Supports(string providerName) => providerName == "Oracle.EntityFrameworkCore"; | ||
/// <inheritdoc /> | ||
protected override string EscapeName([NotNull] string name) => $"\"{name}\""; | ||
/// <inheritdoc /> | ||
protected override string? SourcePrefix => "s."; | ||
/// <inheritdoc /> | ||
protected override string? TargetPrefix => "t."; | ||
/// <inheritdoc /> | ||
protected override string Parameter(int index) => $":p{index}"; | ||
/// <inheritdoc /> | ||
protected override int? MaxQueryParams => 1000; | ||
|
||
/// <inheritdoc /> | ||
public override string GenerateCommand( | ||
string tableName, | ||
ICollection<ICollection<(string ColumnName, ConstantValue Value, string DefaultSql, bool AllowInserts)>> entities, | ||
ICollection<(string ColumnName, bool IsNullable)> joinColumns, | ||
ICollection<(string ColumnName, IKnownValue Value)>? updateExpressions, | ||
KnownExpression? updateCondition) | ||
{ | ||
ArgumentNullException.ThrowIfNull(entities); | ||
var result = new StringBuilder(); | ||
|
||
result.Append(CultureInfo.InvariantCulture, $"MERGE INTO {tableName} t USING ("); | ||
foreach (var item in entities.Select((e, ind) => new {e, ind})) | ||
{ | ||
result.Append(" SELECT "); | ||
result.Append(string.Join(", ", item.e.Select(ec => string.Join(" AS ", ExpandValue(ec.Value), EscapeName(ec.ColumnName))))); | ||
result.Append(" FROM dual"); | ||
if (entities.Count > 1 && item.ind != entities.Count - 1) | ||
{ | ||
result.Append(" UNION ALL "); | ||
} | ||
} | ||
result.Append(") s ON ("); | ||
result.Append(string.Join(" AND ", joinColumns.Select(j => $"t.{EscapeName(j.ColumnName)} = s.{EscapeName(j.ColumnName)}"))); | ||
result.Append(") "); | ||
result.Append(" WHEN NOT MATCHED THEN INSERT ("); | ||
result.Append(string.Join(", ", entities.First().Where(e => e.AllowInserts).Select(e => EscapeName(e.ColumnName)))); | ||
result.Append(") VALUES ("); | ||
result.Append(string.Join(", ", entities.First().Where(e => e.AllowInserts).Select(e => $"s.{EscapeName(e.ColumnName)}"))); | ||
result.Append(") "); | ||
if (updateExpressions is not null) | ||
{ | ||
result.Append("WHEN MATCHED "); | ||
|
||
result.Append("THEN UPDATE SET "); | ||
result.Append(string.Join(", ", updateExpressions.Select(e => $"t.{EscapeName(e.ColumnName)} = {ExpandValue(e.Value)}"))); | ||
if (updateCondition is not null) | ||
{ | ||
result.Append(CultureInfo.InvariantCulture, $" WHERE {ExpandExpression(updateCondition)} "); | ||
} | ||
} | ||
|
||
return result.ToString(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override string ExpandExpression(KnownExpression expression, Func<string, string>? expandLeftColumn = null) | ||
{ | ||
ArgumentNullException.ThrowIfNull(expression); | ||
|
||
switch (expression.ExpressionType) | ||
{ | ||
case ExpressionType.And: | ||
{ | ||
var left = ExpandValue(expression.Value1, expandLeftColumn); | ||
var right = ExpandValue(expression.Value2!, expandLeftColumn); | ||
return $"BITAND({left}, {right})"; | ||
} | ||
case ExpressionType.Or: | ||
{ | ||
var left = ExpandValue(expression.Value1, expandLeftColumn); | ||
var right = ExpandValue(expression.Value2!, expandLeftColumn); | ||
return $"BITOR({left}, {right})"; | ||
} | ||
case ExpressionType.Modulo: | ||
{ | ||
var left = ExpandValue(expression.Value1, expandLeftColumn); | ||
var right = ExpandValue(expression.Value2!, expandLeftColumn); | ||
return $"MOD({left}, {right})"; | ||
} | ||
|
||
default: | ||
return base.ExpandExpression(expression, expandLeftColumn); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,5 +7,6 @@ public enum DbDriver | |
MySQL, | ||
InMemory, | ||
Sqlite, | ||
Oracle, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
...bs.EntityFrameworkCore.Upsert.IntegrationTests/ContainerisedDatabaseInitializerFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System.Threading.Tasks; | ||
using DotNet.Testcontainers.Containers; | ||
|
||
namespace FlexLabs.EntityFrameworkCore.Upsert.IntegrationTests | ||
{ | ||
public abstract class ContainerisedDatabaseInitializerFixture<TContainer> : DatabaseInitializerFixture | ||
where TContainer : IContainer, IDatabaseContainer | ||
{ | ||
public TContainer TestContainer { get; } | ||
|
||
public ContainerisedDatabaseInitializerFixture() | ||
{ | ||
if (!BuildEnvironment.UseLocalService) | ||
{ | ||
TestContainer = BuildContainer(); | ||
} | ||
} | ||
|
||
protected abstract TContainer BuildContainer(); | ||
|
||
public override async Task InitializeAsync() | ||
{ | ||
if (TestContainer is not null) | ||
{ | ||
await TestContainer.StartAsync(); | ||
} | ||
|
||
await base.InitializeAsync(); | ||
} | ||
|
||
public override async Task DisposeAsync() | ||
{ | ||
if (TestContainer is not null) | ||
{ | ||
await TestContainer.StopAsync(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.