From 9824a9c3580e79e95a7bed5af12bcdb15ffbe4cc Mon Sep 17 00:00:00 2001 From: Amanda Tarafa Mas Date: Thu, 18 Apr 2024 00:40:12 -0700 Subject: [PATCH] feat!: Change default mappings for CLR type decimal BREAKING CHANGE: The default mapping for values of CLR type decimal was FLOAT64 and it is now Numeric. --- .../KeysTests.cs | 82 +++++++++---------- .../SpannerCommandTests.cs | 2 +- .../SpannerConversionOptionsTests.cs | 2 +- .../SpannerParameterTests.cs | 5 +- .../SpannerConversionOptions.cs | 2 +- .../SpannerDbType.ValueConversion.cs | 5 -- .../SpannerDbType.cs | 4 +- 7 files changed, 49 insertions(+), 53 deletions(-) diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs index a8769d5313a0..852e6813d9e4 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs @@ -132,17 +132,17 @@ public void TypeMappings() var key = new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k1", Value = 3.14m }, new SpannerParameter { ParameterName = "k2", Value = new DateTime(2022, 06, 08) } }); - Assert.Equal(new ListValue { Values = { Value.ForNumber(3.14), Value.ForString("2022-06-08T00:00:00Z") } }, + Assert.Equal(new ListValue { Values = { Value.ForString("3.14"), Value.ForString("2022-06-08T00:00:00Z") } }, key.ToProtobuf(builder.ConversionOptions)); // Specify the type mappings. - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; - Assert.Equal(new ListValue { Values = { Value.ForString("3.14"), Value.ForString("2022-06-08") } }, + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; + Assert.Equal(new ListValue { Values = { Value.ForNumber(3.14), Value.ForString("2022-06-08") } }, key.ToProtobuf(builder.ConversionOptions)); // Revert the type mappings. builder.ClrToSpannerTypeDefaultMappings = ""; - Assert.Equal(new ListValue { Values = { Value.ForNumber(3.14), Value.ForString("2022-06-08T00:00:00Z") } }, + Assert.Equal(new ListValue { Values = { Value.ForString("3.14"), Value.ForString("2022-06-08T00:00:00Z") } }, key.ToProtobuf(builder.ConversionOptions)); } @@ -152,15 +152,15 @@ public void ToString_() var builder = new SpannerConnectionStringBuilder(); var key = new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k1", Value = 3.14m }, new SpannerParameter { ParameterName = "k2", Value = new DateTime(2022, 06, 08) } }); - Assert.Equal("[ 3.14, \"2022-06-08T00:00:00Z\" ]", key.ToString(builder)); + Assert.Equal("[ \"3.14\", \"2022-06-08T00:00:00Z\" ]", key.ToString(builder)); // Specify the type mappings. - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; - Assert.Equal("[ \"3.14\", \"2022-06-08\" ]", key.ToString(builder)); + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; + Assert.Equal("[ 3.14, \"2022-06-08\" ]", key.ToString(builder)); // Revert the type mappings. builder.ClrToSpannerTypeDefaultMappings = ""; - Assert.Equal("[ 3.14, \"2022-06-08T00:00:00Z\" ]", key.ToString(builder)); + Assert.Equal("[ \"3.14\", \"2022-06-08T00:00:00Z\" ]", key.ToString(builder)); } } @@ -264,8 +264,8 @@ public void TypeMappings() Assert.Equal(new V1.KeyRange { - StartClosed = new ListValue { Values = { new[] { new Value { NumberValue = 3.14 } } } }, - EndOpen = new ListValue { Values = { new[] { new Value { NumberValue = 1.71 } } } }, + StartClosed = new ListValue { Values = { new[] { new Value { StringValue = "3.14" } } } }, + EndOpen = new ListValue { Values = { new[] { new Value { StringValue = "1.71" } } } }, }, range1.ToProtobuf(builder.ConversionOptions)); Assert.Equal(new V1.KeyRange @@ -275,11 +275,11 @@ public void TypeMappings() }, range2.ToProtobuf(builder.ConversionOptions)); // Specifying the type mappings. - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; Assert.Equal(new V1.KeyRange { - StartClosed = new ListValue { Values = { new[] { new Value { StringValue = "3.14" } } } }, - EndOpen = new ListValue { Values = { new[] { new Value { StringValue = "1.71" } } } }, + StartClosed = new ListValue { Values = { new[] { new Value { NumberValue = 3.14 } } } }, + EndOpen = new ListValue { Values = { new[] { new Value { NumberValue = 1.71 } } } }, }, range1.ToProtobuf(builder.ConversionOptions)); Assert.Equal(new V1.KeyRange @@ -292,8 +292,8 @@ public void TypeMappings() builder.ClrToSpannerTypeDefaultMappings = ""; Assert.Equal(new V1.KeyRange { - StartClosed = new ListValue { Values = { new[] { new Value { NumberValue = 3.14 } } } }, - EndOpen = new ListValue { Values = { new[] { new Value { NumberValue = 1.71 } } } }, + StartClosed = new ListValue { Values = { new[] { new Value { StringValue = "3.14" } } } }, + EndOpen = new ListValue { Values = { new[] { new Value { StringValue = "1.71" } } } }, }, range1.ToProtobuf(builder.ConversionOptions)); Assert.Equal(new V1.KeyRange @@ -312,17 +312,17 @@ public void ToString_() var range2 = KeyRange.ClosedClosed(new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k3", Value = new DateTime(2022, 06, 08) } }), new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k4", Value = new DateTime(2024, 06, 08) } })); - Assert.Equal("[[ 3.14 ], [ 1.71 ])", range1.ToString(builder)); + Assert.Equal("[[ \"3.14\" ], [ \"1.71\" ])", range1.ToString(builder)); Assert.Equal("[[ \"2022-06-08T00:00:00Z\" ], [ \"2024-06-08T00:00:00Z\" ]]", range2.ToString(builder)); - // Specify the type mappings.(Numeric for decimal, Date for DateTime) - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; - Assert.Equal("[[ \"3.14\" ], [ \"1.71\" ])", range1.ToString(builder)); + // Specify the type mappings. (double for decimal, Date for DateTime) + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; + Assert.Equal("[[ 3.14 ], [ 1.71 ])", range1.ToString(builder)); Assert.Equal("[[ \"2022-06-08\" ], [ \"2024-06-08\" ]]", range2.ToString(builder)); - // Revert to default. (double for decimal, Timestamp for DateTime) + // Revert to default. (Numeric for decimal, Timestamp for DateTime) builder.ClrToSpannerTypeDefaultMappings = ""; - Assert.Equal("[[ 3.14 ], [ 1.71 ])", range1.ToString(builder)); + Assert.Equal("[[ \"3.14\" ], [ \"1.71\" ])", range1.ToString(builder)); Assert.Equal("[[ \"2022-06-08T00:00:00Z\" ], [ \"2024-06-08T00:00:00Z\" ]]", range2.ToString(builder)); } } @@ -358,19 +358,19 @@ public void TypeMappings() new SpannerParameter { ParameterName = "k2", Value = new DateTime(2022, 06, 08) } }); var protobufValues = keySet.ToProtobuf(builder.ConversionOptions); - Assert.Equal(3.14d, protobufValues.Keys[0].Values[0].NumberValue); // default to double. + Assert.Equal("3.14", protobufValues.Keys[0].Values[0].StringValue); // default to Numeric. Assert.Equal("2022-06-08T00:00:00Z", protobufValues.Keys[0].Values[1].StringValue); // default to Timestamp. // Specifying the type mappings. - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; protobufValues = keySet.ToProtobuf(builder.ConversionOptions); - Assert.Equal("3.14", protobufValues.Keys[0].Values[0].StringValue); // Numeric string. + Assert.Equal(3.14d, protobufValues.Keys[0].Values[0].NumberValue); // double value. Assert.Equal("2022-06-08", protobufValues.Keys[0].Values[1].StringValue); // Date string. // Revert the type mappings. builder.ClrToSpannerTypeDefaultMappings = ""; protobufValues = keySet.ToProtobuf(builder.ConversionOptions); - Assert.Equal(3.14d, protobufValues.Keys[0].Values[0].NumberValue); // default to double. + Assert.Equal("3.14", protobufValues.Keys[0].Values[0].StringValue); // default to Numeric. Assert.Equal("2022-06-08T00:00:00Z", protobufValues.Keys[0].Values[1].StringValue); // default to Timestamp. } @@ -394,17 +394,17 @@ public void ToStringKeySetWithBuilder() new SpannerParameter { ParameterName = "k2", Value = new DateTime(2022, 06, 08) } }); Assert.Collection(keySet.Keys, - key1 => Assert.Equal("[ 3.14, \"2022-06-08T00:00:00Z\" ]", key1.ToString(builder))); + key1 => Assert.Equal("[ \"3.14\", \"2022-06-08T00:00:00Z\" ]", key1.ToString(builder))); // Specify the type mappings. - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; Assert.Collection(keySet.Keys, - key1 => Assert.Equal("[ \"3.14\", \"2022-06-08\" ]", key1.ToString(builder))); + key1 => Assert.Equal("[ 3.14, \"2022-06-08\" ]", key1.ToString(builder))); // Revert the type mappings. builder.ClrToSpannerTypeDefaultMappings = ""; Assert.Collection(keySet.Keys, - key1 => Assert.Equal("[ 3.14, \"2022-06-08T00:00:00Z\" ]", key1.ToString(builder))); + key1 => Assert.Equal("[ \"3.14\", \"2022-06-08T00:00:00Z\" ]", key1.ToString(builder))); } [Fact] @@ -427,15 +427,15 @@ public void ToStringKeySetFromKeysWithBuilder() var keys = KeySet.FromKeys(new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k1", Value = 3.14m } }), new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k2", Value = new DateTime(2022, 06, 08) } })); - Assert.Equal("KeySet {Keys = [[ 3.14 ], [ \"2022-06-08T00:00:00Z\" ]]}", keys.ToString(builder)); + Assert.Equal("KeySet {Keys = [[ \"3.14\" ], [ \"2022-06-08T00:00:00Z\" ]]}", keys.ToString(builder)); - // Specify the type mappings.(Numeric for decimal, Date for DateTime) - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; - Assert.Equal("KeySet {Keys = [[ \"3.14\" ], [ \"2022-06-08\" ]]}", keys.ToString(builder)); + // Specify the type mappings. (double for decimal, Date for DateTime) + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; + Assert.Equal("KeySet {Keys = [[ 3.14 ], [ \"2022-06-08\" ]]}", keys.ToString(builder)); - // Revert to default. (double for decimal, Timestamp for DateTime) + // Revert to default. (Numeric for decimal, Timestamp for DateTime) builder.ClrToSpannerTypeDefaultMappings = ""; - Assert.Equal("KeySet {Keys = [[ 3.14 ], [ \"2022-06-08T00:00:00Z\" ]]}", keys.ToString(builder)); + Assert.Equal("KeySet {Keys = [[ \"3.14\" ], [ \"2022-06-08T00:00:00Z\" ]]}", keys.ToString(builder)); } [Fact] @@ -456,15 +456,15 @@ public void ToStringKeySetFromRangesWithBuilder() KeyRange.ClosedClosed(new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k3", Value = new DateTime(2022, 06, 08) } }), new Key(new SpannerParameterCollection { new SpannerParameter { ParameterName = "k4", Value = new DateTime(2024, 06, 08) } }))); - Assert.Equal("KeySet {Ranges = [[[ 3.14 ], [ 1.71 ]), [[ \"2022-06-08T00:00:00Z\" ], [ \"2024-06-08T00:00:00Z\" ]]]}", ranges.ToString(builder)); + Assert.Equal("KeySet {Ranges = [[[ \"3.14\" ], [ \"1.71\" ]), [[ \"2022-06-08T00:00:00Z\" ], [ \"2024-06-08T00:00:00Z\" ]]]}", ranges.ToString(builder)); - // Specify the type mappings.(Numeric for decimal, Date for DateTime) - builder.ClrToSpannerTypeDefaultMappings = "DecimalToNumeric,DateTimeToDate"; - Assert.Equal("KeySet {Ranges = [[[ \"3.14\" ], [ \"1.71\" ]), [[ \"2022-06-08\" ], [ \"2024-06-08\" ]]]}", ranges.ToString(builder)); + // Specify the type mappings. (double for decimal, Date for DateTime) + builder.ClrToSpannerTypeDefaultMappings = "DecimalToFloat64,DateTimeToDate"; + Assert.Equal("KeySet {Ranges = [[[ 3.14 ], [ 1.71 ]), [[ \"2022-06-08\" ], [ \"2024-06-08\" ]]]}", ranges.ToString(builder)); - // Revert to default. (double for decimal, Timestamp for DateTime) + // Revert to default. (Numeric for decimal, Timestamp for DateTime) builder.ClrToSpannerTypeDefaultMappings = ""; - Assert.Equal("KeySet {Ranges = [[[ 3.14 ], [ 1.71 ]), [[ \"2022-06-08T00:00:00Z\" ], [ \"2024-06-08T00:00:00Z\" ]]]}", ranges.ToString(builder)); + Assert.Equal("KeySet {Ranges = [[[ \"3.14\" ], [ \"1.71\" ]), [[ \"2022-06-08T00:00:00Z\" ], [ \"2024-06-08T00:00:00Z\" ]]]}", ranges.ToString(builder)); } } } diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerCommandTests.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerCommandTests.cs index 30d402968709..a958587ea8c3 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerCommandTests.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerCommandTests.cs @@ -1368,7 +1368,7 @@ public static IEnumerable ConfiguredSpannerDbTypes() { // Format : ClrToSpannerTypeDefaultMappings value, Parameter value, expected SpannerDbType. // Decimal mappings. - yield return new object[] { default, 3.14m, SpannerDbType.Float64 }; + yield return new object[] { default, 3.14m, SpannerDbType.Numeric }; yield return new object[] { DecimalToFloat64, 3.14m, SpannerDbType.Float64 }; yield return new object[] { DecimalToNumeric, 3.14m, SpannerDbType.Numeric }; yield return new object[] { DecimalToPgNumeric, 3.14m, SpannerDbType.PgNumeric }; diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerConversionOptionsTests.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerConversionOptionsTests.cs index 4908fda620c0..bfa23b8983ee 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerConversionOptionsTests.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerConversionOptionsTests.cs @@ -26,7 +26,7 @@ public void Defaults() Assert.True(options.UseDBNull); Assert.Equal(SpannerDbType.Float32, options.SingleToConfiguredSpannerType); - Assert.Equal(SpannerDbType.Float64, options.DecimalToConfiguredSpannerType); + Assert.Equal(SpannerDbType.Numeric, options.DecimalToConfiguredSpannerType); Assert.Equal(SpannerDbType.Timestamp, options.DateTimeToConfiguredSpannerType); Assert.Equal(typeof(DateTime), options.DateToConfiguredClrType); } diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerParameterTests.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerParameterTests.cs index cde128ca48b4..37853f5c9f69 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerParameterTests.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerParameterTests.cs @@ -70,7 +70,8 @@ public static IEnumerable GetValueConversions() yield return new object[] { 2.718f, SpannerDbType.Float32, DbType.Single, typeof(float) }; yield return new object[] { 3.14d, SpannerDbType.Float64, DbType.Double, typeof(double) }; - yield return new object[] { 3.14m, SpannerDbType.Float64, DbType.Double, typeof(double) }; + + yield return new object[] { 3.14m, SpannerDbType.Numeric, DbType.VarNumeric, typeof(SpannerNumeric) }; yield return new object[] { (short)1, SpannerDbType.Int64, DbType.Int64, typeof(long) }; yield return new object[] { (ushort)1, SpannerDbType.Int64, DbType.Int64, typeof(long) }; @@ -208,7 +209,7 @@ public static IEnumerable ConfiguredSpannerDbTypes() yield return new object[] { new SpannerParameter { Value = 2.718f }, GetSpannerConversionOptions(SingleToFloat64), SpannerDbType.Float64 }; yield return new object[] { new SpannerParameter { Value = 3.14M }, - GetSpannerConversionOptions(default), SpannerDbType.Float64 }; + GetSpannerConversionOptions(default), SpannerDbType.Numeric }; yield return new object[] { new SpannerParameter { Value = 3.14M }, GetSpannerConversionOptions(DecimalToFloat64), SpannerDbType.Float64 }; yield return new object[] { new SpannerParameter { Value = 3.14M }, diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerConversionOptions.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerConversionOptions.cs index 1f363edeab04..73be2080595f 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerConversionOptions.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerConversionOptions.cs @@ -139,7 +139,7 @@ internal SpannerConversionOptions WithSpannerToClrMappings(string spannerToClrMa internal void SetClrToSpannerTypeDefaults() { SingleToConfiguredSpannerType = SpannerDbType.Float32; - DecimalToConfiguredSpannerType = SpannerDbType.Float64; + DecimalToConfiguredSpannerType = SpannerDbType.Numeric; DateTimeToConfiguredSpannerType = SpannerDbType.Timestamp; } diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs index e93fd3686222..a24e757b3ac4 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs @@ -251,11 +251,6 @@ internal Value ToProtobufValue(object value) } if (value is float || value is double || value is decimal) { - // TODO: Check with Jon if LossOfPrecisionHandling needs to be changed. - // We throw if there's a loss of precision. We could use - // LossOfPrecisionHandling.Truncate but GoogleSQL documentation requests to - // use half-away-from-zero rounding but the SpannerNumeric implementation - // truncates instead. return Value.ForString(SpannerNumeric.FromDecimal( Convert.ToDecimal(value, InvariantCulture), LossOfPrecisionHandling.Truncate).ToString()); } diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs index 60c8ed40da45..d00366e3bd80 100644 --- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs +++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs @@ -352,7 +352,7 @@ public static SpannerDbType FromClrType(System.Type type) { return Float32; } - if (type == typeof(double) || type == typeof(decimal)) + if (type == typeof(double)) { return Float64; } @@ -361,7 +361,7 @@ public static SpannerDbType FromClrType(System.Type type) { return Int64; } - if (type == typeof(SpannerNumeric)) + if (type == typeof(SpannerNumeric) || type == typeof(decimal)) { return Numeric; }