Skip to content

Checked context breaks Linq-to-CQL #624

@BrunoJuchli

Description

@BrunoJuchli

Symptom

When using a checked context (we enabled it by default for the whole solution) some of our queries start to fail with errors like:

System.InvalidOperationException : variable 'x' of type 'CounterEntryEntity`1[ElectricActiveEnergyTwoTariffsCount]' referenced from scope '', but it is not defined
at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.Compiler.VariableBinder.VisitUnary(UnaryExpression node)
at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitUnary(UnaryExpression node)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitBinary(BinaryExpression node)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitBinary(BinaryExpression node)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitBinary(BinaryExpression node)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitBinary(BinaryExpression node)
at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitLambda[T](Expression`1 node)
at Cassandra.Data.Linq.CqlExpressionVisitor.VisitMethodCall(MethodCallExpression node)
at Cassandra.Data.Linq.CqlExpressionVisitor.GetSelect(Expression expression, Object[]& values)
at Cassandra.Data.Linq.CqlQuery`1.ExecutePagedAsync(String executionProfile)

Analysis

Given a table entity like:

public class Table
{
    public short Short { get; init; }
}

And a query where filtering like:

short s = 3821;

.Where(t => t.Short == s);

Depending on whether a checked or unchecked context is used, the following expression is generated:

  • ... (ConvertChecked(t.Short, Int32) == ConvertChecked(value(<generatedclass>.s, Int32)))
  • ... (Convert(t.Short, Int32) == Convert(value(<generatedclass>.s, Int32)))

Proposed Solution

There's four usages of ExpressionType.Convert in the solution (3 in CqlExpressionVisitor and 1 in Mapping\Map.cs.
All perform comparisons like node.NodeType == ExpressionType.Convert
I suggest it should be replaced with

(node.NodeType == ExpressionType.Convert || node.NodeType == ExpressionType.ConvertChecked)

Workaround

Instead of writing

.Where(t => t.Short == s);

write

.Where(unchecked(t => t.Short == s));

Reproduce

I wanted to provide a PR with a reproduction. Unfortunately I fail to compile the solution (both from dotnet CLI and within rider, I get nuget restore errors for Apps.Metrics.Concurrency and I also think there may be some setup steps necessary to get the right target frameworks in the projects, but I haven't found any documentation / the documentation link to the wiki is dead).

However, adding

<PropertyGroup>
  <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>

to the test projects might already cause some of the tests to fail.

if not, try a scenario as shown in Analysis, where you filter for a Short value, and put the predicate into a checked context:

short s = 3802;

.Where(checked(t => t.Short == s));

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions