Skip to content

Fix QueryOver Coalesce projection in Select #2246

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

Merged
merged 3 commits into from
Oct 20, 2019

Conversation

bahusoid
Copy link
Member

@bahusoid bahusoid commented Oct 14, 2019

Fixes #1107

In fact simple Select(x => x.Value.Coalesce(0)) was broken in QueryOver due to using NHibernateUtil.Object as return type for coalesce function which is something completely unrelated:

Handles "any" mappings and the old deprecated "object" type.

@@ -316,7 +316,7 @@ internal static IProjection ProcessCoalesce(MethodCallExpression methodCallExpre
{
IProjection property = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[0]).AsProjection();
var replaceValueIfIsNull = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[1]);
return Projections.SqlFunction("coalesce", NHibernateUtil.Object, property, replaceValueIfIsNull.AsProjection());
return new SqlFunctionProjection("coalesce", returnTypeProjection: property, property, replaceValueIfIsNull.AsProjection());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other option is to use GuessType here but current fix looks more proper to me (though it requires changes in SqlFunctionProjection). Let me know if you think GuessType is enough here.

IType type = sqlFunction.ReturnType(returnType, criteriaQuery.Factory);
return new IType[] {type};

var resultType = returnType ?? returnTypeProjection?.GetTypes(criteria, criteriaQuery).FirstOrDefault();
Copy link
Member

@fredericDelaporte fredericDelaporte Oct 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are more than one type returned, should it be silently ignored? (Shouldn't SingleOrDefault() be called here?)

And all the null coalesce, OrDefault stuff combined with yielding an IType[] of one element which may as a result be null does not look good. Digging on the GetTypes usage, it seems it ends up being used by calling methods on the elements it contains without checking if they are null or not. (See CriteriaLoader.GetResultRow.)
So either it should yield an empty type array when no type are determined if such an empty array can be a valid return value, or throw for the sake of failing earlier.
This defect was somehow already there, since nothing was checking the sqlFunction return type was not null. But it looks worsened by the change.
The function may have a fixed return type, in which case its argument being null changes nothing, it will return a non-null return type. But otherwise it may return null if we give it null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are more than one type returned, should it be silently ignored? (Shouldn't SingleOrDefault() be called here?)

Well I don't know. I made it the way I saw somewhere else in Criteria handling. IMHO it's not important until multi-column support is added to ISQLFunction.ReturnType

And all the null coalesce, OrDefault stuff combined with yielding an IType[] of one element which may as a result be null does not look good. ... it seems it ends up being used by calling methods

Not sure I properly understand it. But this logic ends up in sqlFunction.ReturnType which expects single IType and returns single IType. And I see no restrictions why null should be forbidden for sqlFunction.ReturnType - function return type is known beforehand in most cases . So both returnType and returnTypeProjection should be nullable.

Copy link
Member

@fredericDelaporte fredericDelaporte Oct 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trouble is, GetTypes returned array is used as if its content could never be null, without null checks, see CriteriaLoader.GetResultRow. So potentially yielding new IType [] { null } looks plain wrong.
The PR introduces more possibilities for this to happen.
I think is should either yield an empty array, or throw. It should not yield an array containing null. Or is there something I have missed which would allow this returned value to be actually supported?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No but empty type collection also doesn't make much sense for using in Select projection... Ok - I've added null handling just in case.

@fredericDelaporte fredericDelaporte self-requested a review October 17, 2019 19:15
@fredericDelaporte fredericDelaporte added this to the 5.3 milestone Oct 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NH-2983 - Coalesce in projection doesn't work if there is more than 1 Coalesce
2 participants