diff --git a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/SortByColumns.cs b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/SortByColumns.cs index ec4463cd84..51a2649d71 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/SortByColumns.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/SortByColumns.cs @@ -273,7 +273,11 @@ private bool CheckTypesOrderTableOverload(CheckTypesContext context, TexlNode[] } var column = columns.Single(); - if (columnType != null && !columnType.Accepts(column.Type, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) + + // Allow numeric coercion between Number (Float) and Decimal for sort order values. + // A Float column can be ordered by a Decimal order table and vice versa. + var isNumericCoercionAllowed = columnType != null && columnType.IsNumeric && column.Type.IsNumeric; + if (columnType != null && !isNumericCoercionAllowed && !columnType.Accepts(column.Type, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules)) { errors.EnsureError( DocumentErrorSeverity.Severe, diff --git a/src/libraries/Microsoft.PowerFx.Interpreter/Functions/LibraryTable.cs b/src/libraries/Microsoft.PowerFx.Interpreter/Functions/LibraryTable.cs index d53d204078..5c0abc1fec 100644 --- a/src/libraries/Microsoft.PowerFx.Interpreter/Functions/LibraryTable.cs +++ b/src/libraries/Microsoft.PowerFx.Interpreter/Functions/LibraryTable.cs @@ -1001,6 +1001,11 @@ public static async ValueTask SortByColumnsOrderTable(EvalVisitor return CommonErrors.RuntimeTypeMismatch(irContext); } + // Normalize numeric types so that Number (double) and Decimal can be compared + // interchangeably. Without this, Float column values would never match Decimal + // order-table values via IndexOf because double != decimal in object.Equals. + primitiveValue = NormalizeNumericPrimitive(primitiveValue); + if (orderTableValues.Contains(primitiveValue)) { return new ErrorValue(irContext, new ExpressionError() @@ -1054,6 +1059,9 @@ public static async ValueTask SortByColumnsOrderTable(EvalVisitor return int.MaxValue; } + // Normalize numeric types to match the normalization applied to orderTableValues. + primitiveValue = NormalizeNumericPrimitive(primitiveValue); + var indexOnTable = orderTableValues.IndexOf(primitiveValue); if (indexOnTable < 0) { @@ -1071,6 +1079,18 @@ public static async ValueTask SortByColumnsOrderTable(EvalVisitor return new InMemoryTableValue(irContext, orderedRows); } + // Normalizes numeric primitive values so that Number (double) and Decimal values + // can be compared interchangeably. Both are represented as double for lookup purposes. + private static object NormalizeNumericPrimitive(object value) + { + if (value is decimal d) + { + return (double)d; + } + + return value; + } + private class FormulaValueComparer : IComparer { private EvalVisitor Runner { get; } diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled.txt b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled.txt index 36cd7229dc..85cb2ab0fd 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled.txt +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled.txt @@ -415,7 +415,16 @@ Error({Kind:ErrorKind.Numeric}) { name: "Team Meeting", Day: "Monday" }, { name: "Scrum Meeting 2", Day: "Tuesday" }), "Day", ["Monday","Tuesday","Wednesday",Left("Hello", -1),"Friday"]) Error({Kind:ErrorKind.InvalidArgument}) - + +// Mixing Float (Number) column values with Decimal order table - issue #2607 +// Sort Float column with string literal column name and decimal order table +>> SortByColumns([{A:Float(1)},{A:Float(3)},{A:Float(2)}], "A", [3,2,1]) +Table({A:3},{A:2},{A:1}) + +// Sort Float column with dynamic string column name and decimal order table +>> SortByColumns([{A:Float(1)},{A:Float(3)},{A:Float(2)}], If(true,"A"), [3,2,1]) +Table({A:3},{A:2},{A:1}) + // Sort calendar, with error in a column of the source referenced by the order values >> SortByColumns(Table( { name: "Project Meeting 3", Day: "Friday" }, diff --git a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled_V1CompatEnabled.txt b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled_V1CompatEnabled.txt index e00b5ac5bb..138f9d0b3b 100644 --- a/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled_V1CompatEnabled.txt +++ b/src/tests/Microsoft.PowerFx.Core.Tests.Shared/ExpressionTestCases/SortByColumnsOrderTable_ColumnNamesAsIdentifiersEnabled_V1CompatEnabled.txt @@ -495,3 +495,8 @@ Error({Kind:ErrorKind.InvalidArgument}) { name: "Team Meeting", Day: "Monday" }, { name: "Scrum Meeting 2", Day: "Tuesday" }),Day,["Monday","Tuesday", Blank(), "Monday"]) Error({Kind:ErrorKind.InvalidArgument}) + +// Mixing Float (Number) column values with Decimal order table - issue #2607 +// Sort Float column with identifier column name and decimal order table (V1 + SCNAI mode) +>> SortByColumns([{A:Float(1)},{A:Float(3)},{A:Float(2)}], A, [3,2,1]) +Table({A:3},{A:2},{A:1})