Skip to content

Comments

CSHARP-5889: Optimize comparison with nullable constant translation#1885

Open
sanych-sun wants to merge 2 commits intomongodb:mainfrom
sanych-sun:csharp5889
Open

CSHARP-5889: Optimize comparison with nullable constant translation#1885
sanych-sun wants to merge 2 commits intomongodb:mainfrom
sanych-sun:csharp5889

Conversation

@sanych-sun
Copy link
Member

No description provided.

@sanych-sun sanych-sun requested a review from a team as a code owner February 20, 2026 01:29
Copilot AI review requested due to automatic review settings February 20, 2026 01:29
@sanych-sun sanych-sun added the improvement Optimizations or refactoring (no new features or fixes). label Feb 20, 2026
@sanych-sun sanych-sun requested a review from papafe February 20, 2026 01:29
@sanych-sun sanych-sun requested review from adelinowona and removed request for papafe February 20, 2026 01:31
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes the comparison expression translation for nullable boolean constants in the MongoDB C# Driver's LINQ3 implementation. The changes extend existing boolean comparison optimizations to also handle nullable boolean types.

Changes:

  • Modified the comparison translator to recognize nullable boolean comparisons in addition to non-nullable boolean comparisons
  • Added comprehensive unit tests to verify the optimization works correctly for both nullable and non-nullable integer/boolean field comparisons with constant values
  • Implemented test cases covering all comparison operators (eq, gt, gte, lt, lte) for both field-to-constant and constant-to-field orderings

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
ComparisonExpressionToFilterTranslator.cs Extended boolean constant comparison optimization to support nullable boolean types (bool?) in addition to regular booleans
ComparisonExpressionToFilterTranslatorTests.cs Added comprehensive unit tests for comparison operations with nullable and non-nullable constant values across all comparison operators

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

rightExpression.Type == typeof(bool))
(leftExpression.Type == typeof(bool) || leftExpression.Type == typeof(bool?)))
{
return TranslateComparisonToBooleanConstant(context, expression, leftExpression, comparisonOperator, (bool)comparandExpression.Value);
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The cast to bool on line 89 may throw an InvalidCastException when comparandExpression.Value is null for nullable bool comparisons. When leftExpression.Type is typeof(bool?), the constant expression could have a null value. Consider adding a null check or using a safer cast pattern (e.g., comparandExpression.Value as bool? ?? false, or checking comparandExpression.Value for null before casting).

Suggested change
return TranslateComparisonToBooleanConstant(context, expression, leftExpression, comparisonOperator, (bool)comparandExpression.Value);
// Only apply the boolean-constant optimization when the comparand is a non-null boolean.
// For nullable bool comparisons with a null constant, fall through to the general logic below.
if (!(leftExpression.Type == typeof(bool?) && comparandExpression.Value == null))
{
return TranslateComparisonToBooleanConstant(
context,
expression,
leftExpression,
comparisonOperator,
(bool)comparandExpression.Value);
}

Copilot uses AI. Check for mistakes.
CreateTestCase<MyModel>(CreateLambda<MyModel, int>(x => x.Int), Expression.Constant(5), ExpressionType.LessThanOrEqual, AstFilter.Compare(AstFilter.Field("Int"), AstComparisonFilterOperator.Lte, 5)),
CreateTestCase<MyModel>(CreateLambda<MyModel, int?>(x => x.NullableInt), Expression.Constant(5, typeof(int?)), ExpressionType.LessThanOrEqual, AstFilter.Compare(AstFilter.Field("NullableInt"), AstComparisonFilterOperator.Lte, 5)),
CreateTestCase<MyModel>(Expression.Constant(5), CreateLambda<MyModel, int>(x => x.Int), ExpressionType.LessThanOrEqual, AstFilter.Compare(AstFilter.Field("Int"), AstComparisonFilterOperator.Gte, 5)),
CreateTestCase<MyModel>(Expression.Constant(5, typeof(int?)), CreateLambda<MyModel, int?>(x => x.NullableInt), ExpressionType.LessThanOrEqual, AstFilter.Compare(AstFilter.Field("NullableInt"), AstComparisonFilterOperator.Gte, 5)),
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The test cases do not cover nullable bool comparisons to null constants. Given the optimization now includes bool? type support, test cases should verify behavior when comparing nullable bool fields to null values (e.g., x.NullableBool == null). This is important to ensure the optimization handles null correctly.

Suggested change
CreateTestCase<MyModel>(Expression.Constant(5, typeof(int?)), CreateLambda<MyModel, int?>(x => x.NullableInt), ExpressionType.LessThanOrEqual, AstFilter.Compare(AstFilter.Field("NullableInt"), AstComparisonFilterOperator.Gte, 5)),
CreateTestCase<MyModel>(Expression.Constant(5, typeof(int?)), CreateLambda<MyModel, int?>(x => x.NullableInt), ExpressionType.LessThanOrEqual, AstFilter.Compare(AstFilter.Field("NullableInt"), AstComparisonFilterOperator.Gte, 5)),
// Comparison to null constant test cases: bool?
CreateTestCase<MyModel>(CreateLambda<MyModel, bool?>(x => x.NullableBool), Expression.Constant(null, typeof(bool?)), ExpressionType.Equal, AstFilter.Eq(AstFilter.Field("NullableBool"), BsonNull.Value)),
CreateTestCase<MyModel>(Expression.Constant(null, typeof(bool?)), CreateLambda<MyModel, bool?>(x => x.NullableBool), ExpressionType.Equal, AstFilter.Eq(AstFilter.Field("NullableBool"), BsonNull.Value)),

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +131
public int Int {get; set; }

public int? NullableInt {get; set; }
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The properties in the MyModel class are missing a space before the opening brace. According to the codebase convention, property declarations should have a space between "set;" and the closing brace (e.g., "get; set; }" instead of "get; set; }"). This inconsistency can be seen in other test files throughout the codebase.

Suggested change
public int Int {get; set; }
public int? NullableInt {get; set; }
public int Int { get; set; }
public int? NullableInt { get; set; }

Copilot uses AI. Check for mistakes.
resultAst.Render().Should().Be(BsonDocument.Parse(expectedTranslation));
}

// TODO: Probably need to be merged with CSharp2422Tests and CSharp4401Tests.
Copy link
Member Author

Choose a reason for hiding this comment

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

We have tests to cover comparison translation at least in 2 more test classes: CSharp2422Tests and CSharp4401Tests. Should we move that tests into this file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Optimizations or refactoring (no new features or fixes).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant