From 2e5de94906f8c66482331156f1a021ae63fd9bad Mon Sep 17 00:00:00 2001 From: Austin Drenski Date: Fri, 25 May 2018 18:44:44 -0400 Subject: [PATCH 1/2] Patch for custom binary operators that use standard comparison symbols When a custom binary operator uses a standard comparison operator (`=`, `<>`, `<`, `>`, `<=`, `>=`) EF Core will generate a pattern that causes a syntax error in PostgreSQL. Example SQL: ```sql WHERE x."Inet" < @__inet_1 = TRUE ``` This patch adds logic to check if the custom binary operator is one of the standard comparison operators. If so, it wraps the expression in parenthesis. Example SQL (patched): ```sql WHERE (x."Inet" < @__inet_1) = TRUE ``` While it seems like this could be avoided by having the custom translator construct a standard `Expression.LessThan(Expression,Expression)`, this causes an error to be thrown...because the binary operator isn't defined. Example stack trace: ``` System.InvalidOperationException : The binary operator LessThanOrEqual is not defined for the types 'System.Net.IPAddress' and 'System.Net.IPAddress'. at System.Linq.Expressions.Expression.GetUserDefinedBinaryOperatorOrThrow(...) at System.Linq.Expressions.Expression.GetComparisonOperator(...) at System.Linq.Expressions.Expression.LessThanOrEqual(...) ``` Related: - https://github.com/aspnet/EntityFrameworkCore/issues/9143 - https://github.com/npgsql/Npgsql.EntityFrameworkCore.PostgreSQL/pull/323#pullrequestreview-101049961 --- .../Sql/Internal/NpgsqlQuerySqlGenerator.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs b/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs index 7d7bde9ea..02e25e91c 100644 --- a/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs +++ b/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Text.RegularExpressions; @@ -41,6 +42,15 @@ public class NpgsqlQuerySqlGenerator : DefaultQuerySqlGenerator { readonly bool _reverseNullOrderingEnabled; + /// + /// The collection of standard comparison operators. + /// + /// + /// See: https://github.com/aspnet/EntityFrameworkCore/issues/9143 + /// + [NotNull] static readonly HashSet StandardComparisonSymbols = + new HashSet(new[] { "=", "<>", "<", ">", "<=", ">=" }); + protected override string TypedTrueLiteral => "TRUE::bool"; protected override string TypedFalseLiteral => "FALSE::bool"; @@ -322,12 +332,28 @@ public virtual Expression VisitCustomBinary(CustomBinaryExpression expression) { Check.NotNull(expression, nameof(expression)); + //-------------------------------------------------------------------------------------------- + // BUG: custom relational operators cause Postgres to throw 42601: syntax error at or near "=" + // Example: + // source.Where(x => EF.Functions.LessThan(x.Inet, inet)); + // WHERE x."Inet" < @__inet_1 = TRUE + // + // See: https://github.com/aspnet/EntityFrameworkCore/issues/9143 + //-------------------------------------------------------------------------------------------- + var operatorIsComparisonSymbol = StandardComparisonSymbols.Contains(expression.Operator); + + if (operatorIsComparisonSymbol) + Sql.Append('('); + Visit(expression.Left); Sql.Append(" "); Sql.Append(expression.Operator); Sql.Append(" "); Visit(expression.Right); + if (operatorIsComparisonSymbol) + Sql.Append(')'); + return expression; } From 87a418bb47c1f5c0f8ba3c91ac9fee9c1340732a Mon Sep 17 00:00:00 2001 From: Austin Drenski Date: Sat, 26 May 2018 02:09:29 -0400 Subject: [PATCH 2/2] Update to always wrap a custom binary expression in parenthesis - A future PR could look more thoughtfully at operator precedence overall - Either at generation time or as an analytical stage. - See:https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-PRECEDENCE-TABLE - Updates tests that check for SQL affected by this PR. --- QueryBaseline.cs | 218 ++++++++++++++++++ .../Sql/Internal/NpgsqlQuerySqlGenerator.cs | 32 +-- .../FullTextSearchDbFunctionsNpgsqlTest.cs | 14 +- .../Query/RangeQueryNpgsqlTest.cs | 42 ++-- .../Query/SimpleQueryNpgsqlTest.cs | 15 +- 5 files changed, 257 insertions(+), 64 deletions(-) diff --git a/QueryBaseline.cs b/QueryBaseline.cs index ae7130b75..c0803e86f 100644 --- a/QueryBaseline.cs +++ b/QueryBaseline.cs @@ -3796,3 +3796,221 @@ +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(secs => 1)) AS ""OrderDate"" +FROM ""Orders"" AS o +WHERE o.""OrderDate"" IS NOT NULL"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(years => 1)) AS ""OrderDate"" +FROM ""Orders"" AS o +WHERE o.""OrderDate"" IS NOT NULL"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(mins => 1)) AS ""OrderDate"" +FROM ""Orders"" AS o +WHERE o.""OrderDate"" IS NOT NULL"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"@__years_0='2' + +SELECT (o.""OrderDate"" + MAKE_INTERVAL(years => @__years_0)) AS ""OrderDate"" +FROM ""Orders"" AS o +WHERE o.""OrderDate"" IS NOT NULL"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(months => 1)) AS ""OrderDate"" +FROM ""Orders"" AS o +WHERE o.""OrderDate"" IS NOT NULL"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(hours => 1)) AS ""OrderDate"" +FROM ""Orders"" AS o +WHERE o.""OrderDate"" IS NOT NULL"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('a & b') && to_tsquery('c & d')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('a & b') || to_tsquery('c & d')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsvector('a') @@ 'b') +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsvector('a') @@ to_tsquery('b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('b') <@ to_tsquery('a & b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('a & b') @> to_tsquery('b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsvector('b') || to_tsvector('c')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('a & b') && to_tsquery('c & d')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('a & b') || to_tsquery('c & d')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsvector('a') @@ 'b') +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsvector('a') @@ to_tsquery('b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('b') <@ to_tsquery('a & b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsquery('a & b') @> to_tsquery('b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) : + AssertSql( + @"SELECT (to_tsvector('b') || to_tsvector('c')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryAnd() : + AssertSql( + @"SELECT (to_tsquery('a & b') && to_tsquery('c & d')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryOr() : + AssertSql( + @"SELECT (to_tsquery('a & b') || to_tsquery('c & d')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.Matches_With_String() : + AssertSql( + @"SELECT (to_tsvector('a') @@ 'b') +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.Matches_With_Tsquery() : + AssertSql( + @"SELECT (to_tsvector('a') @@ to_tsquery('b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryIsContainedIn() : + AssertSql( + @"SELECT (to_tsquery('b') <@ to_tsquery('a & b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryContains() : + AssertSql( + @"SELECT (to_tsquery('a & b') @> to_tsquery('b')) +FROM ""Customers"" AS c +LIMIT 1"); + + + +Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsVectorConcat() : + AssertSql( + @"SELECT (to_tsvector('b') || to_tsvector('c')) +FROM ""Customers"" AS c +LIMIT 1"); + + + diff --git a/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs b/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs index 02e25e91c..287764238 100644 --- a/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs +++ b/src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs @@ -24,7 +24,6 @@ #endregion using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Text.RegularExpressions; @@ -42,15 +41,6 @@ public class NpgsqlQuerySqlGenerator : DefaultQuerySqlGenerator { readonly bool _reverseNullOrderingEnabled; - /// - /// The collection of standard comparison operators. - /// - /// - /// See: https://github.com/aspnet/EntityFrameworkCore/issues/9143 - /// - [NotNull] static readonly HashSet StandardComparisonSymbols = - new HashSet(new[] { "=", "<>", "<", ">", "<=", ">=" }); - protected override string TypedTrueLiteral => "TRUE::bool"; protected override string TypedFalseLiteral => "FALSE::bool"; @@ -332,27 +322,13 @@ public virtual Expression VisitCustomBinary(CustomBinaryExpression expression) { Check.NotNull(expression, nameof(expression)); - //-------------------------------------------------------------------------------------------- - // BUG: custom relational operators cause Postgres to throw 42601: syntax error at or near "=" - // Example: - // source.Where(x => EF.Functions.LessThan(x.Inet, inet)); - // WHERE x."Inet" < @__inet_1 = TRUE - // - // See: https://github.com/aspnet/EntityFrameworkCore/issues/9143 - //-------------------------------------------------------------------------------------------- - var operatorIsComparisonSymbol = StandardComparisonSymbols.Contains(expression.Operator); - - if (operatorIsComparisonSymbol) - Sql.Append('('); - + Sql.Append('('); Visit(expression.Left); - Sql.Append(" "); + Sql.Append(' '); Sql.Append(expression.Operator); - Sql.Append(" "); + Sql.Append(' '); Visit(expression.Right); - - if (operatorIsComparisonSymbol) - Sql.Append(')'); + Sql.Append(')'); return expression; } diff --git a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs index 9178914cc..6d8acf722 100644 --- a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs @@ -208,7 +208,7 @@ public void TsQueryAnd() } AssertSql( - @"SELECT to_tsquery('a & b') && to_tsquery('c & d') + @"SELECT (to_tsquery('a & b') && to_tsquery('c & d')) FROM ""Customers"" AS c LIMIT 1"); } @@ -225,7 +225,7 @@ public void TsQueryOr() } AssertSql( - @"SELECT to_tsquery('a & b') || to_tsquery('c & d') + @"SELECT (to_tsquery('a & b') || to_tsquery('c & d')) FROM ""Customers"" AS c LIMIT 1"); } @@ -259,7 +259,7 @@ public void TsQueryContains() } AssertSql( - @"SELECT to_tsquery('a & b') @> to_tsquery('b') + @"SELECT (to_tsquery('a & b') @> to_tsquery('b')) FROM ""Customers"" AS c LIMIT 1"); } @@ -276,7 +276,7 @@ public void TsQueryIsContainedIn() } AssertSql( - @"SELECT to_tsquery('b') <@ to_tsquery('a & b') + @"SELECT (to_tsquery('b') <@ to_tsquery('a & b')) FROM ""Customers"" AS c LIMIT 1"); } @@ -436,7 +436,7 @@ public void Matches_With_String() } AssertSql( - @"SELECT to_tsvector('a') @@ 'b' + @"SELECT (to_tsvector('a') @@ 'b') FROM ""Customers"" AS c LIMIT 1"); } @@ -453,7 +453,7 @@ public void Matches_With_Tsquery() } AssertSql( - @"SELECT to_tsvector('a') @@ to_tsquery('b') + @"SELECT (to_tsvector('a') @@ to_tsquery('b')) FROM ""Customers"" AS c LIMIT 1"); } @@ -470,7 +470,7 @@ public void TsVectorConcat() } AssertSql( - @"SELECT to_tsvector('b') || to_tsvector('c') + @"SELECT (to_tsvector('b') || to_tsvector('c')) FROM ""Customers"" AS c LIMIT 1"); } diff --git a/test/EFCore.PG.FunctionalTests/Query/RangeQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/RangeQueryNpgsqlTest.cs index c31d32016..7d08e0763 100644 --- a/test/EFCore.PG.FunctionalTests/Query/RangeQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/RangeQueryNpgsqlTest.cs @@ -47,7 +47,7 @@ public void RangeContainsRange(NpgsqlRange range) .Where(x => x.Range.Contains(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" @> @__range_0 = TRUE"); + AssertContainsSql("WHERE (x.\"Range\" @> @__range_0) = TRUE"); } } @@ -65,7 +65,7 @@ public void RangeDoesNotContainRange(NpgsqlRange range) .Where(x => !x.Range.Contains(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" @> @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" @> @__range_0) = TRUE)"); } } @@ -83,7 +83,7 @@ public void RangeContainsValue(int value) .Where(x => x.Range.Contains(value)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" @> @__value_0"); + AssertContainsSql("WHERE (x.\"Range\" @> @__value_0)"); } } @@ -101,7 +101,7 @@ public void RangeDoesNotContainValue(int value) .Where(x => !x.Range.Contains(value)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" @> @__value_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" @> @__value_0) = TRUE)"); } } @@ -119,7 +119,7 @@ public void RangeContainedByRange(NpgsqlRange range) .Where(x => range.ContainedBy(x.Range)) .ToArray(); - AssertContainsSql("WHERE @__range_0 <@ x.\"Range\" = TRUE"); + AssertContainsSql("WHERE (@__range_0 <@ x.\"Range\") = TRUE"); } } @@ -137,7 +137,7 @@ public void RangeNotContainedByRange(NpgsqlRange range) .Where(x => !range.ContainedBy(x.Range)) .ToArray(); - AssertContainsSql("WHERE NOT (@__range_0 <@ x.\"Range\" = TRUE)"); + AssertContainsSql("WHERE NOT ((@__range_0 <@ x.\"Range\") = TRUE)"); } } @@ -227,7 +227,7 @@ public void RangeOvelapsRange(NpgsqlRange range) .Where(x => x.Range.Overlaps(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" && @__range_0"); + AssertContainsSql("WHERE (x.\"Range\" && @__range_0)"); } } @@ -245,7 +245,7 @@ public void RangeDoesNotOvelapRange(NpgsqlRange range) .Where(x => !x.Range.Overlaps(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" && @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" && @__range_0) = TRUE)"); } } @@ -263,7 +263,7 @@ public void RangeIsStrictlyLeftOfRange(NpgsqlRange range) .Where(x => x.Range.IsStrictlyLeftOf(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" << @__range_0"); + AssertContainsSql("WHERE (x.\"Range\" << @__range_0)"); } } @@ -281,7 +281,7 @@ public void RangeIsNotStrictlyLeftOfRange(NpgsqlRange range) .Where(x => !x.Range.IsStrictlyLeftOf(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" << @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" << @__range_0) = TRUE)"); } } @@ -299,7 +299,7 @@ public void RangeIsStrictlyRightOfRange(NpgsqlRange range) .Where(x => x.Range.IsStrictlyRightOf(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" >> @__range_0"); + AssertContainsSql("WHERE (x.\"Range\" >> @__range_0)"); } } @@ -317,7 +317,7 @@ public void RangeIsNotStrictlyRightOfRange(NpgsqlRange range) .Where(x => !x.Range.IsStrictlyRightOf(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" >> @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" >> @__range_0) = TRUE)"); } } @@ -335,7 +335,7 @@ public void RangeDoesNotExtendLeftOfRange(NpgsqlRange range) .Where(x => x.Range.DoesNotExtendLeftOf(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" &> @__range_0"); + AssertContainsSql("WHERE (x.\"Range\" &> @__range_0)"); } } @@ -353,7 +353,7 @@ public void RangeDoesExtendLeftOfRange(NpgsqlRange range) .Where(x => !x.Range.DoesNotExtendLeftOf(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" &> @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" &> @__range_0) = TRUE)"); } } @@ -371,7 +371,7 @@ public void RangeDoesNotExtendRightOfRange(NpgsqlRange range) .Where(x => x.Range.DoesNotExtendRightOf(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" &< @__range_0"); + AssertContainsSql("WHERE (x.\"Range\" &< @__range_0)"); } } @@ -389,7 +389,7 @@ public void RangeDoesExtendRightOfRange(NpgsqlRange range) .Where(x => !x.Range.DoesNotExtendRightOf(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" &< @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" &< @__range_0) = TRUE)"); } } @@ -407,7 +407,7 @@ public void RangeIsAdjacentToRange(NpgsqlRange range) .Where(x => x.Range.IsAdjacentTo(range)) .ToArray(); - AssertContainsSql("WHERE x.\"Range\" -|- @__range_0"); + AssertContainsSql("WHERE (x.\"Range\" -|- @__range_0) = TRUE"); } } @@ -425,7 +425,7 @@ public void RangeIsNotAdjacentToRange(NpgsqlRange range) .Where(x => !x.Range.IsAdjacentTo(range)) .ToArray(); - AssertContainsSql("WHERE NOT (x.\"Range\" -|- @__range_0 = TRUE)"); + AssertContainsSql("WHERE NOT ((x.\"Range\" -|- @__range_0) = TRUE)"); } } @@ -443,7 +443,7 @@ public void RangeUnionRange(NpgsqlRange range) .Select(x => x.Range.Union(range)) .ToArray(); - AssertContainsSql("SELECT x.\"Range\" + @__range_0"); + AssertContainsSql("SELECT (x.\"Range\" + @__range_0)"); } } @@ -461,7 +461,7 @@ public void RangeIntersectsRange(NpgsqlRange range) .Select(x => x.Range.Intersect(range)) .ToArray(); - AssertContainsSql("SELECT x.\"Range\" * @__range_0"); + AssertContainsSql("SELECT (x.\"Range\" * @__range_0)"); } } @@ -486,7 +486,7 @@ public void RangeExceptRange(NpgsqlRange range) // ignore: Npgsql.PostgresException : 22000: result of range difference would not be contiguous. } - AssertContainsSql("SELECT x.\"Range\" - @__range_0"); + AssertContainsSql("SELECT (x.\"Range\" - @__range_0)"); } } diff --git a/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs index 368702d9c..333ea6777 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Microsoft.EntityFrameworkCore.TestUtilities; @@ -20,7 +19,7 @@ public override void Select_expression_date_add_year() base.Select_expression_date_add_year(); AssertSql( - @"SELECT o.""OrderDate"" + MAKE_INTERVAL(years => 1) AS ""OrderDate"" + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(years => 1)) AS ""OrderDate"" FROM ""Orders"" AS o WHERE o.""OrderDate"" IS NOT NULL"); } @@ -42,7 +41,7 @@ public void Select_expression_date_add_year_param() AssertSql( @"@__years_0='2' -SELECT o.""OrderDate"" + MAKE_INTERVAL(years => @__years_0) AS ""OrderDate"" +SELECT (o.""OrderDate"" + MAKE_INTERVAL(years => @__years_0)) AS ""OrderDate"" FROM ""Orders"" AS o WHERE o.""OrderDate"" IS NOT NULL"); } @@ -62,7 +61,7 @@ public void Select_expression_datetime_add_month() e => e.OrderDate); AssertSql( - @"SELECT o.""OrderDate"" + MAKE_INTERVAL(months => 1) AS ""OrderDate"" + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(months => 1)) AS ""OrderDate"" FROM ""Orders"" AS o WHERE o.""OrderDate"" IS NOT NULL"); } @@ -80,7 +79,7 @@ public void Select_expression_datetime_add_hour() e => e.OrderDate); AssertSql( - @"SELECT o.""OrderDate"" + MAKE_INTERVAL(hours => 1) AS ""OrderDate"" + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(hours => 1)) AS ""OrderDate"" FROM ""Orders"" AS o WHERE o.""OrderDate"" IS NOT NULL"); } @@ -98,7 +97,7 @@ public void Select_expression_datetime_add_minute() e => e.OrderDate); AssertSql( - @"SELECT o.""OrderDate"" + MAKE_INTERVAL(mins => 1) AS ""OrderDate"" + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(mins => 1)) AS ""OrderDate"" FROM ""Orders"" AS o WHERE o.""OrderDate"" IS NOT NULL"); } @@ -116,7 +115,7 @@ public void Select_expression_datetime_add_second() e => e.OrderDate); AssertSql( - @"SELECT o.""OrderDate"" + MAKE_INTERVAL(secs => 1) AS ""OrderDate"" + @"SELECT (o.""OrderDate"" + MAKE_INTERVAL(secs => 1)) AS ""OrderDate"" FROM ""Orders"" AS o WHERE o.""OrderDate"" IS NOT NULL"); }