Fix | Enable transport of large decimal values with explicit Precision and Scale#4443
Open
edwardneal wants to merge 1 commit into
Open
Fix | Enable transport of large decimal values with explicit Precision and Scale#4443edwardneal wants to merge 1 commit into
edwardneal wants to merge 1 commit into
Conversation
This is necessary because a normal System.Decimal value is limited to a precision of 29, while SQL Server's numeric type has a maximum precision of 38.
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Member
|
/azp run |
|
Azure Pipelines: Successfully started running 3 pipeline(s). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
#1655 indicates that it's impossible to send
decimal.MaxValue(or any other largedecimalvalue) as a parameter to SQL Server when that parameter'sPrecisionandScaleare set. This is a challenge for Always Encrypted scenarios, which requires these to always be explicitly set.This issue was originally marked as external to SqlClient, but upon review I don't believe this is the case. The root cause is that the .NET
decimaltype supports a precision of up to 29 significant digits while the SQL Servernumerictype's precision can range up to 38. Any conversion fromSqlDecimal(which aligns with SQL Server) back todecimalafter having been rescaled will risk overflowing - while the value itself might be within the bounds ofdecimal, the precision might not be. TheOverflowExceptionis therefore indicative of a correctness problem within SqlClient, not .NET.This conversion from
SqlDecimaltodecimaloccurs inTdsParser.AdjustDecimalScale. When taken in the context of its call site in (TDSExecuteRPCAddParameter) we see that to adjust the scale of adecimalSqlClient:AdjustDecimalScaledecimalvalueSqlDecimalinstance.SqlDecimal.AdjustScale, accounting for the AppContext switch.SqlDecimal.Valueto converts the scaled value back to adecimal. This is the location of theOverflowException.SqlDecimalwith the return value of the above methodSqlDecimalinstance's precision is >= the desired precision.Once we've rescaled a
decimalvalue, it should never perform a blind round-trip fromSqlDecimaltodecimalwithout checks to ensure that it'll fit in the CLR type. This PR removes this round-trip. We now returnSqlDecimaldirectly fromAdjustDecimalScale, then force the rest ofTDSExecuteRPCAddParameterto treat the value as a SQL type. This has the pleasant side-effect of being able to merge some of the verification logic.Issues
Fixes #1655.
Upstream EF Core issues: dotnet/efcore#28240; dotnet/efcore#33109; dotnet/efcore#35921.
Testing
One new test added which exercises the code path. One Always Encrypted test modified to verify that there have been no regressions.