dataobjects-net icon indicating copy to clipboard operation
dataobjects-net copied to clipboard

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

Open letarak opened this issue 1 year ago • 0 comments

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

letarak avatar Feb 05 '25 08:02 letarak