diff --git a/README.md b/README.md index c907164..b46d1cd 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ Starting with SQL Server 2022 (16.x) it is possible to use LAG with IGNORE NULLS Use the following expression: ```cs -Expression> lastNonNullExpr = r => EF.Functions.Lag(r.Col1, 0, Clauses.RespectOrIgnoreNulls.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id) +Expression> lastNonNullExpr = r => EF.Functions.Lag(r.Col1, 0, NullHandling.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id) ``` More SQL Server related information on the LAG function here available [here](https://learn.microsoft.com/en-us/sql/t-sql/functions/lead-transact-sql?view=sql-server-ver16#-ignore-nulls--respect-nulls-). diff --git a/src/Zomp.EFCore.WindowFunctions/Clauses/RespectOrIgnoreNulls.cs b/src/Zomp.EFCore.WindowFunctions/NullHandling.cs similarity index 59% rename from src/Zomp.EFCore.WindowFunctions/Clauses/RespectOrIgnoreNulls.cs rename to src/Zomp.EFCore.WindowFunctions/NullHandling.cs index b791c36..c110e45 100644 --- a/src/Zomp.EFCore.WindowFunctions/Clauses/RespectOrIgnoreNulls.cs +++ b/src/Zomp.EFCore.WindowFunctions/NullHandling.cs @@ -1,9 +1,9 @@ -namespace Zomp.EFCore.WindowFunctions.Clauses; +namespace Zomp.EFCore.WindowFunctions; /// -/// Respect Or Ignore Nulls. +/// Specifies whether null should be respected or ignored. /// -public enum RespectOrIgnoreNulls +public enum NullHandling { /// /// Respect nulls. diff --git a/src/Zomp.EFCore.WindowFunctions/Query/Internal/ExpressionVisitorExtensions.cs b/src/Zomp.EFCore.WindowFunctions/Query/Internal/ExpressionVisitorExtensions.cs index db4e68c..c048410 100644 --- a/src/Zomp.EFCore.WindowFunctions/Query/Internal/ExpressionVisitorExtensions.cs +++ b/src/Zomp.EFCore.WindowFunctions/Query/Internal/ExpressionVisitorExtensions.cs @@ -20,9 +20,9 @@ public static Expression VisitWindowFunction(this ExpressionVisitor expressionVi GenerateList(relationalCommandBuilder, windowFunctionExpression.Arguments, e => expressionVisitor.Visit(e)); relationalCommandBuilder.Append(") "); - if (windowFunctionExpression.RespectOrIgnoreNulls is { } respectOrIgnoreNulls) + if (windowFunctionExpression.NullHandling is { } nullHandling) { - relationalCommandBuilder.Append(respectOrIgnoreNulls == RespectOrIgnoreNulls.RespectNulls + relationalCommandBuilder.Append(nullHandling == NullHandling.RespectNulls ? "RESPECT NULLS " : "IGNORE NULLS "); } diff --git a/src/Zomp.EFCore.WindowFunctions/Query/Internal/WindowFunctionsTranslator.cs b/src/Zomp.EFCore.WindowFunctions/Query/Internal/WindowFunctionsTranslator.cs index 9e1973e..5adb4cd 100644 --- a/src/Zomp.EFCore.WindowFunctions/Query/Internal/WindowFunctionsTranslator.cs +++ b/src/Zomp.EFCore.WindowFunctions/Query/Internal/WindowFunctionsTranslator.cs @@ -71,7 +71,7 @@ protected virtual SqlExpression Parse(IReadOnlyList arguments, st var directArgs = new List(); OverExpression? over = null; - RespectOrIgnoreNulls? respectOrIgnoreNulls = null; + NullHandling? nullHandling = null; for (var i = 1; i < arguments.Count; ++i) { @@ -83,16 +83,16 @@ protected virtual SqlExpression Parse(IReadOnlyList arguments, st break; } - if (argument is SqlConstantExpression sce && sce.Type == typeof(RespectOrIgnoreNulls)) + if (argument is SqlConstantExpression sce && sce.Type == typeof(NullHandling)) { - respectOrIgnoreNulls = (RespectOrIgnoreNulls?)sce.Value; + nullHandling = (NullHandling?)sce.Value; continue; } directArgs.Add(sqlExpressionFactory.ApplyDefaultTypeMapping(argument)); } - return new WindowFunctionExpression(functionName, directArgs, respectOrIgnoreNulls, over?.PartitionByExpression?.List, over?.OrderingExpression?.List, over?.OrderingExpression?.RowOrRangeClause, RelationalTypeMapping.NullMapping); + return new WindowFunctionExpression(functionName, directArgs, nullHandling, over?.PartitionByExpression?.List, over?.OrderingExpression?.List, over?.OrderingExpression?.RowOrRangeClause, RelationalTypeMapping.NullMapping); } private static OverExpression GetOrderingSqlExpression(IReadOnlyList arguments) diff --git a/src/Zomp.EFCore.WindowFunctions/Query/SqlExpressions/WindowFunctionExpression.cs b/src/Zomp.EFCore.WindowFunctions/Query/SqlExpressions/WindowFunctionExpression.cs index 2cdd1ff..6367e62 100644 --- a/src/Zomp.EFCore.WindowFunctions/Query/SqlExpressions/WindowFunctionExpression.cs +++ b/src/Zomp.EFCore.WindowFunctions/Query/SqlExpressions/WindowFunctionExpression.cs @@ -5,7 +5,7 @@ /// /// Function (MIN, MAX). /// A list of argument expressions of the Window function. -/// Respect or ignore nulls. +/// Respect or ignore nulls. /// A list of expressions to partition by. /// A list of ordering expressions to order by. /// Row or range clause. @@ -23,7 +23,7 @@ public class WindowFunctionExpression( string function, IReadOnlyList arguments, - RespectOrIgnoreNulls? respectOrIgnoreNulls, + NullHandling? nullHandling, IReadOnlyList? partitions, IReadOnlyList? orderings, RowOrRangeExpression? rowOrRange, @@ -37,7 +37,7 @@ public class WindowFunctionExpression( /// /// Gets the respect nulls or ignore nulls parameter. /// - public RespectOrIgnoreNulls? RespectOrIgnoreNulls { get; } = respectOrIgnoreNulls; + public NullHandling? NullHandling { get; } = nullHandling; /// /// Gets the list of expressions used in partitioning. @@ -74,7 +74,7 @@ public WindowFunctionExpression Update(IReadOnlyList arguments, I && (ReferenceEquals(partitions, Partitions) || partitions.SequenceEqual(Partitions)) && (ReferenceEquals(orderings, Orderings) || orderings.SequenceEqual(Orderings)) ? this - : new(Function, arguments, RespectOrIgnoreNulls, partitions, orderings, RowOrRange, TypeMapping); + : new(Function, arguments, NullHandling, partitions, orderings, RowOrRange, TypeMapping); /// public override bool Equals(object? obj) @@ -135,7 +135,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) } return changed - ? new WindowFunctionExpression(Function, arguments, RespectOrIgnoreNulls, partitions, orderings, RowOrRange, TypeMapping) + ? new WindowFunctionExpression(Function, arguments, NullHandling, partitions, orderings, RowOrRange, TypeMapping) : this; } @@ -147,9 +147,9 @@ protected override void Print(ExpressionPrinter expressionPrinter) expressionPrinter.VisitCollection(Arguments); expressionPrinter.Append(") "); - if (RespectOrIgnoreNulls is { } respectOrIgnoreNulls) + if (NullHandling is { } nullHandling) { - expressionPrinter.Append(respectOrIgnoreNulls == Clauses.RespectOrIgnoreNulls.RespectNulls + expressionPrinter.Append(nullHandling == WindowFunctions.NullHandling.RespectNulls ? "RESPECT NULLS " : "IGNORE NULLS "); } diff --git a/src/Zomp.EFCore.WindowFunctions/Templates/Includes.ttinclude b/src/Zomp.EFCore.WindowFunctions/Templates/Includes.ttinclude index e970cdb..29282bd 100644 --- a/src/Zomp.EFCore.WindowFunctions/Templates/Includes.ttinclude +++ b/src/Zomp.EFCore.WindowFunctions/Templates/Includes.ttinclude @@ -57,7 +57,7 @@ public record Configuration(FunctionType FunctionType, ExpressionType Expression string AlwaysNullableTypeDefinition { get; } = GetAlwaysNullableTypeDefinition(ExpressionType); private static readonly Argument Offset = new("long", "offset", "The offset."); - private static readonly Argument RespectOrIgnoreNulls = new("RespectOrIgnoreNulls?", "respectOrIgnoreNulls", "Respect nulls or ignore nulls."); + private static readonly Argument NullHandling = new("NullHandling?", "nullHandling", "Respect nulls or ignore nulls. If omitted or is specified, provider's default is used which is to respect nulls."); public bool IsGeneric => ExpressionType is ExpressionType.NonNullableStruct or ExpressionType.NullableStruct or ExpressionType.Generic; public bool IsGenericWithQualifier => ExpressionType is ExpressionType.NonNullableStruct or ExpressionType.NullableStruct; public string ReturnType => CustomReturnType ? "TResult" : !string.IsNullOrEmpty(SpecificReturnType) ? SpecificReturnType : IsGeneric ? "T" : TypeDefinition; @@ -71,7 +71,7 @@ public record Configuration(FunctionType FunctionType, ExpressionType Expression list = list[..^NumberOfOptionalArguments]; if (RespectIgnoreNullsOption) { - list.Add(RespectOrIgnoreNulls); + list.Add(NullHandling); } return list; } diff --git a/tests/Zomp.EFCore.WindowFunctions.Testing.Shared/AnalyticTests.cs b/tests/Zomp.EFCore.WindowFunctions.Testing.Shared/AnalyticTests.cs index bda3b2a..5cd94c3 100644 --- a/tests/Zomp.EFCore.WindowFunctions.Testing.Shared/AnalyticTests.cs +++ b/tests/Zomp.EFCore.WindowFunctions.Testing.Shared/AnalyticTests.cs @@ -18,7 +18,7 @@ public void LeadBasic() } [SkippableFact] - public void LeadNullForRespectOrIgnoreNulls() + public void LeadNullForNullHandling() { var query = DbContext.TestRows .Select(r => EF.Functions.Lead(r.Id, Offset, Default, null, EF.Functions.Over().OrderBy(r.Id))); @@ -35,7 +35,7 @@ public void LeadRespectNulls() Skip.If(DbContext.IsSqlite || DbContext.IsPostgreSQL); var query = DbContext.TestRows - .Select(r => EF.Functions.Lead(r.Id, Offset, Default, Clauses.RespectOrIgnoreNulls.RespectNulls, EF.Functions.Over().OrderBy(r.Id))); + .Select(r => EF.Functions.Lead(r.Id, Offset, Default, NullHandling.RespectNulls, EF.Functions.Over().OrderBy(r.Id))); var result = query.ToList(); @@ -51,7 +51,7 @@ public void LeadIgnoreNulls() var query = DbContext.TestRows .Select(r => new { - Lead = EF.Functions.Lead(r.Id, Offset, Default, Clauses.RespectOrIgnoreNulls.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id)), + Lead = EF.Functions.Lead(r.Id, Offset, Default, NullHandling.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id)), Original = r, }) .OrderBy(r => r.Original) @@ -95,8 +95,8 @@ public void LagLastNonNull(bool withDefault) Skip.If(DbContext.IsSqlite || DbContext.IsPostgreSQL); Expression> lastNonNullExpr = withDefault - ? r => EF.Functions.Lag(r.Col1, 0, null, Clauses.RespectOrIgnoreNulls.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id)) - : r => EF.Functions.Lag(r.Col1, 0, Clauses.RespectOrIgnoreNulls.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id)); + ? r => EF.Functions.Lag(r.Col1, 0, null, NullHandling.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id)) + : r => EF.Functions.Lag(r.Col1, 0, NullHandling.IgnoreNulls, EF.Functions.Over().OrderBy(r.Id)); var query = DbContext.TestRows.Select(lastNonNullExpr);