Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
letarak opened this issue Feb 5, 2025 · 0 comments

Comments

@letarak
Copy link

letarak commented Feb 5, 2025

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant