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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions