From 65b16fbfe557ec744909a54662cd3c802520672a Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 11 Mar 2016 14:18:08 +0100 Subject: [PATCH] U4-8154 - bugfix Sql query expressions parsing --- .../Querying/BaseExpressionHelper.cs | 78 +++++++++++++++---- .../Persistence/Querying/PetaPocoSqlTests.cs | 22 ++++++ 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs index f3cbe5a583..ab6cdb341c 100644 --- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs @@ -128,6 +128,34 @@ namespace Umbraco.Core.Persistence.Querying right = Visit(b.Right); } } + else if (operand == "=") + { + // deal with (x == true|false) - most common + var constRight = b.Right as ConstantExpression; + if (constRight != null && constRight.Type == typeof (bool)) + return ((bool) constRight.Value) ? VisitNotNot(b.Left) : VisitNot(b.Left); + right = Visit(b.Right); + + // deal with (true|false == x) - why not + var constLeft = b.Left as ConstantExpression; + if (constLeft != null && constLeft.Type == typeof (bool)) + return ((bool) constLeft.Value) ? VisitNotNot(b.Right) : VisitNot(b.Right); + left = Visit(b.Left); + } + else if (operand == "<>") + { + // deal with (x != true|false) - most common + var constRight = b.Right as ConstantExpression; + if (constRight != null && constRight.Type == typeof(bool)) + return ((bool) constRight.Value) ? VisitNot(b.Left) : VisitNotNot(b.Left); + right = Visit(b.Right); + + // deal with (true|false != x) - why not + var constLeft = b.Left as ConstantExpression; + if (constLeft != null && constLeft.Type == typeof(bool)) + return ((bool) constLeft.Value) ? VisitNot(b.Right) : VisitNotNot(b.Right); + left = Visit(b.Left); + } else { left = Visit(b.Left); @@ -231,25 +259,47 @@ namespace Umbraco.Core.Persistence.Querying switch (u.NodeType) { case ExpressionType.Not: - var o = Visit(u.Operand); - - //use a Not equal operator instead of <> since we don't know that <> works in all sql servers - - switch (u.Operand.NodeType) - { - case ExpressionType.MemberAccess: - //In this case it wil be a false property , i.e. x => !Trashed - SqlParameters.Add(true); - return string.Format("NOT ({0} = @0)", o); - default: - //In this case it could be anything else, such as: x => !x.Path.StartsWith("-20") - return string.Format("NOT ({0})", o); - } + return VisitNot(u.Operand); default: return Visit(u.Operand); } } + private string VisitNot(Expression exp) + { + var o = Visit(exp); + + // use a "NOT (...)" syntax instead of "<>" since we don't know whether "<>" works in all sql servers + // also, x.StartsWith(...) translates to "x LIKE '...%'" which we cannot "<>" and have to "NOT (...") + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + // false property , i.e. x => !Trashed + SqlParameters.Add(true); + return "NOT (" + o + " = @0)"; + default: + // could be anything else, such as: x => !x.Path.StartsWith("-20") + return "NOT (" + o + ")"; + } + } + + private string VisitNotNot(Expression exp) + { + var o = Visit(exp); + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + // true property, i.e. x => Trashed + SqlParameters.Add(true); + return o + " = @0"; + default: + // could be anything else, such as: x => x.Path.StartsWith("-20") + return o; + } + } + protected virtual string VisitNewArray(NewArrayExpression na) { diff --git a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs index f934b0c536..98d9d9551c 100644 --- a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs @@ -51,6 +51,18 @@ namespace Umbraco.Tests.Persistence.Querying Assert.AreEqual("-20%", sql.Arguments[1]); } + [Test] + public void Where_Clause_With_EqualsFalse_Starts_With() + { + var level = 1; + var sql = new Sql("SELECT *").From().Where(x => x.Level == level && x.Path.StartsWith("-20") == false); + + Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[level] = @0 AND NOT (upper([umbracoNode].[path]) LIKE upper(@1)))", sql.SQL.Replace("\n", " ")); + Assert.AreEqual(2, sql.Arguments.Length); + Assert.AreEqual(level, sql.Arguments[0]); + Assert.AreEqual("-20%", sql.Arguments[1]); + } + [Test] public void Where_Clause_With_Equals_Clause() { @@ -71,6 +83,16 @@ namespace Umbraco.Tests.Persistence.Querying Assert.AreEqual(true, sql.Arguments[0]); } + [Test] + public void Where_Clause_With_EqualsFalse_Boolean() + { + var sql = new Sql("SELECT *").From().Where(x => x.Trashed == false); + + Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (NOT ([umbracoNode].[trashed] = @0))", sql.SQL.Replace("\n", " ")); + Assert.AreEqual(1, sql.Arguments.Length); + Assert.AreEqual(true, sql.Arguments[0]); + } + [Test] public void Where_Clause_With_Boolean() {