-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Translate COALESCE as ISNULL #34171
base: main
Are you sure you want to change the base?
Translate COALESCE as ISNULL #34171
Conversation
At least a test for the type propagation is needed |
Another application for this is that COALESCE cannot be used for Indexed Views in SQL Server, however ISNULL is supported in case of aggregations. This makes it difficult/impossible to make EF Core generated queries use the indexed views as I suppose the query processor thinks they're different. For example:
|
@ranma42, gentle nudge on this. We like the |
@cincuranet I will rebase the PR and check the new test results ASAP (worst case: next weekend) Last time I kind of got stuck because of some typing issues as mentioned in #34171 (comment) I will try to point out explicitly the issues that must be solved (ideally as questions) to unblock this |
public virtual Task Coalesce_Correct_Type(bool async) | ||
=> AssertQuery( | ||
async, | ||
ss => ss.Set<Customer>().Select(c => c.Region ?? "no region specified")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is designed to ensure that EFCore takes care of the difference in type propagation between ISNULL
and COALESCE
.
From the docs:
Data type determination of the resulting expression is different. ISNULL uses the data type of the first parameter, COALESCE follows the CASE expression rules and returns the data type of value with the highest precedence.
Specifically, in this case c.Region
is an nvarchar(15)
, while "no region specified"
cannot be represented as a value of that type (it's longer than 15).
The implementation I did in this PR would take care of this, under the assumption that the typeMapping
for CONVERT
can be used to determine the type (and/or whether any casting/conversion is needed), but unfortunately EFCore currently computes it as nvarchar(15)
even though (I believe) its result is actually an nvarchar(18)
.
See https://dbfiddle.uk/ITGS6ZVJ
DECLARE @what sql_variant;
DECLARE @foo varchar(15) = NULL;
SELECT @what = ISNULL(@foo, 'no region selected');
SELECT
@what,
SQL_VARIANT_PROPERTY(@what, 'BaseType'),
SQL_VARIANT_PROPERTY(@what, 'MaxLength');
SELECT @what = COALESCE(@foo, 'no region selected');
SELECT
@what,
SQL_VARIANT_PROPERTY(@what, 'BaseType'),
SQL_VARIANT_PROPERTY(@what, 'MaxLength');
`COALESCE`is a syntactic shortcut for the CASE expression. As such, the input values are evaluated multiple times. `ISNULL` does not have this shortcoming. Fixes dotnet#32519.
What is the best way forward for the type issue? I see that the type inference already handles string concatenation in a special way, but it looks like nothing at all happens for other values/operations ( NB: I am not sure I actually grasp what typeMappings are supposed to represent (hence how they should be computed/used), so maybe I am simply misusing it in this PR 😇 |
COALESCE
is a syntactic shortcut for the CASE expression. As such, the input values are evaluated multiple times.ISNULL
does not have this shortcoming.Fixes #32519.