From 38ba6c7c36a51d3bdbbfd54c7712dd7d1230ebb6 Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Sun, 10 Sep 2023 11:02:37 -0400 Subject: [PATCH] Add Greatest, Least and NullIf DbFunctions --- .../NpgsqlDbFunctionsExtensions.cs | 338 ++++++++++ .../NpgsqConditionalExpressionsTranslator.cs | 64 ++ .../NpgsqlMethodCallTranslatorProvider.cs | 1 + .../NorthwindDbFunctionsQueryNpgsqlTest.cs | 612 ++++++++++++++++++ 4 files changed, 1015 insertions(+) create mode 100644 src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqConditionalExpressionsTranslator.cs diff --git a/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs b/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs index f1684865b..c6e4acbdb 100644 --- a/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs +++ b/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs @@ -137,4 +137,342 @@ public static int Distance(this DbFunctions _, DateOnly a, DateOnly b) /// public static TimeSpan Distance(this DbFunctions _, DateTime a, DateTime b) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance))); + + /// + /// Returns a value if equals ; otherwise it returns . + /// + /// + /// For more information on NullIf, see + /// . + /// + public static T? NullIf(this DbFunctions _, T value1, T value2) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(NullIf))); + + #region Greatest + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13, T value14) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13, T value14, T value15) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + /// + /// Returns the greatest value from a list of any number of expressions. + /// + /// + /// For more information on Greatest, see + /// . + /// + public static T Greatest(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13, T value14, T value15, T value16) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest))); + + #endregion + + #region Least + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13, T value14) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13, T value14, T value15) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + /// + /// Returns the least value from a list of any number of expressions. + /// + /// + /// For more information on Least, see + /// . + /// + public static T Least(this DbFunctions _, T value1, T value2, T value3, T value4, T value5, T value6, T value7, T value8, T value9, T value10, T value11, T value12, T value13, T value14, T value15, T value16) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Least))); + + #endregion } diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqConditionalExpressionsTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqConditionalExpressionsTranslator.cs new file mode 100644 index 000000000..d9d639d97 --- /dev/null +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqConditionalExpressionsTranslator.cs @@ -0,0 +1,64 @@ +using System.Runtime.CompilerServices; +using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics; + +namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal; + +/// +/// Translates methods into PostgreSQL GREATEST expressions. +/// +public class NpgsqConditionalExpressionsTranslator : IMethodCallTranslator +{ + private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory; + + /// + /// Initializes a new instance of the class. + /// + public NpgsqConditionalExpressionsTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory) + { + _sqlExpressionFactory = sqlExpressionFactory; + } + + /// + public virtual SqlExpression? Translate( + SqlExpression? instance, + MethodInfo method, + IReadOnlyList arguments, + IDiagnosticsLogger logger) + { + if (method.DeclaringType == typeof(NpgsqlDbFunctionsExtensions)) + { + switch (method.Name) + { + case nameof(NpgsqlDbFunctionsExtensions.NullIf): + return _sqlExpressionFactory.Function( + "nullif", + arguments.Skip(1), + nullable: true, + argumentsPropagateNullability: TrueArrays[arguments.Count - 1], + method.ReturnType); + case nameof(NpgsqlDbFunctionsExtensions.Greatest): + return _sqlExpressionFactory.Function( + "greatest", + arguments.Skip(1), + nullable: true, + argumentsPropagateNullability: GetArgumentsPropagateNullability(arguments.Count - 1), + method.ReturnType); + case nameof(NpgsqlDbFunctionsExtensions.Least): + return _sqlExpressionFactory.Function( + "least", + arguments.Skip(1), + nullable: true, + argumentsPropagateNullability: GetArgumentsPropagateNullability(arguments.Count - 1), + method.ReturnType); + } + } + + return null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IEnumerable GetArgumentsPropagateNullability(int length) + => length < TrueArrays.Length + ? TrueArrays[length] + : (IEnumerable)Enumerable.Repeat(true, length).ToArray(); +} diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs index 2a82c7a6d..a7b5442c2 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs @@ -44,6 +44,7 @@ public NpgsqlMethodCallTranslatorProvider( { new NpgsqlArrayMethodTranslator(sqlExpressionFactory, jsonTranslator), new NpgsqlByteArrayMethodTranslator(sqlExpressionFactory), + new NpgsqConditionalExpressionsTranslator(sqlExpressionFactory), new NpgsqlConvertTranslator(sqlExpressionFactory), new NpgsqlDateTimeMethodTranslator(typeMappingSource, sqlExpressionFactory), new NpgsqlFullTextSearchMethodTranslator(typeMappingSource, sqlExpressionFactory, model), diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs index e5c49de7a..39a3137bd 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs @@ -207,6 +207,618 @@ WHERE string_to_array(c."ContactName", ' ', 'Maria') = ARRAY[NULL,'Anders']::tex """); } + [Fact] + public void NullIf_returns_not_null() + { + using var context = CreateContext(); + var nameNotNull = context.Customers + .Where(c => c.CustomerID == "ALFKI") + .Select(c => EF.Functions.NullIf(c.ContactName, "Something")) + .First(); + + Assert.NotNull(nameNotNull); + + AssertSql(""" +SELECT nullif(c."ContactName", 'Something') +FROM "Customers" AS c +WHERE c."CustomerID" = 'ALFKI' +LIMIT 1 +"""); + } + + [Fact] + public void NullIf_returns_null() + { + using var context = CreateContext(); + var nameNull = context.Customers + .Where(c => c.CustomerID == "ALFKI") + .Select(c => EF.Functions.NullIf(c.ContactName, "Maria Anders")) + .First(); + + Assert.Null(nameNull); + + AssertSql(""" +SELECT nullif(c."ContactName", 'Maria Anders') +FROM "Customers" AS c +WHERE c."CustomerID" = 'ALFKI' +LIMIT 1 +"""); + } + + #endregion + + #region Greatest + + [Fact] + public void Greatest_with_1_property() + { + using var context = CreateContext(); + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID)).First(); + + //Assert.Equal(10582, closestOrder.OrderID); + + AssertSql( +""" +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID") NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_2_properties() + { + using var context = CreateContext(); + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, o.OrderID)).First(); + + AssertSql( +""" +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", o."OrderID") NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_3_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_4_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_5_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_6_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_7_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_8_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_9_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_10_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_11_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_12_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_13_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_14_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_15_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Greatest_with_16_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Greatest(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY greatest(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) NULLS FIRST +LIMIT 1 +"""); + } + #endregion + + #region Least + + [Fact] + public void Least_with_1_property() + { + using var context = CreateContext(); + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID)).First(); + + //Assert.Equal(10582, closestOrder.OrderID); + + AssertSql( +""" +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID") NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_2_properties() + { + using var context = CreateContext(); + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, o.OrderID)).First(); + + AssertSql( +""" +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", o."OrderID") NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_3_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_4_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_5_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_6_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_7_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_8_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_9_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_10_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_11_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_12_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_13_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_14_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_15_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) NULLS FIRST +LIMIT 1 +"""); + } + + [Fact] + public void Least_with_16_mix() + { + using var context = CreateContext(); + var value = 1; + var orderDetail = context.OrderDetails.OrderBy(o => EF.Functions.Least(o.ProductID, value, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).First(); + + AssertSql( +""" +@__value_1='1' + +SELECT o."OrderID", o."ProductID", o."Discount", o."Quantity", o."UnitPrice" +FROM "Order Details" AS o +ORDER BY least(o."ProductID", @__value_1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) NULLS FIRST +LIMIT 1 +"""); + } #endregion private void AssertSql(params string[] expected)