Skip to content

Commit

Permalink
* Testing MySql variable use for filter conditions thanks to @APIWT, #47
Browse files Browse the repository at this point in the history
  • Loading branch information
artiomchi committed May 22, 2019
1 parent 6055d7d commit 0954300
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace FlexLabs.EntityFrameworkCore.Upsert.Runners
{
/// <summary>
/// Modifier flags that may affect how an expression is translated to SQL
/// </summary>
[Flags]
public enum ExpressionModifiers
{
/// <summary>
/// Replace references to the DB column with a variable referencing this column
/// </summary>
LeftPropertyAsVariable = 1,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ public override string GenerateCommand(string tableName, ICollection<ICollection
ICollection<(string ColumnName, bool IsNullable)> joinColumns, ICollection<(string ColumnName, IKnownValue Value)> updateExpressions,
KnownExpression updateCondition)
{
if (updateCondition != null)
throw UnsupportedExpressionException.MySQLConditionalUpdate();

var result = new StringBuilder("INSERT ");
if (updateExpressions == null)
result.Append("IGNORE ");
Expand All @@ -40,9 +37,14 @@ public override string GenerateCommand(string tableName, ICollection<ICollection
if (updateExpressions != null)
{
result.Append(" ON DUPLICATE KEY UPDATE ");
var variables = updateCondition != null
? string.Join(", ", updateExpressions.Select(e => $"IF (({Variable(e.ColumnName)} := {EscapeName(e.ColumnName)}), NULL, NULL)"))
: null;
result.Append(string.Join(", ", updateExpressions
.Select((e, i) => updateCondition != null
? $"{EscapeName(e.ColumnName)} = IF ({ExpandExpression(updateCondition)}, {ExpandValue(e.Value)}, {EscapeName(e.ColumnName)})"
? i == 0
? $"{EscapeName(e.ColumnName)} = COALESCE ({variables}, IF ({ExpandExpression(updateCondition, ExpressionModifiers.LeftPropertyAsVariable)}, {ExpandValue(e.Value)}, {EscapeName(e.ColumnName)}))"
: $"{EscapeName(e.ColumnName)} = IF ({ExpandExpression(updateCondition, ExpressionModifiers.LeftPropertyAsVariable)}, {ExpandValue(e.Value)}, {EscapeName(e.ColumnName)})"
: $"{EscapeName(e.ColumnName)} = {ExpandValue(e.Value)}")));
}
return result.ToString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public abstract string GenerateCommand(string tableName, ICollection<ICollection
/// <returns>The reference to the parameter</returns>
protected virtual string Parameter(int index) => "@p" + index;
/// <summary>
/// Reference an named variable defined by the query runner
/// </summary>
/// <param name="name">The name of the variable</param>
/// <returns>The reference to the variable</returns>
protected virtual string Variable(string name) => "@x" + name;
/// <summary>
/// Get the escaped database table schema
/// </summary>
/// <param name="entityType">The entity type of the table</param>
Expand Down Expand Up @@ -167,12 +173,16 @@ protected virtual string GetSchema(IEntityType entityType)
/// Expand a known value into database syntax
/// </summary>
/// <param name="value">The KnownValue that has to be converted to database language</param>
/// <param name="modifiers">Modifier flags that may affect how an expression is translated to SQL</param>
/// <returns>A string containing the expression converted to database language</returns>
protected virtual string ExpandValue(IKnownValue value)
protected virtual string ExpandValue(IKnownValue value, ExpressionModifiers modifiers = 0)
{
switch (value)
{
case PropertyValue prop:
if (modifiers.HasFlag(ExpressionModifiers.LeftPropertyAsVariable) && prop.IsLeftParameter)
return Variable(prop.Property.Relational().ColumnName);

var prefix = prop.IsLeftParameter ? TargetPrefix : SourcePrefix;
var suffix = prop.IsLeftParameter ? TargetSuffix : SourceSuffix;
return prefix + EscapeName(prop.Property.Relational().ColumnName) + suffix;
Expand All @@ -192,8 +202,9 @@ protected virtual string ExpandValue(IKnownValue value)
/// Expand a known expression into database syntax
/// </summary>
/// <param name="expression">The KnownExpression that has to be converted to database language</param>
/// <param name="modifiers">Modifier flags that may affect how an expression is translated to SQL</param>
/// <returns>A string containing the expression converted to database language</returns>
protected virtual string ExpandExpression(KnownExpression expression)
protected virtual string ExpandExpression(KnownExpression expression, ExpressionModifiers modifiers = 0)
{
switch (expression.ExpressionType)
{
Expand All @@ -209,31 +220,31 @@ protected virtual string ExpandExpression(KnownExpression expression)
case ExpressionType.Equal:
case ExpressionType.NotEqual:
{
var left = ExpandValue(expression.Value1);
var right = ExpandValue(expression.Value2);
var left = ExpandValue(expression.Value1, modifiers);
var right = ExpandValue(expression.Value2, modifiers);
var op = GetSimpleOperator(expression.ExpressionType);
return $"{left} {op} {right}";
}

case ExpressionType.Coalesce:
{
var left = ExpandValue(expression.Value1);
var right = ExpandValue(expression.Value2);
var left = ExpandValue(expression.Value1, modifiers);
var right = ExpandValue(expression.Value2, modifiers);
return $"COALESCE({left}, {right})";
}

case ExpressionType.Conditional:
{
var ifTrue = ExpandValue(expression.Value1);
var ifFalse = ExpandValue(expression.Value2);
var test = ExpandValue(expression.Value3);
var ifTrue = ExpandValue(expression.Value1, modifiers);
var ifFalse = ExpandValue(expression.Value2, modifiers);
var test = ExpandValue(expression.Value3, modifiers);
return $"CASE WHEN {test} THEN {ifTrue} ELSE {ifFalse} END";
}

case ExpressionType.MemberAccess:
case ExpressionType.Constant:
{
return ExpandValue(expression.Value1);
return ExpandValue(expression.Value1, modifiers);
}

default: throw new NotSupportedException("Don't know how to process operation: " + expression.ExpressionType);
Expand Down
11 changes: 5 additions & 6 deletions test/FlexLabs.EntityFrameworkCore.Upsert.Tests/EF/BasicTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ static BasicTest()

public static readonly List<TestDbContext.DbDriver> DatabaseEngines;
public static IEnumerable<object[]> GetDatabaseEngines() => DatabaseEngines.Select(e => new object[] { e });
public static IEnumerable<object[]> GetDatabaseEnginesExceptMySql() => DatabaseEngines.Where(e => e != TestDbContext.DbDriver.MySQL).Select(e => new object[] { e });

public class Contexts : IDisposable
{
Expand Down Expand Up @@ -1613,7 +1612,7 @@ public void Upsert_ConditionalExpression_UpdateFalse(TestDbContext.DbDriver driv
}
}
[Theory]
[MemberData(nameof(GetDatabaseEnginesExceptMySql))]
[MemberData(nameof(GetDatabaseEngines))]
public void Upsert_UpdateCondition_New(TestDbContext.DbDriver driver)
{
ResetDb(driver);
Expand Down Expand Up @@ -1643,7 +1642,7 @@ public void Upsert_UpdateCondition_New(TestDbContext.DbDriver driver)


[Theory]
[MemberData(nameof(GetDatabaseEnginesExceptMySql))]
[MemberData(nameof(GetDatabaseEngines))]
public void Upsert_UpdateCondition_New_AutoUpdate(TestDbContext.DbDriver driver)
{
ResetDb(driver);
Expand All @@ -1668,7 +1667,7 @@ public void Upsert_UpdateCondition_New_AutoUpdate(TestDbContext.DbDriver driver)
}

[Theory]
[MemberData(nameof(GetDatabaseEnginesExceptMySql))]
[MemberData(nameof(GetDatabaseEngines))]
public void Upsert_UpdateCondition_Update(TestDbContext.DbDriver driver)
{
var dbItem = new TestEntity
Expand Down Expand Up @@ -1716,7 +1715,7 @@ public void Upsert_UpdateCondition_Update(TestDbContext.DbDriver driver)
}

[Theory]
[MemberData(nameof(GetDatabaseEnginesExceptMySql))]
[MemberData(nameof(GetDatabaseEngines))]
public void Upsert_UpdateCondition_AutoUpdate(TestDbContext.DbDriver driver)
{
var dbItem = new TestEntity
Expand Down Expand Up @@ -1760,7 +1759,7 @@ public void Upsert_UpdateCondition_AutoUpdate(TestDbContext.DbDriver driver)
}

[Theory]
[MemberData(nameof(GetDatabaseEnginesExceptMySql))]
[MemberData(nameof(GetDatabaseEngines))]
public void Upsert_UpdateCondition_NoUpdate(TestDbContext.DbDriver driver)
{
var dbItem = new TestEntity
Expand Down

0 comments on commit 0954300

Please sign in to comment.