diff --git a/ChangeLog/6.0.13_dev.txt b/ChangeLog/6.0.13_dev.txt
index 797c999732..d2dd5f7a16 100644
--- a/ChangeLog/6.0.13_dev.txt
+++ b/ChangeLog/6.0.13_dev.txt
@@ -1,2 +1,4 @@
[main] Fixed certain cases of bad translation of casts via 'as' operator in LINQ queries
[main] Addressed certain issues of translation connected with comparison with local entity instace within LINQ queries
+[main] Fixed rare issues of incorrect translation of filtered index expressions including conditional expressions
+[postgresql] Fixed issue of incorrect translation of contitional expressions including comparison with nullable fields
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm.Tests.Framework/NUnitFrameworkExtensions/RequireProviderAttributes.cs b/Orm/Xtensive.Orm.Tests.Framework/NUnitFrameworkExtensions/RequireProviderAttributes.cs
new file mode 100644
index 0000000000..921bb82ab8
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests.Framework/NUnitFrameworkExtensions/RequireProviderAttributes.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NUnit.Framework.Interfaces;
+
+namespace Xtensive.Orm.Tests
+{
+ ///
+ /// Base attribute for test storage requirement
+ ///
+ public abstract class RequireProvderAttribute : Attribute, ITestAction
+ {
+ private Version minVersion = null;
+ private Version maxVersion = null;
+
+ ///
+ /// Gets or sets Minimal version that required for test.
+ /// If not set, version check is not applied
+ ///
+ public virtual string MinVersion
+ {
+ get { return (minVersion == null) ? null : minVersion.ToString(); }
+ set {
+ if (value == null)
+ minVersion = null;
+ else if (!Version.TryParse(value, out minVersion))
+ throw new ArgumentException("Not a version string", nameof(value));
+ }
+ }
+
+ ///
+ /// Gets or sets Maximal version that required for test.
+ /// If not set, version check is not applied.
+ ///
+ public virtual string MaxVersion
+ {
+ get { return (maxVersion == null) ? null : maxVersion.ToString(); }
+ set {
+ if (value == null)
+ maxVersion = null;
+ else if (!Version.TryParse(value, out maxVersion))
+ throw new ArgumentException("Not a version string", nameof(value));
+ }
+ }
+
+ protected abstract StorageProvider RequiredProviders { get; }
+
+ public ActionTargets Targets => ActionTargets.Test;
+
+ public void AfterTest(ITest test)
+ {
+ Require.ProviderIs(RequiredProviders);
+ if (minVersion != null)
+ Require.ProviderVersionAtLeast(minVersion);
+ if (maxVersion != null)
+ Require.ProviderVersionAtMost(maxVersion);
+ }
+
+ public void BeforeTest(ITest test) { }
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequireSqlServerAttribute : RequireProvderAttribute
+ {
+ protected override StorageProvider RequiredProviders => StorageProvider.SqlServer;
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequirePostgreSqlAttribute : RequireProvderAttribute
+ {
+ protected override StorageProvider RequiredProviders => StorageProvider.PostgreSql;
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequireMySqlAttribute : RequireProvderAttribute
+ {
+ protected override StorageProvider RequiredProviders => StorageProvider.MySql;
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequireFirebirdAttribute : RequireProvderAttribute
+ {
+ protected override StorageProvider RequiredProviders => StorageProvider.Firebird;
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequireOracleSqlAttribute : RequireProvderAttribute
+ {
+ protected override StorageProvider RequiredProviders => StorageProvider.Oracle;
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequireSqliteAttribute : RequireProvderAttribute
+ {
+ protected override StorageProvider RequiredProviders => StorageProvider.Sqlite;
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class RequireSeveralProvidersAttribute : RequireProvderAttribute
+ {
+ private readonly StorageProvider providers;
+
+ protected override StorageProvider RequiredProviders => providers;
+
+ public override string MinVersion
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ public override string MaxVersion
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ public RequireSeveralProvidersAttribute(StorageProvider allowedProviders)
+ {
+ providers = allowedProviders;
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueJira0802_PostgreOrderByWithConditionalIssue.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueJira0802_PostgreOrderByWithConditionalIssue.cs
new file mode 100644
index 0000000000..18880a6dbf
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/Issues/IssueJira0802_PostgreOrderByWithConditionalIssue.cs
@@ -0,0 +1,1526 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+// Created by: Alexey Kulakov
+// Created: 2024.01.17
+
+using System;
+using System.Linq;
+using NUnit.Framework;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration;
+using Xtensive.Orm.Tests.Issues.IssueJira0802_PostgreOrderByWithConditionalIssueModel;
+
+namespace Xtensive.Orm.Tests.Issues
+{
+ public sealed class IssueJira0802_PostgreOrderByWithConditionalIssue : AutoBuildTest
+ {
+ private Key sharedFlowKey;
+
+ public Session Session { get; set; }
+
+ public TransactionScope Transaction { get; set; }
+
+ public override void TestFixtureSetUp()
+ {
+ base.TestFixtureSetUp();
+ Session = Domain.OpenSession();
+ }
+
+ public override void TestFixtureTearDown()
+ {
+ Session?.Dispose();
+ base.TestFixtureTearDown();
+ }
+
+ [SetUp]
+ public void SetUp()
+ {
+ Transaction = Session.OpenTransaction();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ Transaction?.Dispose();
+ }
+
+ protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.PostgreSql);
+
+ protected override DomainConfiguration BuildConfiguration()
+ {
+ var config = base.BuildConfiguration();
+ config.Types.Register(typeof(MesObject).Assembly, typeof(MesObject).Namespace);
+ config.UpgradeMode = DomainUpgradeMode.Recreate;
+ return config;
+ }
+
+ protected override void PopulateData()
+ {
+ using (var session = Domain.OpenSession())
+ using (var transaction = session.OpenTransaction()) {
+ var sharedFlow = new LogisticFlow(session, 1000);
+ sharedFlowKey = sharedFlow.Key;
+
+ _ = new PickingProductRequirement(session, 10) {
+ Quantity = new DimensionalField(session, 36),
+ InventoryAction = new InventoryAction(session, 100, sharedFlow, "a")
+ };
+
+ _ = new PickingProductRequirement(session, 20) {
+ Quantity = new DimensionalField(session, 35),
+ InventoryAction = new InventoryAction(session, 200, sharedFlow, "b")
+ };
+
+ _ = new PickingProductRequirement(session, 30) {
+ Quantity = new DimensionalField(session, 34),
+ InventoryAction = new InventoryAction(session, 300, sharedFlow, "a")
+ };
+
+ _ = new PickingProductRequirement(session, 40) {
+ Quantity = new DimensionalField(session, 34),
+ InventoryAction = new InventoryAction(session, 400, new LogisticFlow(session, 1100), null)
+ };
+
+ transaction.Complete();
+ }
+ }
+
+
+ [Test]
+ public void ConditionalExprByEntityInOrderBy()
+ {
+ var sharedFlow = Session.Query.Single(sharedFlowKey);
+
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.GreaterThan(0));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(1).All(a => a.V2 < 40), Is.False);
+ Assert.That(results.Skip(1).All(a => a.V2 > 40 && a.V2 < 75), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInOrderBy1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(1).All(a => a.V2 < 40), Is.False);
+ Assert.That(results.Skip(1).All(a => a.V2 > 40 && a.V2 < 75), Is.True);
+ Assert.That(results[0].V2, Is.LessThan(40));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInOrderBy2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(3).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Skip(3).All(a => a.V2 > 40 && a.V2 < 75), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderBy1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(3).All(a => a.V2 < 40), Is.True);
+ Assert.That(results[3].V2, Is.GreaterThan(40));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderBy2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(1).All(a => a.V2 > 40), Is.True);
+ Assert.That(results[0].V2, Is.LessThan(40));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderBy3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(2).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Skip(2).All(a => a.V2 > 40 && a.V2 < 75), Is.True);
+ Assert.That(results[0].V2, Is.LessThan(40));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderBy4()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(2).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Skip(2).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderBy5()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(3).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Skip(3).All(a => a.V2 > 40 && a.V2 < 75), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderBy6()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(1).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Skip(1).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByEntityInOrderByDesc()
+ {
+ var sharedFlow = Session.Query.Single(sharedFlowKey);
+
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(3).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Take(3).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInOrderByDesc2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(3).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Take(3).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInOrderByDesc3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(1).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Take(1).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderByDesc1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(1).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.Skip(1).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderByDesc2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(3).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.Skip(3).All(a => a.V2 < 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderByDesc3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(2).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Take(2).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderByDesc4()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(2).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.Skip(2).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderByDesc5()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Take(1).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.Skip(1).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInOrderByDesc6()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .OrderByDescending(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Skip(3).All(a => a.V2 < 40), Is.True);
+ Assert.That(results.Take(3).All(a => a.V2 > 40), Is.True);
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByEntityInWhere()
+ {
+ var sharedFlow = Session.Query.Single(sharedFlowKey);
+
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInWhere1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInWhere2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(1));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInWhere1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(1));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInWhere2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInWhere3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(2));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInWhere4()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(2));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInWhere5()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(1));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInWhere6()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Where(t => t.V2 > 40)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.All(a => a.V2 > 40), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByEntityInGroupBy()
+ {
+ var sharedFlow = Session.Query.Single(sharedFlowKey);
+
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(1));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(3));
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInGroupBy1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(1));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(2));
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInGroupBy2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(2));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(1));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInGroupBy1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(3));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(1));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInGroupBy2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(1));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(3));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInGroupBy3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(2));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(2));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInGroupBy4()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(2));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(2));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInGroupBy5()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(2));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(1));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInGroupBy6()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .GroupBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(3));
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+
+ Assert.That(results.Select(r => r.Key).Count(a => a < 40), Is.EqualTo(1));
+ Assert.That(results.Select(r => r.Key).Count(a => a > 40), Is.EqualTo(2));
+ }
+
+ [Test]
+ public void ConditionalExprByEntityInSum()
+ {
+ var sharedFlow = Session.Query.Single(sharedFlowKey);
+
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInSum1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInSum2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInSum1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInSum2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInSum3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInSum4()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInSum5()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInSum6()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (int) (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1))
+ })
+ .Sum(t => t.V2);
+
+ Assert.That(results, Is.EqualTo(expected));
+ }
+
+ [Test]
+ public void ConditionalExprByEntityInInclude()
+ {
+ var sharedFlow = Session.Query.Single(sharedFlowKey);
+
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.LogisticFlow == sharedFlow ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(2));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInInclude1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.ID > 100 ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNumberInInclude2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1)).In(40, 70, 72) // 100 -> 36
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.ID == 100 ? margin : 1)).In(40, 70, 72) // 100 -> 36
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInInclude1()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1)).In(40, 68, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == null ? margin : 1)).In(40, 68, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInInclude2()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField != null ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(2));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInInclude3()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "a" ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInInclude4()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "a" || p.InventoryAction.NullableField == null ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInInclude5()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue * (p.InventoryAction.NullableField == "b" ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+ [Test]
+ public void ConditionalExprByNullableFieldInInclude6()
+ {
+ var margin = 2;
+ var results = Session.Query.All()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ var expected = Session.Query.All().AsEnumerable()
+ .Select(p => new {
+ V2 = (p.Quantity.NormalizedValue *
+ (p.InventoryAction.NullableField != "b" || p.InventoryAction.NullableField == null ? margin : 1)).In(40, 70, 72)
+ })
+ .OrderBy(t => t.V2)
+ .ToArray();
+
+ Assert.That(expected.Length, Is.EqualTo(4));
+
+ Assert.That(results.Length, Is.EqualTo(expected.Length));
+ Assert.That(results.Count(p => p.V2 == true), Is.EqualTo(1));
+ Assert.That(results.SequenceEqual(expected), Is.True);
+ }
+
+
+ //private void PrintExpected(TItem[] items, Func accessor)
+ //{
+ // Console.WriteLine();
+ // Console.WriteLine("Expected order of values:");
+
+ // foreach (var item in items) {
+ // Console.WriteLine(accessor(item));
+ // }
+ //}
+
+ private static void CommandExecutingEventHandler(object sender, DbCommandEventArgs e)
+ {
+ var command = e.Command;
+ var commandText = command.CommandText;
+ Console.WriteLine("No Modifications SQL Text:");
+ Console.WriteLine(commandText);
+ var parameters = command.Parameters;
+
+ Console.Write(" Parameters: ");
+ for (int i = 0, count = parameters.Count; i < count; i++) {
+ var parameter = parameters[0];
+ Console.WriteLine($"{parameter.ParameterName} = {parameter.Value.ToString()}");
+ }
+ }
+ }
+}
+
+
+namespace Xtensive.Orm.Tests.Issues.IssueJira0802_PostgreOrderByWithConditionalIssueModel
+{
+ [HierarchyRoot]
+ public class InventoryAction : MesObject
+ {
+ [Field]
+ public LogisticFlow LogisticFlow { get; set; }
+
+ [Field(Length = 50)]
+ public string NullableField { get; set; }
+
+ public InventoryAction(Session Session, int id, LogisticFlow logisticFlow, string nullableValue)
+ : base(Session, id)
+ {
+ LogisticFlow = logisticFlow;
+ NullableField = nullableValue;
+ }
+ }
+
+ [HierarchyRoot]
+ public class LogisticFlow : MesObject
+ {
+ public LogisticFlow(Session Session, int id)
+ : base(Session, id)
+ {
+ }
+ }
+
+ [HierarchyRoot]
+ public class PickingProductRequirement : MesObject
+ {
+ [Field]
+ public DimensionalField Quantity { get; set; }
+
+ [Field]
+ public InventoryAction InventoryAction { get; set; }
+
+ public PickingProductRequirement(Session Session, int id)
+ : base(Session, id)
+ {
+ }
+ }
+
+ public class DimensionalField : Structure
+ {
+ [Field]
+ public int NormalizedValue { get; private set; }
+
+ public DimensionalField(Session Session, int nValue)
+ : base(Session)
+ {
+ NormalizedValue = nValue;
+ }
+ }
+
+ public abstract class MesObject : Entity
+ {
+ [Field, Key]
+ public int ID { get; private set; }
+
+ protected MesObject(Session Session, int id)
+ : base(Session, id)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm.Tests/Storage/PartialIndexTest.cs b/Orm/Xtensive.Orm.Tests/Storage/PartialIndexTest.cs
index 2a8bf8cbd5..aafd9540f0 100644
--- a/Orm/Xtensive.Orm.Tests/Storage/PartialIndexTest.cs
+++ b/Orm/Xtensive.Orm.Tests/Storage/PartialIndexTest.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2021 Xtensive LLC.
+// Copyright (C) 2011-2024 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
@@ -14,6 +14,7 @@
using Xtensive.Orm.Model;
using Xtensive.Orm.Providers;
using Xtensive.Orm.Tests.Storage.PartialIndexTestModel;
+using NUnit.Framework.Interfaces;
namespace Xtensive.Orm.Tests.Storage.PartialIndexTestModel
{
@@ -70,9 +71,9 @@ public class SimpleFilterWithProperty : TestBase
}
[HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
- public class FilterOnReferenceField : TestBase
+ public class FilterOnReferenceField1 : TestBase
{
- public static Expression> Index() =>
+ public static Expression> Index() =>
test => test.Target != null;
[Field]
@@ -80,15 +81,87 @@ public static Expression> Index() =>
}
[HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
- public class FilterOnComplexReferenceField : TestBase
+ public class FilterOnReferenceField2 : TestBase
{
- public static Expression> Index() =>
+ public static Expression> Index() =>
+ test => test.Target == null;
+
+ [Field]
+ public TargetEntity Target { get; set; }
+ }
+
+ [HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
+ public class FilterOnReferenceField3 : TestBase
+ {
+ public static Expression> Index() =>
+ test => test.Target == test.Alien;
+
+ [Field]
+ public TargetEntity Target { get; set; }
+
+ [Field]
+ public TargetEntity Alien { get; set; }
+ }
+
+ [HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
+ public class FilterOnReferenceField4 : TestBase
+ {
+ public static Expression> Index() =>
+ test => test.Target != test.Alien;
+
+ [Field]
+ public TargetEntity Target { get; set; }
+
+ [Field]
+ public TargetEntity Alien { get; set; }
+ }
+
+ [HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
+ public class FilterOnComplexReferenceField1 : TestBase
+ {
+ public static Expression> Index() =>
test => test.Target != null;
[Field]
public ComplexTargetEntity Target { get; set; }
}
+ [HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
+ public class FilterOnComplexReferenceField2 : TestBase
+ {
+ public static Expression> Index() =>
+ test => test.Target == null;
+
+ [Field]
+ public ComplexTargetEntity Target { get; set; }
+ }
+
+ [HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
+ public class FilterOnComplexReferenceField3 : TestBase
+ {
+ public static Expression> Index() =>
+ test => test.Target == test.Alien;
+
+ [Field]
+ public ComplexTargetEntity Target { get; set; }
+
+ [Field]
+ public ComplexTargetEntity Alien { get; set; }
+ }
+
+ [HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
+ public class FilterOnComplexReferenceField4 : TestBase
+ {
+ public static Expression> Index() =>
+ test => test.Target != test.Alien;
+
+ [Field]
+ public ComplexTargetEntity Target { get; set; }
+
+ [Field]
+ public ComplexTargetEntity Alien { get; set; }
+ }
+
[HierarchyRoot, Index(nameof(Target), Filter = nameof(Index))]
public class FilterOnReferenceIdField : TestBase
{
@@ -164,6 +237,16 @@ public class ContainsOperatorSupport : TestBase
public string TestField { get; set; }
}
+ [HierarchyRoot, Index(nameof(TestField), Filter = nameof(Index))]
+ public class ConditionalExpressionSuport : TestBase
+ {
+ public static Expression> Index =>
+ test => test.Id == 100 ? test.TestField.Contains("hello") : false;
+
+ [Field]
+ public string TestField { get; set; }
+ }
+
[HierarchyRoot,
Index(nameof(TestField), Filter = nameof(More), Name = "MoreIndex"),
Index(nameof(TestField), Filter = nameof(Less), Name = "LessIndex")]
@@ -520,10 +603,28 @@ private void AssertBuildFailure(params Type[] entities)
public void SimpleFilterWithPropertyTest() => AssertBuildSuccess(typeof(SimpleFilterWithProperty));
[Test]
- public void FilterOnReferenceFieldTest() => AssertBuildSuccess(typeof(FilterOnReferenceField));
+ public void FilterOnReferenceFieldTest1() => AssertBuildSuccess(typeof(FilterOnReferenceField1));
[Test]
- public void FilterOnComplexReferenceFieldTest() => AssertBuildSuccess(typeof(FilterOnComplexReferenceField));
+ public void FilterOnReferenceFieldTest2() => AssertBuildSuccess(typeof(FilterOnReferenceField2));
+
+ [Test]
+ public void FilterOnReferenceFieldTest3() => AssertBuildFailure(typeof(FilterOnReferenceField3));
+
+ [Test]
+ public void FilterOnReferenceFieldTest4() => AssertBuildFailure(typeof(FilterOnReferenceField4));
+
+ [Test]
+ public void FilterOnComplexReferenceFieldTest1() => AssertBuildSuccess(typeof(FilterOnComplexReferenceField1));
+
+ [Test]
+ public void FilterOnComplexReferenceFieldTest2() => AssertBuildSuccess(typeof(FilterOnComplexReferenceField2));
+
+ [Test]
+ public void FilterOnComplexReferenceFieldTest3() => AssertBuildFailure(typeof(FilterOnComplexReferenceField3));
+
+ [Test]
+ public void FilterOnComplexReferenceFieldTest4() => AssertBuildFailure(typeof(FilterOnComplexReferenceField4));
[Test]
public void FilterOnReferenceFieldIdTest() => AssertBuildSuccess(typeof(FilterOnReferenceIdField));
@@ -543,6 +644,9 @@ private void AssertBuildFailure(params Type[] entities)
[Test]
public void ContainsOperatorSupportTest() => AssertBuildSuccess(typeof(ContainsOperatorSupport));
+ [Test, RequirePostgreSql]
+ public void ConditionalOperationSupportTest() => AssertBuildSuccess(typeof(ConditionalExpressionSuport));
+
[Test]
public void DoubleIndexWithNameTest() => AssertBuildSuccess(typeof(DoubleIndexWithName));
@@ -564,12 +668,34 @@ private void AssertBuildFailure(params Type[] entities)
[Test]
public void EnumFieldFilterTest() => AssertBuildSuccess(typeof(EnumFieldFilter));
- [Test]
- public void ValidateTest()
+ [Test, RequirePostgreSql]
+ public void ValidateTestForPgSql()
+ {
+ var types = typeof(TestBase).Assembly
+ .GetTypes()
+ .Where(type => type.Namespace == typeof(TestBase).Namespace
+ && type != typeof(InheritanceClassTable)
+ && type != typeof(FilterOnReferenceField3)
+ && type != typeof(FilterOnReferenceField4)
+ && type != typeof(FilterOnComplexReferenceField3)
+ && type != typeof(FilterOnComplexReferenceField4))
+ .ToList();
+ BuildDomain(types, DomainUpgradeMode.Recreate);
+ BuildDomain(types, DomainUpgradeMode.Validate);
+ }
+
+ [Test, RequireSqlServer]
+ public void ValidateTestForSqlServer()
{
var types = typeof(TestBase).Assembly
.GetTypes()
- .Where(type => type.Namespace == typeof(TestBase).Namespace && type != typeof(InheritanceClassTable))
+ .Where(type => type.Namespace == typeof(TestBase).Namespace
+ && type != typeof(InheritanceClassTable)
+ && type != typeof(ConditionalExpressionSuport)
+ && type != typeof(FilterOnReferenceField3)
+ && type != typeof(FilterOnReferenceField4)
+ && type != typeof(FilterOnComplexReferenceField3)
+ && type != typeof(FilterOnComplexReferenceField4))
.ToList();
BuildDomain(types, DomainUpgradeMode.Recreate);
BuildDomain(types, DomainUpgradeMode.Validate);
diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs
index fd08eededc..0d2482b618 100644
--- a/Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs
+++ b/Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs
@@ -88,6 +88,8 @@ protected override Expression VisitBinary(BinaryExpression b)
return BuildEntityCheck(field, b.NodeType);
if (entityAccessMap.TryGetValue(right, out field) && IsNull(left))
return BuildEntityCheck(field, b.NodeType);
+ if (entityAccessMap.TryGetValue(left, out var _) && entityAccessMap.TryGetValue(right, out var _))
+ throw UnableToTranslate(b, Strings.ComparisonOfTwoEntityFieldsIsNotSupported);
return base.VisitBinary(b);
diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs
index 49cc1b674d..8b843abaaf 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs
@@ -15,7 +15,7 @@
namespace Xtensive.Orm.Providers
{
- partial class ExpressionProcessor
+ internal partial class ExpressionProcessor
{
private readonly static Type ObjectType = typeof(object);
private readonly static Type BooleanType = typeof(bool);
@@ -157,7 +157,7 @@ private SqlExpression TryTranslateBinaryExpressionSpecialCases(Expression expres
private SqlExpression TryTranslateEqualitySpecialCases(SqlExpression left, SqlExpression right)
{
- if (right.NodeType==SqlNodeType.Null || emptyStringIsNull && IsEmptyStringLiteral(right))
+ if (right.NodeType==SqlNodeType.Null || EmptyStringIsNull && IsEmptyStringLiteral(right))
return SqlDml.IsNull(left);
object id = null;
@@ -173,7 +173,7 @@ private SqlExpression TryTranslateEqualitySpecialCases(SqlExpression left, SqlEx
private SqlExpression TryTranslateInequalitySpecialCases(SqlExpression left, SqlExpression right)
{
- if (right.NodeType==SqlNodeType.Null || emptyStringIsNull && IsEmptyStringLiteral(right))
+ if (right.NodeType==SqlNodeType.Null || EmptyStringIsNull && IsEmptyStringLiteral(right))
return SqlDml.IsNotNull(left);
object id = null;
diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs
index fc50bf5519..1c6bb185b5 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2008-2021 Xtensive LLC.
+// Copyright (C) 2008-2024 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Alexey Kochetov
@@ -22,34 +22,51 @@ namespace Xtensive.Orm.Providers
{
internal sealed partial class ExpressionProcessor : ExpressionVisitor
{
+ [Flags]
+ private enum ProcessorOptions
+ {
+ None = 0,
+ FixBooleanExpressions = 1 << 0,
+ PreferCaseOverVariant = 1 << 1,
+ EmptyStringIsNull = 1 << 2,
+ DateTimeEmulation = 1 << 3,
+ DateTimeOffsetEmulation = 1 << 4,
+ SpecialByteArrayComparison = 1 << 5
+ }
+
private static readonly SqlExpression SqlFalse = SqlDml.Literal(false);
private static readonly SqlExpression SqlTrue = SqlDml.Literal(true);
+ private readonly SqlCompiler compiler;
+ private readonly LambdaExpression lambda;
private readonly StorageDriver driver;
private readonly BooleanExpressionConverter booleanExpressionConverter;
private readonly IMemberCompilerProvider memberCompilerProvider;
private readonly IReadOnlyList[] sourceColumns;
private readonly ExpressionEvaluator evaluator;
private readonly ParameterExtractor parameterExtractor;
- private readonly LambdaExpression lambda;
- private readonly HashSet bindings;
- private readonly List activeParameters;
- private readonly Dictionary> sourceMapping;
- private readonly SqlCompiler compiler;
+ private readonly ProviderInfo providerInfo;
+ private readonly ProcessorOptions options;
+ private readonly List activeParameters
+ = new List();
+ private readonly Dictionary> sourceMapping
+ = new Dictionary>();
private readonly Dictionary bindingsWithIdentity
= new Dictionary();
- private readonly List otherBindings = new List();
-
- private readonly bool fixBooleanExpressions;
- private readonly bool emptyStringIsNull;
- private readonly bool dateTimeEmulation;
- private readonly bool dateTimeOffsetEmulation;
- private readonly bool specialByteArrayComparison;
- private readonly ProviderInfo providerInfo;
+ private readonly List otherBindings
+ = new List();
private bool executed;
+ private bool FixBooleanExpressions => (options & ProcessorOptions.FixBooleanExpressions) !=0;
+ private bool PreferCaseOverVariant => options.HasFlag(ProcessorOptions.PreferCaseOverVariant);
+ private bool EmptyStringIsNull => (options & ProcessorOptions.EmptyStringIsNull) != 0;
+ private bool DateTimeEmulation => (options & ProcessorOptions.DateTimeEmulation) != 0;
+ private bool DateTimeOffsetEmulation => (options & ProcessorOptions.DateTimeOffsetEmulation) != 0;
+ private bool SpecialByteArrayComparison => (options & ProcessorOptions.SpecialByteArrayComparison) != 0;
+
+
public SqlExpression Translate()
{
if (executed)
@@ -101,12 +118,12 @@ private SqlExpression VisitParameterAccess(Expression e, bool smartNull)
SqlExpression result;
if (optimizeBooleanParameter) {
result = SqlDml.Variant(binding, SqlFalse, SqlTrue);
- if (fixBooleanExpressions)
+ if (FixBooleanExpressions)
result = booleanExpressionConverter.IntToBoolean(result);
}
else {
result = binding.ParameterReference;
- if (type==typeof(bool) && fixBooleanExpressions)
+ if (type == typeof(bool) && FixBooleanExpressions)
result = booleanExpressionConverter.IntToBoolean(result);
else if (typeMapping.ParameterCastRequired)
result = SqlDml.Cast(result, typeMapping.MapType());
@@ -151,9 +168,9 @@ private SqlExpression VisitCast(UnaryExpression cast, SqlExpression operand)
if (IsEnumUnderlyingType(sourceType, targetType) || IsEnumUnderlyingType(targetType, sourceType))
return operand;
// Special case for boolean cast
- if (fixBooleanExpressions && IsBooleanExpression(cast.Operand)) {
+ if (FixBooleanExpressions && IsBooleanExpression(cast.Operand)) {
var result = SqlDml.Case();
- result.Add(operand, 1);
+ _ = result.Add(operand, 1);
result.Else = 0;
operand = result;
}
@@ -177,7 +194,7 @@ protected override SqlExpression VisitBinary(BinaryExpression expression)
expression.NodeType == ExpressionType.Equal
|| expression.NodeType == ExpressionType.NotEqual;
- var isBooleanFixRequired = fixBooleanExpressions
+ var isBooleanFixRequired = FixBooleanExpressions
&& (isEqualityCheck || expression.NodeType == ExpressionType.Coalesce)
&& (IsBooleanExpression(expression.Left) || IsBooleanExpression(expression.Right));
@@ -214,7 +231,7 @@ protected override SqlExpression VisitBinary(BinaryExpression expression)
}
//handle SQLite DateTime comparsion
- if (dateTimeEmulation
+ if (DateTimeEmulation
&& left.NodeType != SqlNodeType.Null
&& right.NodeType != SqlNodeType.Null
&& IsComparisonExpression(expression)
@@ -224,7 +241,7 @@ protected override SqlExpression VisitBinary(BinaryExpression expression)
}
//handle SQLite DateTimeOffset comparsion
- if (dateTimeOffsetEmulation
+ if (DateTimeOffsetEmulation
&& left.NodeType != SqlNodeType.Null
&& right.NodeType != SqlNodeType.Null
&& IsComparisonExpression(expression)
@@ -234,7 +251,7 @@ protected override SqlExpression VisitBinary(BinaryExpression expression)
}
//handle Oracle special syntax of BLOB comparison
- if (specialByteArrayComparison
+ if (SpecialByteArrayComparison
&& (IsExpressionOf(expression.Left, typeof(byte[])) || IsExpressionOf(expression.Left, typeof(byte[])))) {
var comparison = BuildByteArraySyntaxComparison(left, right);
left = comparison.left;
@@ -323,43 +340,46 @@ protected override SqlExpression VisitConditional(ConditionalExpression expressi
var check = Visit(expression.Test);
var ifTrue = Visit(expression.IfTrue);
var ifFalse = Visit(expression.IfFalse);
- SqlContainer container = ifTrue as SqlContainer;
- if (container!=null)
- ifTrue = TryUnwrapEnum(container);
- container = ifFalse as SqlContainer;
- if (container!=null)
- ifFalse = TryUnwrapEnum(container);
- var boolCheck = fixBooleanExpressions
+
+ if (ifTrue is SqlContainer ifTrueContainer)
+ ifTrue = TryUnwrapEnum(ifTrueContainer);
+ if (ifFalse is SqlContainer ifFalseContainer)
+ ifFalse = TryUnwrapEnum(ifFalseContainer);
+
+ var fixExpressions = FixBooleanExpressions;
+
+ var boolCheck = fixExpressions
? booleanExpressionConverter.BooleanToInt(check)
: check;
var varCheck = boolCheck as SqlVariant;
- if (!varCheck.IsNullReference())
+
+ if (!PreferCaseOverVariant && !varCheck.IsNullReference()) {
return SqlDml.Variant(varCheck.Id, ifFalse, ifTrue);
- if (fixBooleanExpressions && IsBooleanExpression(expression)) {
- var c = SqlDml.Case();
- c[check] = booleanExpressionConverter.BooleanToInt(ifTrue);
- c.Else = booleanExpressionConverter.BooleanToInt(ifFalse);
- return booleanExpressionConverter.IntToBoolean(c);
+ }
+ var @case = SqlDml.Case();
+ if (fixExpressions && IsBooleanExpression(expression)) {
+ @case[check] = booleanExpressionConverter.BooleanToInt(ifTrue);
+ @case.Else = booleanExpressionConverter.BooleanToInt(ifFalse);
+ return booleanExpressionConverter.IntToBoolean(@case);
}
else {
- var c = SqlDml.Case();
- c[check] = ifTrue;
- c.Else = ifFalse;
- return c;
+ @case[check] = ifTrue;
+ @case.Else = ifFalse;
+ return @case;
}
}
protected override SqlExpression VisitConstant(ConstantExpression expression)
{
if (expression.Value==null)
- return fixBooleanExpressions && expression.Type==typeof (bool?)
+ return FixBooleanExpressions && expression.Type==typeof (bool?)
? booleanExpressionConverter.IntToBoolean(SqlDml.Null)
: SqlDml.Null;
var type = expression.Type;
if (type==typeof (object))
type = expression.Value.GetType();
type = type.StripNullable();
- if (fixBooleanExpressions && type==typeof (bool)) {
+ if (FixBooleanExpressions && type == typeof(bool)) {
var literal = SqlDml.Literal((bool) expression.Value);
return booleanExpressionConverter.IntToBoolean(literal);
}
@@ -404,7 +424,7 @@ private SqlExpression VisitTupleAccess(MethodCallExpression tupleAccess)
var queryRef = sourceMapping[(ParameterExpression) tupleAccess.Object];
result = queryRef[columnIndex];
}
- if (fixBooleanExpressions && IsBooleanExpression(tupleAccess))
+ if (FixBooleanExpressions && IsBooleanExpression(tupleAccess))
result = booleanExpressionConverter.IntToBoolean(result);
return result;
}
@@ -464,39 +484,47 @@ private SqlExpression TryUnwrapEnum(SqlContainer container)
// Constructors
- public ExpressionProcessor(
- LambdaExpression lambda, HandlerAccessor handlers, SqlCompiler compiler, params IReadOnlyList[] sourceColumns)
+ public ExpressionProcessor(LambdaExpression lambda,
+ HandlerAccessor handlers,
+ SqlCompiler compiler,
+ in bool preferCaseOverVariant,
+ params IReadOnlyList[] sourceColumns)
{
ArgumentValidator.EnsureArgumentNotNull(lambda, "lambda");
ArgumentValidator.EnsureArgumentNotNull(handlers, "handlers");
ArgumentValidator.EnsureArgumentNotNull(sourceColumns, "sourceColumns");
+ if (lambda.Parameters.Count != sourceColumns.Length)
+ throw Exceptions.InternalError(Strings.ExParametersCountIsNotSameAsSourceColumnListsCount, OrmLog.Instance);
+ if (sourceColumns.Any(list => list.Any(c => c.IsNullReference())))
+ throw Exceptions.InternalError(Strings.ExSourceColumnListContainsNullValues, OrmLog.Instance);
+
this.compiler = compiler; // This might be null, check before use!
this.lambda = lambda;
this.sourceColumns = sourceColumns;
providerInfo = handlers.ProviderInfo;
driver = handlers.StorageDriver;
-
- fixBooleanExpressions = !providerInfo.Supports(ProviderFeatures.FullFeaturedBooleanExpressions);
- emptyStringIsNull = providerInfo.Supports(ProviderFeatures.TreatEmptyStringAsNull);
- dateTimeEmulation = providerInfo.Supports(ProviderFeatures.DateTimeEmulation);
- dateTimeOffsetEmulation = providerInfo.Supports(ProviderFeatures.DateTimeOffsetEmulation);
memberCompilerProvider = handlers.DomainHandler.GetMemberCompilerProvider();
- specialByteArrayComparison = providerInfo.ProviderName.Equals(WellKnown.Provider.Oracle);
- bindings = new HashSet();
- activeParameters = new List();
evaluator = new ExpressionEvaluator(lambda);
parameterExtractor = new ParameterExtractor(evaluator);
- if (fixBooleanExpressions)
+ options = ProcessorOptions.None;
+ if (!providerInfo.Supports(ProviderFeatures.FullFeaturedBooleanExpressions)) {
+ options |= ProcessorOptions.FixBooleanExpressions;
booleanExpressionConverter = new BooleanExpressionConverter(driver);
- if (lambda.Parameters.Count!=sourceColumns.Length)
- throw Exceptions.InternalError(Strings.ExParametersCountIsNotSameAsSourceColumnListsCount, OrmLog.Instance);
- if (sourceColumns.Any(list => list.Any(c => c.IsNullReference())))
- throw Exceptions.InternalError(Strings.ExSourceColumnListContainsNullValues, OrmLog.Instance);
- sourceMapping = new Dictionary>();
+ }
+ if (providerInfo.Supports(ProviderFeatures.TreatEmptyStringAsNull))
+ options |= ProcessorOptions.EmptyStringIsNull;
+ if (providerInfo.Supports(ProviderFeatures.DateTimeEmulation))
+ options |= ProcessorOptions.DateTimeEmulation;
+ if (providerInfo.Supports(ProviderFeatures.DateTimeOffsetEmulation))
+ options |= ProcessorOptions.DateTimeOffsetEmulation;
+ if (providerInfo.ProviderName.Equals(WellKnown.Provider.Oracle))
+ options |= ProcessorOptions.SpecialByteArrayComparison;
+ if (preferCaseOverVariant)
+ options |= ProcessorOptions.PreferCaseOverVariant;
}
}
}
diff --git a/Orm/Xtensive.Orm/Orm/Providers/PartialIndexFilterCompiler.cs b/Orm/Xtensive.Orm/Orm/Providers/PartialIndexFilterCompiler.cs
index 0d0f5ea5c5..4bf594fb14 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/PartialIndexFilterCompiler.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/PartialIndexFilterCompiler.cs
@@ -1,11 +1,12 @@
-// Copyright (C) 2013 Xtensive LLC
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2013-2024 Xtensive LLC
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
// Created: 2013.07.18
using System.Linq;
using Xtensive.Orm.Model;
+using Xtensive.Core;
using Xtensive.Sql;
using Xtensive.Sql.Dml;
using Xtensive.Sql.Model;
@@ -16,15 +17,18 @@ internal sealed class PartialIndexFilterCompiler
{
public string Compile(HandlerAccessor handlers, IndexInfo index)
{
- var table = SqlDml.TableRef(CreateStubTable(index.ReflectedType.MappingName, index.Filter.Fields.Count));
+ var filter = index.Filter;
+ var fieldsCount = filter.Fields.Count;
+
+ var table = SqlDml.TableRef(CreateStubTable(index.ReflectedType.MappingName, fieldsCount));
// Translation of ColumnRefs without alias seems broken, use original name as alias.
- var columns = index.Filter.Fields
+ var columns = filter.Fields
.Select(field => field.Column.Name)
.Select((name, i) => SqlDml.ColumnRef(table.Columns[i], name))
.Cast()
- .ToList();
+ .ToList(fieldsCount);
- var processor = new ExpressionProcessor(index.Filter.Expression, handlers, null, columns);
+ var processor = new ExpressionProcessor(filter.Expression, handlers, null, true, columns);
var fragment = SqlDml.Fragment(processor.Translate());
var result = handlers.StorageDriver.Compile(fragment).GetCommandText();
return result;
diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs
index aa00b8f55a..7fb873ee41 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2009-2020 Xtensive LLC.
+// Copyright (C) 2009-2024 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
@@ -60,10 +60,10 @@ protected SqlProvider CreateProvider(SqlSelect statement, IEnumerable name;
- protected Pair> ProcessExpression(LambdaExpression le,
+ protected Pair> ProcessExpression(LambdaExpression le, in bool preferCaseOverVariant,
params IReadOnlyList[] sourceColumns)
{
- var processor = new ExpressionProcessor(le, Handlers, this, sourceColumns);
+ var processor = new ExpressionProcessor(le, Handlers, this, preferCaseOverVariant, sourceColumns);
var result = new Pair>(
processor.Translate(), processor.GetBindings());
return result;
diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs
index 3c3547ec1f..41133eb315 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs
@@ -111,7 +111,7 @@ protected override SqlProvider VisitCalculate(CalculateProvider provider)
var sourceColumns = ExtractColumnExpressions(sqlSelect);
var allBindings = EnumerableUtils.Empty;
foreach (var column in provider.CalculatedColumns) {
- var result = ProcessExpression(column.Expression, sourceColumns);
+ var result = ProcessExpression(column.Expression, true, sourceColumns);
var predicate = result.First;
var bindings = result.Second;
if (column.Type.StripNullable()==typeof (bool))
@@ -150,7 +150,7 @@ protected override SqlProvider VisitFilter(FilterProvider provider)
var query = ExtractSqlSelect(provider, source);
var sourceColumns = ExtractColumnExpressions(query);
- var result = ProcessExpression(provider.Predicate, sourceColumns);
+ var result = ProcessExpression(provider.Predicate, true, sourceColumns);
var predicate = result.First;
var bindings = result.Second;
@@ -254,7 +254,7 @@ protected override SqlProvider VisitPredicateJoin(PredicateJoinProvider provider
var joinType = provider.JoinType==JoinType.LeftOuter ? SqlJoinType.LeftOuterJoin : SqlJoinType.InnerJoin;
- var result = ProcessExpression(provider.Predicate, leftExpressions, rightExpressions);
+ var result = ProcessExpression(provider.Predicate, false, leftExpressions, rightExpressions);
var joinExpression = result.First;
var bindings = result.Second;
diff --git a/Orm/Xtensive.Orm/Strings.Designer.cs b/Orm/Xtensive.Orm/Strings.Designer.cs
index 1d2b359c23..b81e1655a9 100644
--- a/Orm/Xtensive.Orm/Strings.Designer.cs
+++ b/Orm/Xtensive.Orm/Strings.Designer.cs
@@ -259,6 +259,15 @@ internal static string Comma {
}
}
+ ///
+ /// Looks up a localized string similar to Comparison of two entity fields is not supported..
+ ///
+ internal static string ComparisonOfTwoEntityFieldsIsNotSupported {
+ get {
+ return ResourceManager.GetString("ComparisonOfTwoEntityFieldsIsNotSupported", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to ComparisonRule({0}, {1}).
///
diff --git a/Orm/Xtensive.Orm/Strings.resx b/Orm/Xtensive.Orm/Strings.resx
index 11131ec5cc..ca63bb877b 100644
--- a/Orm/Xtensive.Orm/Strings.resx
+++ b/Orm/Xtensive.Orm/Strings.resx
@@ -3473,4 +3473,7 @@ Error: {1}
{0} expressions with constant values of {1} type are not supported.
+
+ Comparison of two entity fields is not supported.
+
\ No newline at end of file