Skip to content

Commit 4765aa1

Browse files
Merge upstream (#347)
* AutoBuildTest: Introduce GlobalSession/GlobalTransaction for read-only tests * More tests use global session * AutoBuildTest: Fixed error message on attempt to access uninitialized global session * Move test to correct namespace * Add tests that checks Validator results * Explicitlly set KeyGeneratorKind.None instead of implicit conversion * Write warning to build log about change of generator kind * caviar->caveat in comments * Improve changelog * Fix misspelling of "Length" * Postgresql: Declare the same setting for nulls in indexes as in order by statement otherwise, index order conflicts with order by which cause longer query execution. According to documentation default settings for nulls are exact opposite. * Fix misspelling of "Length" part 2 * Improve changelog * Bump version ot 7.1.4 * Fix warnings after merge --------- Co-authored-by: Alexey Kulakov <[email protected]>
1 parent b7d08d1 commit 4765aa1

26 files changed

+3883
-542
lines changed

ChangeLog/7.1.4_Z_Final.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[main] Addressed IndexOutOfRangeException on translation of certain queries with aggregates
2+
[main] Notification to BuildLog when a hierarchy changes KeyGeneratorKind value to None
3+
[postgresql] Added explicit nulls setting for both column order directions to improve OrderBy/OrderByDescending performance

ChangeLog/7.1.4_dev.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v9_0/Translator.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public override void Translate(SqlCompilerContext context, object literalValue)
3030
}
3131
}
3232

33+
public override void TranslateSortOrder(IOutput output, bool ascending) =>
34+
output.Append(ascending ? "ASC NULLS FIRST" : "DESC NULLS LAST");
35+
3336
// Constructors
3437

3538
public Translator(SqlDriver driver)

Orm/Xtensive.Orm.Tests.Framework/AutoBuildTest.cs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,26 @@ namespace Xtensive.Orm.Tests
1818
public abstract class AutoBuildTest : HasConfigurationAccessTest
1919
{
2020
private const string ErrorInTestFixtureSetup = "Error in TestFixtureSetUp:\r\n{0}";
21+
private const string ErrorNotInitializedGlobalSession = "Set InitGlobalSession to true";
22+
2123
private DisposableSet disposables;
24+
private (Session session, TransactionScope transaction) globalSessionAndTransaction;
25+
26+
/// <summary>
27+
/// If set to <see langword="true"/>, global session and transaction will be opened
28+
/// to use one session accross all test methods.
29+
/// </summary>
30+
protected virtual bool InitGlobalSession => false;
2231

2332
protected ProviderInfo ProviderInfo { get; set; }
2433
protected Domain Domain { get; set; }
2534

35+
// Use these two for read-only tests only, don't change them, they are controlled by AutoBuildTest.
36+
// If there is need to change Session/Transactionscope or add/modify/remove entities
37+
// then open dedicated Session/TransactionScope within test
38+
protected Session GlobalSession => InitGlobalSession ? globalSessionAndTransaction.session : throw new Exception(ErrorNotInitializedGlobalSession);
39+
protected TransactionScope GlobalTransaction => InitGlobalSession ? globalSessionAndTransaction.transaction : throw new Exception(ErrorNotInitializedGlobalSession);
40+
2641
[OneTimeSetUp]
2742
public virtual void TestFixtureSetUp()
2843
{
@@ -58,10 +73,16 @@ protected void RebuildDomain()
5873

5974
var config = BuildConfiguration();
6075
Domain = BuildDomain(config);
61-
PopulateData();
62-
63-
if (Domain!=null)
76+
if (Domain != null) {
6477
ProviderInfo = Domain.StorageProviderInfo;
78+
if (InitGlobalSession) {
79+
globalSessionAndTransaction = CreateSessionAndTransaction();
80+
}
81+
}
82+
else {
83+
ProviderInfo = StorageProviderInfo.Instance.Info;
84+
}
85+
PopulateData();
6586
}
6687

6788
protected virtual void PopulateData()
@@ -74,17 +95,21 @@ protected virtual void CheckRequirements()
7495

7596
protected (Session, TransactionScope) CreateSessionAndTransaction()
7697
{
98+
var initDisposable = disposables is null;
7799
try {
78-
disposables = new DisposableSet();
100+
if (initDisposable)
101+
disposables = new DisposableSet();
79102
var session = Domain.OpenSession();
80103
var transaction = session.OpenTransaction();
81104
_ = disposables.Add(session);
82105
_ = disposables.Add(transaction);
83106
return (session, transaction);
84107
}
85108
catch {
86-
disposables.DisposeSafely();
87-
disposables = null;
109+
if (initDisposable) {
110+
disposables.DisposeSafely();
111+
disposables = null;
112+
}
88113
throw;
89114
}
90115
}

Orm/Xtensive.Orm.Tests.Framework/Internals/KeyValuePairInstanceGenerator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
1+
// Copyright (C) 2003-2010 Xtensive LLC.
22
// All rights reserved.
33
// For conditions of distribution and use, see license.
44
// Created by: Alex Yakunin
@@ -13,13 +13,13 @@ namespace Xtensive.Orm.Tests
1313
[Serializable]
1414
internal class ArrayInstanceGenerator<T>: WrappingInstanceGenerator<T[], T>
1515
{
16-
public const int ArrayLenght = 100;
16+
public const int ArrayLength = 100;
1717

1818
public override T[] GetInstance(Random random)
1919
{
20-
T[] result = new T[ArrayLenght];
20+
T[] result = new T[ArrayLength];
2121
int i = 0;
22-
foreach (T t in BaseGenerator.GetInstances(random, ArrayLenght)) {
22+
foreach (T t in BaseGenerator.GetInstances(random, ArrayLength)) {
2323
result[i] = t;
2424
i++;
2525
}

Orm/Xtensive.Orm.Tests/Issues/AggregatesRelatedIssues/IssueJira0761_ReadingAvgAndSumByDecimalField.cs

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ public class Order : Entity
164164

165165
[Field(Precision = 10, Scale = 0)]
166166
public decimal Count { get; set; }
167+
168+
public Order(Session session)
169+
: base(session)
170+
{
171+
}
167172
}
168173
}
169174

@@ -173,7 +178,7 @@ public class IssueJira0761_ReadingAvgAndSumByDecimalField : AutoBuildTest
173178
{
174179
private const int OrderCount = 100;
175180

176-
private Session globalSession;
181+
protected override bool InitGlobalSession => true;
177182

178183
protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.SqlServer | StorageProvider.PostgreSql);
179184

@@ -187,11 +192,8 @@ protected override DomainConfiguration BuildConfiguration()
187192

188193
protected override void PopulateData()
189194
{
190-
var sAndT = CreateSessionAndTransaction();
191-
globalSession = sAndT.Item1;
192-
193195
for (var i = 0; i < OrderCount; i++) {
194-
_ = new Order() {
196+
_ = new Order(GlobalSession) {
195197
Sum = (i % 2 == 0) ? 100000000000000000000000000.11m : 100000000000000000000000000.12m,
196198
Sum2 = 100000000000000000000000000.3m,
197199
Sum3 = 10000000000000000000000000.33m,
@@ -207,31 +209,31 @@ protected override void PopulateData()
207209
}
208210

209211
foreach (var i in Enumerable.Range(1, 1000)) {
210-
_ = new ValueByEntityRefCase(globalSession) {
211-
Ref = new DirectFieldValueCase(globalSession) {
212+
_ = new ValueByEntityRefCase(GlobalSession) {
213+
Ref = new DirectFieldValueCase(GlobalSession) {
212214
Accepted = 163767
213215
}
214216
};
215217

216-
_ = new KeyValueByEntityRefCase(globalSession) {
217-
Ref = new KeyExpressionCase(globalSession, 163767 + i)
218+
_ = new KeyValueByEntityRefCase(GlobalSession) {
219+
Ref = new KeyExpressionCase(GlobalSession, 163767 + i)
218220
};
219221

220-
_ = new DecimalValueStructureEntityByRefCase(globalSession) {
221-
Ref = new DecimalValueStructureCase(globalSession) {
222-
Struct = new DecimalValueStructure(globalSession) {
222+
_ = new DecimalValueStructureEntityByRefCase(GlobalSession) {
223+
Ref = new DecimalValueStructureCase(GlobalSession) {
224+
Struct = new DecimalValueStructure(GlobalSession) {
223225
Value = 163767,
224226
Code = i
225227
}
226228
}
227229
};
228230
}
229231

230-
globalSession.SaveChanges();
232+
GlobalSession.SaveChanges();
231233

232234
#if DEBUG
233235
// just to keep data in database
234-
sAndT.Item2.Complete();
236+
GlobalTransaction.Complete();
235237
#endif
236238
}
237239

@@ -240,43 +242,43 @@ protected override void PopulateData()
240242
[Test]
241243
public void AverageComplexTest()
242244
{
243-
var queryResult = globalSession.Query.All<Order>().Average(o => o.Sum);
245+
var queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum);
244246
var fraction = queryResult - Math.Floor(queryResult);
245247
Assert.That(fraction, Is.EqualTo(0.11m).Or.EqualTo(0.12m));
246248

247-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum2);
249+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum2);
248250
fraction = queryResult - Math.Floor(queryResult);
249251
Assert.That(fraction, Is.EqualTo(0.3m));
250252

251-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum3);
253+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum3);
252254
fraction = queryResult - Math.Floor(queryResult);
253255
Assert.That(fraction, Is.EqualTo(0.33m));
254256

255-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum4);
257+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum4);
256258
fraction = queryResult - Math.Floor(queryResult);
257259
Assert.That(fraction, Is.EqualTo(0.333m));
258260

259-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum5);
261+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum5);
260262
fraction = queryResult - Math.Floor(queryResult);
261263
Assert.That(fraction, Is.EqualTo(0.3333m));
262264

263-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum6);
265+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum6);
264266
fraction = queryResult - Math.Floor(queryResult);
265267
Assert.That(fraction, Is.EqualTo(0.33333m));
266268

267-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum7);
269+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum7);
268270
fraction = queryResult - Math.Floor(queryResult);
269271
Assert.That(fraction, Is.EqualTo(0.333333m));
270272

271-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum8);
273+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum8);
272274
fraction = queryResult - Math.Floor(queryResult);
273275
Assert.That(fraction, Is.EqualTo(0.3333333m));
274276

275-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum9);
277+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum9);
276278
fraction = queryResult - Math.Floor(queryResult);
277279
Assert.That(fraction, Is.EqualTo(0.33333333m));
278280

279-
queryResult = globalSession.Query.All<Order>().Average(o => o.Sum10);
281+
queryResult = GlobalSession.Query.All<Order>().Average(o => o.Sum10);
280282
fraction = queryResult - Math.Floor(queryResult);
281283
Assert.That(fraction, Is.EqualTo(0.333333333m));
282284
}
@@ -332,10 +334,10 @@ public void AvgDecimalValueStructureEntityByRefCase()
332334
[Test]
333335
public void AvgDecimalExpressionInSourceExpressionsCase()
334336
{
335-
var results = globalSession.Query.All<DirectFieldValueCase>()
337+
var results = GlobalSession.Query.All<DirectFieldValueCase>()
336338
.GroupBy(e => e.Id, e => new { Split = e.Accepted * 0.01M })
337339
.Select(g => g.Select(x => x.Split).Distinct().Average()).ToList();
338-
var localResults = globalSession.Query.All<DirectFieldValueCase>().AsEnumerable()
340+
var localResults = GlobalSession.Query.All<DirectFieldValueCase>().AsEnumerable()
339341
.GroupBy(e => e.Id, e => new { Split = e.Accepted * 0.01M })
340342
.Select(g => g.Select(x => x.Split).Distinct().Average()).ToList();
341343

@@ -345,16 +347,16 @@ public void AvgDecimalExpressionInSourceExpressionsCase()
345347

346348
private void TestAverage<TEntity>(Expression<Func<TEntity, decimal>> selector) where TEntity : Entity
347349
{
348-
var results = globalSession.Query.All<TEntity>().Average(selector);
349-
var localResults = globalSession.Query.All<TEntity>().AsEnumerable().Average(selector.Compile());
350+
var results = GlobalSession.Query.All<TEntity>().Average(selector);
351+
var localResults = GlobalSession.Query.All<TEntity>().AsEnumerable().Average(selector.Compile());
350352
Assert.That(results, Is.EqualTo(localResults), $"Failed on Average({selector})");
351353

352-
results = globalSession.Query.All<TEntity>().Select(selector).Average();
353-
localResults = globalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Average();
354+
results = GlobalSession.Query.All<TEntity>().Select(selector).Average();
355+
localResults = GlobalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Average();
354356
Assert.That(results, Is.EqualTo(localResults), $"Failed on Select({selector}).Average()");
355357

356-
results = globalSession.Query.All<TEntity>().Select(selector).Distinct().Average();
357-
localResults = globalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Distinct().Average();
358+
results = GlobalSession.Query.All<TEntity>().Select(selector).Distinct().Average();
359+
localResults = GlobalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Distinct().Average();
358360
Assert.That(results, Is.EqualTo(localResults), $"Failed on Select({selector}).Distinct().Average()");
359361
}
360362

@@ -367,44 +369,44 @@ public void SumComplexTest()
367369
{
368370
Require.ProviderIs(StorageProvider.SqlServer, " MS SQL Server has scale reduction algorithm, PgSql doesn't");
369371

370-
var queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum);
371-
var localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum);
372+
var queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum);
373+
var localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum);
372374
Assert.That(queryResult, Is.EqualTo(localResult + 3));
373375

374-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum2);
375-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum2);
376+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum2);
377+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum2);
376378
Assert.That(queryResult, Is.EqualTo(localResult + 6));
377379

378-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum3);
379-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum3);
380+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum3);
381+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum3);
380382
Assert.That(queryResult, Is.EqualTo(localResult + 0.6m));
381383

382-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum4);
383-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum4);
384+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum4);
385+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum4);
384386
Assert.That(queryResult, Is.EqualTo(localResult));
385387

386-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum5);
387-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum5);
388+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum5);
389+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum5);
388390
Assert.That(queryResult, Is.EqualTo(localResult));
389391

390-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum6);
391-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum6);
392+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum6);
393+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum6);
392394
Assert.That(queryResult, Is.EqualTo(localResult));
393395

394-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum7);
395-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum7);
396+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum7);
397+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum7);
396398
Assert.That(queryResult, Is.EqualTo(localResult));
397399

398-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum8);
399-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum8);
400+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum8);
401+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum8);
400402
Assert.That(queryResult, Is.EqualTo(localResult));
401403

402-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum9);
403-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum9);
404+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum9);
405+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum9);
404406
Assert.That(queryResult, Is.EqualTo(localResult));
405407

406-
queryResult = globalSession.Query.All<Order>().Sum(o => o.Sum10);
407-
localResult = globalSession.Query.All<Order>().ToArray().Sum(o => o.Sum10);
408+
queryResult = GlobalSession.Query.All<Order>().Sum(o => o.Sum10);
409+
localResult = GlobalSession.Query.All<Order>().ToArray().Sum(o => o.Sum10);
408410
Assert.That(queryResult, Is.EqualTo(localResult));
409411
}
410412

@@ -459,10 +461,10 @@ public void SumDecimalValueStructureEntityByRefCase()
459461
[Test]
460462
public void SumDecimalExpressionInSourceExpressionsCase()
461463
{
462-
var results = globalSession.Query.All<DirectFieldValueCase>()
464+
var results = GlobalSession.Query.All<DirectFieldValueCase>()
463465
.GroupBy(e => e.Id, e => new { Split = e.Accepted * 0.01M })
464466
.Select(g => g.Select(x => x.Split).Distinct().Sum()).ToList();
465-
var localResults = globalSession.Query.All<DirectFieldValueCase>().AsEnumerable()
467+
var localResults = GlobalSession.Query.All<DirectFieldValueCase>().AsEnumerable()
466468
.GroupBy(e => e.Id, e => new { Split = e.Accepted * 0.01M })
467469
.Select(g => g.Select(x => x.Split).Distinct().Sum()).ToList();
468470

@@ -472,16 +474,16 @@ public void SumDecimalExpressionInSourceExpressionsCase()
472474

473475
private void TestSum<TEntity>(Expression<Func<TEntity, decimal>> selector) where TEntity : Entity
474476
{
475-
var results = globalSession.Query.All<TEntity>().Sum(selector);
476-
var localResults = globalSession.Query.All<TEntity>().AsEnumerable().Sum(selector.Compile());
477+
var results = GlobalSession.Query.All<TEntity>().Sum(selector);
478+
var localResults = GlobalSession.Query.All<TEntity>().AsEnumerable().Sum(selector.Compile());
477479
Assert.That(results, Is.EqualTo(localResults), $"Failed on Sum({selector})");
478480

479-
results = globalSession.Query.All<TEntity>().Select(selector).Sum();
480-
localResults = globalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Sum();
481+
results = GlobalSession.Query.All<TEntity>().Select(selector).Sum();
482+
localResults = GlobalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Sum();
481483
Assert.That(results, Is.EqualTo(localResults), $"Failed on Select({selector}).Sum()");
482484

483-
results = globalSession.Query.All<TEntity>().Select(selector).Distinct().Sum();
484-
localResults = globalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Distinct().Sum();
485+
results = GlobalSession.Query.All<TEntity>().Select(selector).Distinct().Sum();
486+
localResults = GlobalSession.Query.All<TEntity>().AsEnumerable().Select(selector.Compile()).Distinct().Sum();
485487
Assert.That(results, Is.EqualTo(localResults), $"Failed on Select({selector}).Distinct().Sum()");
486488
}
487489

0 commit comments

Comments
 (0)