From 7fede262e8ee3bf24e3009b51d6f3fb03f578eb4 Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Tue, 22 Sep 2026 01:41:40 +0000 Subject: [PATCH 1/4] Fix precision of rescaled zero decimal parameters Normalize zero precision without restoring the CLR decimal round-trip, preserving large-decimal support. Add unit, sync/async integration coverage, and a precision documentation example for #4715. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- doc/samples/SqlParameter_Precision.cs | 17 +++ .../Microsoft.Data.SqlClient/SqlParameter.xml | 4 + .../src/Microsoft/Data/SqlClient/TdsParser.cs | 6 + .../SQL/ParameterTest/ParametersTest.cs | 109 +++++++++++++++++- .../tests/UnitTests/TdsParserDecimalTests.cs | 92 +++++++++++++++ 5 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs diff --git a/doc/samples/SqlParameter_Precision.cs b/doc/samples/SqlParameter_Precision.cs index 35f068f878..99fde06c38 100644 --- a/doc/samples/SqlParameter_Precision.cs +++ b/doc/samples/SqlParameter_Precision.cs @@ -18,3 +18,20 @@ private static void AddSqlParameter(SqlCommand command) } } // + +// +class ZeroParameterExample +{ + private static void AddZeroSqlParameter(SqlCommand command) + { + SqlParameter parameter = new SqlParameter("@Factor", SqlDbType.Decimal) + { + Value = 0m, + Precision = 3, + Scale = 3 + }; + + command.Parameters.Add(parameter); + } +} +// diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml index 6559612464..5c071de596 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml @@ -597,6 +597,10 @@ The following example creates a and [!code-csharp[SqlParameter_Precision Example#1](~/../sqlclient/doc/samples/SqlParameter_Precision.cs#1)] +Zero is a valid value when precision equals scale. For example, the following parameter represents zero for a `decimal(3,3)` column. + +[!code-csharp[SqlParameter_Precision Example#2](~/../sqlclient/doc/samples/SqlParameter_Precision.cs#2)] + ]]> diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs index 7c079cfee0..99161ca827 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -8279,6 +8279,12 @@ internal static SqlDecimal AdjustDecimalScale(decimal value, int newScale) { bool round = !LocalAppContextSwitches.TruncateScaledDecimal; num = SqlDecimal.AdjustScale(num, newScale - oldScale, round); + + if (value == decimal.Zero) + { + // AdjustScale preserves the integer digit of scale-zero zero, but decimal(p,p) needs no integer digits. + num = SqlDecimal.ConvertToPrecScale(num, Math.Max(1, newScale), newScale); + } } return num; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 1bb9b23824..4b5036934c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -532,11 +532,108 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr Assert.True(ValidateInsertedValues(connection, decimalTable.Name, truncateScaledDecimal), $"Invalid test happened with connection string [{connection.ConnectionString}]"); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public static void TestOutOfRangeDecimalParameter_CommandSelect() + /// + /// Zero values with different CLR representations must round-trip into decimal(p,p) columns. + /// + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTCPConnStringSetup))] + [InlineData(1, false)] + [InlineData(1, true)] + [InlineData(2, false)] + [InlineData(2, true)] + [InlineData(3, false)] + [InlineData(3, true)] + [InlineData(28, false)] + [InlineData(28, true)] + [InlineData(29, false)] + [InlineData(29, true)] + [InlineData(38, false)] + [InlineData(38, true)] + public static async Task ZeroDecimalParameter_CommandInsert(byte scale, bool useAsync) { using SqlConnection connection = new(DataTestUtility.TCPConnectionString); - connection.Open(); + if (useAsync) + { + await connection.OpenAsync(); + } + else + { + connection.Open(); + } + + using Table table = new(connection, "ZeroDecimalParameter", $"([Value] decimal({scale},{scale}))"); + using SqlCommand command = new( + $"INSERT INTO {table.Name} ([Value]) OUTPUT INSERTED.[Value] VALUES (@Value)", connection); + SqlParameter parameter = command.Parameters.Add("@Value", SqlDbType.Decimal); + parameter.Precision = scale; + parameter.Scale = scale; + + foreach (decimal value in new[] { 0m, 0.0m, 0.000m, new decimal(0, 0, 0, true, 0) }) + { + parameter.Value = value; + using SqlDataReader reader = useAsync + ? await command.ExecuteReaderAsync() + : command.ExecuteReader(); + Assert.True(useAsync ? await reader.ReadAsync() : reader.Read()); + SqlDecimal actual = reader.GetSqlDecimal(0); + Assert.Equal(scale, actual.Precision); + Assert.Equal(scale, actual.Scale); + Assert.Equal(0, actual.CompareTo(new SqlDecimal(0))); + } + } + + /// + /// Correcting zero precision must not allow nonzero values that exceed the parameter precision. + /// + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTCPConnStringSetup))] + [InlineData(false)] + [InlineData(true)] + public static async Task DecimalParameter_RejectsInsufficientPrecision(bool useAsync) + { + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + if (useAsync) + { + await connection.OpenAsync(); + } + else + { + connection.Open(); + } + + using SqlCommand command = new("SELECT @Value", connection); + command.Parameters.Add(new SqlParameter("@Value", SqlDbType.Decimal) + { + Precision = 3, + Scale = 3, + Value = 1m + }); + + if (useAsync) + { + await Assert.ThrowsAsync(() => command.ExecuteNonQueryAsync()); + } + else + { + Assert.Throws(() => command.ExecuteNonQuery()); + } + } + + /// + /// Large decimal values must still round-trip after rescaling beyond the CLR decimal capacity. + /// + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTCPConnStringSetup))] + [InlineData(false)] + [InlineData(true)] + public static async Task TestOutOfRangeDecimalParameter_CommandSelect(bool useAsync) + { + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + if (useAsync) + { + await connection.OpenAsync(); + } + else + { + connection.Open(); + } using SqlCommand cmd = new("SELECT @Value", connection); // A System.Decimal value has a maximum precision of 29 digits. We specify a Precision of 38 and a Scale of 2 in order @@ -550,9 +647,11 @@ public static void TestOutOfRangeDecimalParameter_CommandSelect() cmd.Parameters.Add(p); - using SqlDataReader reader = cmd.ExecuteReader(); + using SqlDataReader reader = useAsync + ? await cmd.ExecuteReaderAsync() + : cmd.ExecuteReader(); - reader.Read(); + Assert.True(useAsync ? await reader.ReadAsync() : reader.Read()); // Read the original value back as a SqlDecimal, with matching scale and precision. SqlDecimal roundtrippedDecimal = reader.GetSqlDecimal(0); diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs new file mode 100644 index 0000000000..586ea14972 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data.SqlTypes; +using System.Globalization; +using Microsoft.Data.SqlClient.Tests.Common; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests +{ + /// + /// Verifies decimal scale adjustment preserves values and the precision required by RPC parameters. + /// + [Collection(AppContextSwitchTestCollection.Name)] + public class TdsParserDecimalTests + { + /// + /// Zero must fit when parameter precision equals scale, regardless of its CLR scale or sign. + /// + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AdjustDecimalScale_ZeroUsesMinimumPrecision(bool truncate) + { + using LocalAppContextSwitchesHelper switches = new(); + switches.TruncateScaledDecimal = truncate; + + for (byte oldScale = 0; oldScale <= 28; oldScale++) + { + foreach (bool negative in new[] { false, true }) + { + decimal value = new(0, 0, 0, negative, oldScale); + for (int newScale = 0; newScale <= 38; newScale++) + { + SqlDecimal adjusted = TdsParser.AdjustDecimalScale(value, newScale); + + Assert.Equal(newScale, adjusted.Scale); + Assert.Equal(Math.Max(1, newScale), adjusted.Precision); + Assert.Equal(new int[4], adjusted.Data); + } + } + } + } + + /// + /// Scale adjustment must retain the rounding switch and precision needed for nonzero values. + /// + [Theory] + [InlineData("0.001", 3, "0.001", "0.001", 3)] + [InlineData("0.01", 3, "0.010", "0.010", 3)] + [InlineData("1", 3, "1.000", "1.000", 4)] + [InlineData("-1", 3, "-1.000", "-1.000", 4)] + [InlineData("1.005", 2, "1.01", "1.00", 3)] + [InlineData("-1.005", 2, "-1.01", "-1.00", 3)] + public void AdjustDecimalScale_NonzeroPreservesValueAndPrecision( + string value, int scale, string rounded, string truncated, int precision) + { + using LocalAppContextSwitchesHelper switches = new(); + foreach (bool truncate in new[] { false, true }) + { + switches.TruncateScaledDecimal = truncate; + SqlDecimal adjusted = TdsParser.AdjustDecimalScale( + decimal.Parse(value, CultureInfo.InvariantCulture), scale); + + Assert.Equal(truncate ? truncated : rounded, adjusted.ToString()); + Assert.Equal(scale, adjusted.Scale); + Assert.Equal(precision, adjusted.Precision); + } + } + + /// + /// Rescaling the CLR decimal limits must not reintroduce the overflow fixed by PR #4443. + /// + [Theory] + [InlineData(2)] + [InlineData(9)] + public void AdjustDecimalScale_LargeValuesRemainSqlDecimal(int scale) + { + foreach (decimal value in new[] { decimal.MinValue, decimal.MaxValue }) + { + SqlDecimal adjusted = TdsParser.AdjustDecimalScale(value, scale); + + Assert.Equal(value.ToString(CultureInfo.InvariantCulture) + "." + new string('0', scale), adjusted.ToString()); + Assert.Equal(scale, adjusted.Scale); + Assert.Equal(29 + scale, adjusted.Precision); + Assert.Throws(() => adjusted.Value); + } + } + } +} From b737a4dbdf0c3b527c8ab24f37f17188682fc238 Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Tue, 22 Sep 2026 05:22:04 +0000 Subject: [PATCH 2/4] Reduce redundant async decimal integration cases Keep a representative async zero round-trip and async exception propagation while testing scale boundaries synchronously, addressing PR review feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/ManualTests/SQL/ParameterTest/ParametersTest.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 4b5036934c..62c34dc959 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -534,20 +534,16 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr /// /// Zero values with different CLR representations must round-trip into decimal(p,p) columns. + /// One async case covers command execution parity; scale boundaries use the shared synchronous conversion. /// [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTCPConnStringSetup))] [InlineData(1, false)] - [InlineData(1, true)] [InlineData(2, false)] - [InlineData(2, true)] [InlineData(3, false)] [InlineData(3, true)] [InlineData(28, false)] - [InlineData(28, true)] [InlineData(29, false)] - [InlineData(29, true)] [InlineData(38, false)] - [InlineData(38, true)] public static async Task ZeroDecimalParameter_CommandInsert(byte scale, bool useAsync) { using SqlConnection connection = new(DataTestUtility.TCPConnectionString); @@ -583,6 +579,7 @@ public static async Task ZeroDecimalParameter_CommandInsert(byte scale, bool use /// /// Correcting zero precision must not allow nonzero values that exceed the parameter precision. + /// Both command APIs must surface the same exception, including when the async result is awaited. /// [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTCPConnStringSetup))] [InlineData(false)] From 39242a442c391f2411637df5a84a78894e9c705c Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Tue, 22 Sep 2026 06:06:35 +0000 Subject: [PATCH 3/4] Handle zero in shared decimal RPC precision validation Address review feedback by covering both decimal and SqlDecimal input paths after scale adjustment. Retain nonzero overflow checks and large-decimal support, with RPC-level and SQL integration coverage for signed and scaled zero values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 11 +-- .../SQL/ParameterTest/ParametersTest.cs | 37 ++++---- .../tests/UnitTests/TdsParserDecimalTests.cs | 88 ++++++++++++++++--- 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs index 99161ca827..92e40cc292 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -8279,12 +8279,6 @@ internal static SqlDecimal AdjustDecimalScale(decimal value, int newScale) { bool round = !LocalAppContextSwitches.TruncateScaledDecimal; num = SqlDecimal.AdjustScale(num, newScale - oldScale, round); - - if (value == decimal.Zero) - { - // AdjustScale preserves the integer digit of scale-zero zero, but decimal(p,p) needs no integer digits. - num = SqlDecimal.ConvertToPrecScale(num, Math.Max(1, newScale), newScale); - } } return num; @@ -10439,7 +10433,10 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet // If Precision is specified, verify value precision vs param precision if (precision != 0) { - if (precision < adjustedValue.Precision) + // Precision metadata can overstate zero's required digits. + // Compare magnitudes to recognize negative zero as well. + if (precision < adjustedValue.Precision && + (SqlDecimal.Abs(adjustedValue) != new SqlDecimal(0)).IsTrue) { throw ADP.ParameterValueOutOfRange(adjustedValue); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 62c34dc959..77b58ce09b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -533,7 +533,7 @@ public static void TestScaledDecimalParameter_CommandInsert(string connectionStr } /// - /// Zero values with different CLR representations must round-trip into decimal(p,p) columns. + /// CLR and SQL zero values with different representations must round-trip into decimal(p,p) columns. /// One async case covers command execution parity; scale boundaries use the shared synchronous conversion. /// [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsTCPConnStringSetup))] @@ -563,7 +563,14 @@ public static async Task ZeroDecimalParameter_CommandInsert(byte scale, bool use parameter.Precision = scale; parameter.Scale = scale; - foreach (decimal value in new[] { 0m, 0.0m, 0.000m, new decimal(0, 0, 0, true, 0) }) + foreach (object value in new object[] + { + 0m, 0.0m, 0.000m, new decimal(0, 0, 0, true, 0), + new SqlDecimal(0m), new SqlDecimal(0.000m), + new SqlDecimal(new decimal(0, 0, 0, true, 0)), + new SqlDecimal(38, scale, true, 0, 0, 0, 0), + new SqlDecimal(38, scale, false, 0, 0, 0, 0) + }) { parameter.Value = value; using SqlDataReader reader = useAsync @@ -597,20 +604,20 @@ public static async Task DecimalParameter_RejectsInsufficientPrecision(bool useA } using SqlCommand command = new("SELECT @Value", connection); - command.Parameters.Add(new SqlParameter("@Value", SqlDbType.Decimal) - { - Precision = 3, - Scale = 3, - Value = 1m - }); - - if (useAsync) - { - await Assert.ThrowsAsync(() => command.ExecuteNonQueryAsync()); - } - else + SqlParameter parameter = command.Parameters.Add("@Value", SqlDbType.Decimal); + parameter.Precision = 3; + parameter.Scale = 3; + foreach (object value in new object[] { 1m, -1m, new SqlDecimal(1m), new SqlDecimal(-1m) }) { - Assert.Throws(() => command.ExecuteNonQuery()); + parameter.Value = value; + if (useAsync) + { + await Assert.ThrowsAsync(() => command.ExecuteNonQueryAsync()); + } + else + { + Assert.Throws(() => command.ExecuteNonQuery()); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs index 586ea14972..57a4f2cfd8 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/TdsParserDecimalTests.cs @@ -3,26 +3,30 @@ // See the LICENSE file in the project root for more information. using System; +using System.Data; using System.Data.SqlTypes; using System.Globalization; +using System.Reflection; using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.UnitTests { /// - /// Verifies decimal scale adjustment preserves values and the precision required by RPC parameters. + /// Verifies decimal scale adjustment and RPC precision validation preserve representable values. /// [Collection(AppContextSwitchTestCollection.Name)] public class TdsParserDecimalTests { /// - /// Zero must fit when parameter precision equals scale, regardless of its CLR scale or sign. + /// CLR and SQL zero must serialize when precision equals scale, regardless of input scale or sign. /// [Theory] - [InlineData(false)] - [InlineData(true)] - public void AdjustDecimalScale_ZeroUsesMinimumPrecision(bool truncate) + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void RpcDecimalParameter_ZeroFitsPrecisionEqualToScale(bool isSqlDecimal, bool truncate) { using LocalAppContextSwitchesHelper switches = new(); switches.TruncateScaledDecimal = truncate; @@ -31,19 +35,52 @@ public void AdjustDecimalScale_ZeroUsesMinimumPrecision(bool truncate) { foreach (bool negative in new[] { false, true }) { - decimal value = new(0, 0, 0, negative, oldScale); - for (int newScale = 0; newScale <= 38; newScale++) + decimal zero = new(0, 0, 0, negative, oldScale); + object value = isSqlDecimal ? (object)new SqlDecimal(zero) : zero; + for (byte scale = 1; scale <= 38; scale++) { - SqlDecimal adjusted = TdsParser.AdjustDecimalScale(value, newScale); - - Assert.Equal(newScale, adjusted.Scale); - Assert.Equal(Math.Max(1, newScale), adjusted.Precision); - Assert.Equal(new int[4], adjusted.Data); + WriteDecimalParameter(value, scale, scale); } } } } + /// + /// Excess SqlDecimal precision must not reject signed zero or a value that rounds/truncates to zero. + /// + [Theory] + [InlineData(false)] + [InlineData(true)] + public void RpcDecimalParameter_ZeroWithExcessPrecisionFits(bool negative) + { + using LocalAppContextSwitchesHelper switches = new(); + foreach (bool truncate in new[] { false, true }) + { + switches.TruncateScaledDecimal = truncate; + WriteDecimalParameter(new SqlDecimal(38, 3, !negative, 0, 0, 0, 0), 3, 3); + WriteDecimalParameter(SqlDecimal.ConvertToPrecScale( + new SqlDecimal(new decimal(0, 0, 0, negative, 3)), 38, 3), 3, 3); + WriteDecimalParameter(new SqlDecimal(38, 4, !negative, 1, 0, 0, 0), 3, 3); + } + } + + /// + /// Exempting zero must not permit nonzero CLR or SQL values with insufficient parameter precision. + /// + [Theory] + [InlineData(false)] + [InlineData(true)] + public void RpcDecimalParameter_NonzeroExceedingPrecisionThrows(bool isSqlDecimal) + { + foreach (decimal value in new[] { -1m, 1m }) + { + object parameterValue = isSqlDecimal ? (object)new SqlDecimal(value) : value; + TargetInvocationException exception = Assert.Throws( + () => WriteDecimalParameter(parameterValue, 3, 3)); + Assert.IsType(exception.InnerException); + } + } + /// /// Scale adjustment must retain the rounding switch and precision needed for nonzero values. /// @@ -88,5 +125,32 @@ public void AdjustDecimalScale_LargeValuesRemainSqlDecimal(int scale) Assert.Throws(() => adjusted.Value); } } + + /// + /// Serializes one RPC parameter into a fresh parser buffer without connecting to a server. + /// + /// The CLR decimal or SqlDecimal parameter value. + /// The declared parameter precision. + /// The declared parameter scale. + private static void WriteDecimalParameter(object value, byte precision, byte scale) + { + const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic; + TdsParser parser = new(false, false); + object state = typeof(TdsParser).GetField("_physicalStateObj", Flags)!.GetValue(parser)!; + SqlParameter parameter = new("@Value", SqlDbType.Decimal) + { + Value = value, + Precision = precision, + Scale = scale + }; + parameter.Validate(0, false); + + using SqlCommand command = new(); + MethodInfo write = typeof(TdsParser).GetMethod("TDSExecuteRPCAddParameter", Flags)!; + Assert.Null(write.Invoke(parser, new object[] + { + state, parameter, parameter.InternalMetaType, (byte)0, command, false + })); + } } } From c0f34ab0b25baf5553776982eb61ae4f6384b304 Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Tue, 22 Sep 2026 18:47:21 +0000 Subject: [PATCH 4/4] Remove decimal zero documentation additions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- doc/samples/SqlParameter_Precision.cs | 17 ----------------- .../Microsoft.Data.SqlClient/SqlParameter.xml | 4 ---- 2 files changed, 21 deletions(-) diff --git a/doc/samples/SqlParameter_Precision.cs b/doc/samples/SqlParameter_Precision.cs index 99fde06c38..35f068f878 100644 --- a/doc/samples/SqlParameter_Precision.cs +++ b/doc/samples/SqlParameter_Precision.cs @@ -18,20 +18,3 @@ private static void AddSqlParameter(SqlCommand command) } } // - -// -class ZeroParameterExample -{ - private static void AddZeroSqlParameter(SqlCommand command) - { - SqlParameter parameter = new SqlParameter("@Factor", SqlDbType.Decimal) - { - Value = 0m, - Precision = 3, - Scale = 3 - }; - - command.Parameters.Add(parameter); - } -} -// diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml index 5c071de596..6559612464 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlParameter.xml @@ -597,10 +597,6 @@ The following example creates a and [!code-csharp[SqlParameter_Precision Example#1](~/../sqlclient/doc/samples/SqlParameter_Precision.cs#1)] -Zero is a valid value when precision equals scale. For example, the following parameter represents zero for a `decimal(3,3)` column. - -[!code-csharp[SqlParameter_Precision Example#2](~/../sqlclient/doc/samples/SqlParameter_Precision.cs#2)] - ]]>