Skip to content

Wrong SQL translation when call In/Contains method for outer lambda parameter property #423

Open
@letarak

Description

@letarak

DO 7.1.4

Sample

Second query translated with wrong table alias for field [Name]
Forth query throw translation exception

In production we got another behaviour, exception not thrown but SQL use different field (not matched by name)

using DoTest;
using Microsoft.Data.SqlClient;
using Xtensive.IoC;
using Xtensive.Orm;
using Xtensive.Orm.Building;
using Xtensive.Orm.Building.Definitions;
using Xtensive.Orm.Configuration;

internal class Program
{
    private static void Main(string[] args)
    {
        var currentConnection =
            new SqlConnectionStringBuilder(DbHelper.ConnectionString());

        var dc = new DomainConfiguration("sqlserver", currentConnection.ToString());

        dc.Types.Register(typeof(TestEntity));
        dc.Types.Register(typeof(TestEntity2));
        dc.Types.Register(typeof(SqlLogModule));

        dc.UpgradeMode = DomainUpgradeMode.Recreate;

        using (var d = Domain.Build(dc))
        {
            using (var s = d.OpenSession())
            using (s.Activate())
            using (var t  = s.OpenTransaction())
            { 
                // OK
                // SELECT CAST((CASE WHEN EXISTS (SELECT *  FROM [dbo].[Program.TestEntity2] [a] WHERE EXISTS (SELECT *  FROM [dbo].[Program.TestEntity] [b] WHERE (([b].[Name] = [a].[Name]) AND ([a].[Name] = N'123')) ) ) THEN 1 ELSE 0 END)  AS bit) AS [c01umn1];
                _ = Query.All<TestEntity2>()
                    .Where(it => Query.All<TestEntity>().Any(e => e.Name == it.Name && it.Name == "123"))
                    .Any();
                
                // WRONG
                // SELECT CAST((CASE WHEN EXISTS (SELECT *  FROM [dbo].[Program.TestEntity2] [a] WHERE EXISTS (SELECT *  FROM [dbo].[Program.TestEntity] [b] WHERE (([b].[Name] = [a].[Name]) AND ([b].[Name] IN (@p0_2_0_0))) ) ) THEN  1 ELSE  0 END)  AS bit) AS [c01umn2];
                _ = Query.All<TestEntity2>()
                    .Where(it => Query.All<TestEntity>().Any(e => e.Name == it.Name && it.Name.In("123")))
                    .Any();
                
                // OK
                // SELECT CAST((CASE WHEN EXISTS (SELECT *  FROM [dbo].[Program.TestEntity2] [a] WHERE EXISTS (SELECT *  FROM [dbo].[Program.TestEntity] [b] WHERE (([b].[Name] = [a].[Name]) AND ([a].[TestField] = N'123')) ) ) THEN 1 ELSE 0 END)  AS bit) AS [c01umn1];
                _ = Query.All<TestEntity2>()
                    .Where(it => Query.All<TestEntity>().Any(e => e.Name == it.Name && it.TestField == "123"))
                    .Any();
                
                // Exception
                _ = Query.All<TestEntity2>()
                    .Where(it => Query.All<TestEntity>().Any(e => e.Name == it.Name && it.TestField.In("123")))
                    .Any();

                t.Complete();
            }
        }
    }

    [HierarchyRoot]
    public class TestEntity : Entity
    {
        public TestEntity(Session session) : base(session)
        {
        }

        [Key] [Field] public int Id { get; set; }

        [Field] public string Name { get; set; }
        
        [Field] public EnumType? EnumType { get; set; }
    }

    [HierarchyRoot]
    public class TestEntity2 : Entity
    {
        public TestEntity2(Session session) : base(session)
        {
        }

        [Key] [Field] public int Id { get; set; }

        [Field] public string Name { get; set; }

        [Field]
        [Association(OnTargetRemove = OnRemoveAction.None)]
        public TestEntity Owner { get; set; }

        [Field]
        public string TestField { get; set; }
    }
}

internal enum EnumType
{
    A,
    B
}

/// <summary>
/// Sql log
/// </summary>
public class SqlLogModule : IModule
{
    /// <summary>
    ///     Initializes a new instance of the <see cref="SqlLogModule" /> class.
    /// </summary>
    [ServiceConstructor]
    public SqlLogModule()
    {
    }

    /// <inheritdoc />
    public void OnBuilt(Domain domain)
    {
        domain.SessionOpen += (_, args) =>
        {
            args.Session.Events.DbCommandExecuted += DbCommandExecuted;
        };
    }

    /// <inheritdoc />
    public void OnDefinitionsBuilt(BuildingContext context, DomainModelDef model)
    {
    }

    private void DbCommandExecuted(object? sender, DbCommandEventArgs e)
    {
        Console.WriteLine(e.Command.CommandText);
    }
}

Exception

Unhandled exception. Xtensive.Orm.QueryTranslationException: Unable to translate 'Query.All().Where(it => Query.All().Any(e => ((e.Name == it.Name) && it.TestField.In(new String[] {"123"})))).Any()' expression. See inner exception for details.
 ---> System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
   at System.Collections.Generic.List`1.get_Item(Int32 index)
   at Xtensive.Orm.Providers.SqlCompiler.<>c__DisplayClass81_0.<CreateIncludeViaTemporaryTableExpression>b__0(Int32 index)
   at System.Linq.Enumerable.SelectIListIterator`2.MoveNext()
   at Xtensive.Core.EnumerableExtensions.ToArray[T](IEnumerable`1 sequence, Int32 length)
   at Xtensive.Orm.Providers.SqlCompiler.CreateIncludeViaTemporaryTableExpression(IncludeProvider provider, IList`1 sourceColumns, TemporaryTableDescriptor& tableDescriptor)
   at Xtensive.Orm.Providers.SqlCompiler.VisitInclude(IncludeProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Providers.SqlCompiler.VisitFilter(FilterProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Providers.SqlCompiler.VisitExistence(ExistenceProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Providers.SqlCompiler.VisitSelect(SelectProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Providers.SqlCompiler.VisitApply(ApplyProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Providers.SqlCompiler.VisitFilter(FilterProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Providers.SqlCompiler.VisitExistence(ExistenceProvider provider)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Compile(CompilableProvider cp)
   at Xtensive.Orm.Rse.Compilation.Compiler`1.Xtensive.Orm.Rse.Compilation.ICompiler.Compile(CompilableProvider provider)
   at Xtensive.Orm.Providers.CompilationService.Compile(CompilableProvider provider, CompilerConfiguration configuration)
   at Xtensive.Orm.Linq.Translator.Translate(ProjectionExpression projection, IEnumerable`1 tupleParameterBindings)
   at Xtensive.Orm.Linq.Translator.Translate()
   at Xtensive.Orm.Linq.QueryProvider.Translate(Expression expression, CompilerConfiguration compilerConfiguration)
   --- End of inner exception stack trace ---
   at Xtensive.Orm.Linq.QueryProvider.Translate(Expression expression, CompilerConfiguration compilerConfiguration)
   at Xtensive.Orm.Linq.QueryProvider.Translate(Expression expression)
   at Xtensive.Orm.Linq.QueryProvider.Execute[TResult](Expression expression, Func`4 runQuery)
   at Xtensive.Orm.Linq.QueryProvider.ExecuteScalar[TResult](Expression expression)
   at Xtensive.Orm.Linq.QueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.Any[TSource](IQueryable`1 source)
   at Program.Main(String[] args) in /Users/anton.guschin/RiderProjects/DoTest/DoTest/Program.cs:line 42

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