Skip to content

Commit dc4a9e4

Browse files
committed
After merge changes
- TestHelper.AddValueRow() is for all build targets - SqlSessionHandler: shared code for async and sync Execute + renames - TemporaryTableManager: removed unused code - CommandFactory: removed commented code - DomainConfuguration: fixed bug of MaxNumberOfConditions value not being validated (added tests to make sure that validation happens) - Test for temp table popupation - Updated MaxQueryParameterCount for Oracle and PostgreSQL 9.0+
1 parent 65b45a2 commit dc4a9e4

10 files changed

Lines changed: 289 additions & 26 deletions

File tree

ChangeLog/7.2.0-Beta-1-dev.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[main] SqlInsert.Values became obsolete and no longer in use. Use ValueRows collection to add value rows
2+
[main] DomainConfiguration.MaxNumberOfConditions is introduced
3+
[main] SqlDml.Truncate() is introduced.
4+
[main] WellKnown.MaxNumberOfConditions became obsolete, use new DomainConfiguration.MaxNumberOfConditions if needed
5+
[main] Temporary tables cleanup now uses TRUNCATE instead of DELETE when possible
6+
[main] Temporary tables population is increased with multi-row inserts - 256 and 16 items per INSERT
7+
[main] PersistParameterBinding has new field RowIndex, use it for multi-row inserts if needed
8+
[main] TemporaryTableDescriptor changed interface to IMultiRecordPersistDescriptor, fully compatible with IPersistDescriptor
9+
[postgresql] QueryInfo.MaxQueryParameterCount actualized, it is 65535 now
10+
[oracle] QueryInfo.MaxQueryParameterCount actualized, it is 65535 now

Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public override QueryInfo GetQueryInfo()
183183
queryInfo.ParameterPrefix = ":";
184184
queryInfo.MaxLength = DoNotKnow;
185185
queryInfo.MaxComparisonOperations = DoNotKnow;
186-
queryInfo.MaxQueryParameterCount = 63999;
186+
queryInfo.MaxQueryParameterCount = 65535;
187187
queryInfo.Features =
188188
QueryFeatures.NamedParameters |
189189
QueryFeatures.ParameterPrefix |
@@ -202,7 +202,8 @@ public override ServerFeatures GetServerFeatures()
202202
return ServerFeatures.Savepoints |
203203
ServerFeatures.LargeObjects |
204204
ServerFeatures.CursorParameters |
205-
ServerFeatures.MultipleResultsViaCursorParameters;
205+
ServerFeatures.MultipleResultsViaCursorParameters |
206+
ServerFeatures.TemporaryTableEmulation;
206207
}
207208

208209
public override IdentityInfo GetIdentityInfo()

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ namespace Xtensive.Sql.Drivers.PostgreSql.v9_0
1010
{
1111
internal class ServerInfoProvider : v8_4.ServerInfoProvider
1212
{
13+
public override QueryInfo GetQueryInfo()
14+
{
15+
var queryInfo = base.GetQueryInfo();
16+
queryInfo.MaxQueryParameterCount = 65535;
17+
return queryInfo;
18+
}
19+
1320
// Constructors
1421

1522
public ServerInfoProvider(SqlDriver driver)

Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ public static TimeOnly AdjustTimeOnlyForProvider(this TimeOnly origin, StoragePr
169169
var newTicks = ticks - (ticks % divider.Value);
170170
return new TimeOnly(newTicks);
171171
}
172+
#endif
172173

173174
public static void AddValueRow(this SqlInsert insert, in (SqlColumn column, SqlExpression value) first, params (SqlColumn column, SqlExpression value)[] additional)
174175
{
@@ -180,6 +181,5 @@ public static void AddValueRow(this SqlInsert insert, in (SqlColumn column, SqlE
180181
}
181182
insert.ValueRows.Add(row);
182183
}
183-
#endif
184184
}
185185
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (C) 2023 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using NUnit.Framework;
7+
8+
namespace Xtensive.Orm.Tests.Configuration
9+
{
10+
[TestFixture]
11+
public class MaxNumberOfConditionsTest
12+
{
13+
[Test]
14+
public void NegativeNumberTest()
15+
{
16+
var config = DomainConfigurationFactory.Create();
17+
config.MaxNumberOfConditions = -1;
18+
_ = Assert.Throws<InvalidOperationException>(() => config.Lock());
19+
20+
config = DomainConfigurationFactory.Create();
21+
config.MaxNumberOfConditions = -100;
22+
_ = Assert.Throws<InvalidOperationException>(() => config.Lock());
23+
}
24+
25+
[Test]
26+
public void MaxNumberIsZeroTest()
27+
{
28+
var config = DomainConfigurationFactory.Create();
29+
config.MaxNumberOfConditions = 0;
30+
_ = Assert.Throws<InvalidOperationException>(() => config.Lock());
31+
}
32+
33+
[Test]
34+
public void MaxNumberIsOneTest()
35+
{
36+
var config = DomainConfigurationFactory.Create();
37+
config.MaxNumberOfConditions = 1;
38+
_ = Assert.Throws<InvalidOperationException>(() => config.Lock());
39+
}
40+
41+
[Test]
42+
public void EdgeValuesTest()
43+
{
44+
var config = DomainConfigurationFactory.Create();
45+
config.MaxNumberOfConditions = 2;
46+
Assert.DoesNotThrow(() => config.Lock());
47+
48+
config = DomainConfigurationFactory.Create();
49+
config.MaxNumberOfConditions = 999;
50+
Assert.DoesNotThrow(() => config.Lock());
51+
}
52+
53+
[Test]
54+
public void ExceededLimit()
55+
{
56+
var config = DomainConfigurationFactory.Create();
57+
config.MaxNumberOfConditions = 1000;
58+
_ = Assert.Throws<InvalidOperationException>(() => config.Lock());
59+
}
60+
}
61+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Copyright (C) 2023 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using NUnit.Framework;
9+
using Xtensive.Core;
10+
using Xtensive.Orm.Configuration;
11+
using Xtensive.Orm.Providers;
12+
using Xtensive.Orm.Tests.Storage.TemporaryTablePopulationTestModel;
13+
14+
namespace Xtensive.Orm.Tests.Storage.TemporaryTablePopulationTestModel
15+
{
16+
[HierarchyRoot]
17+
[KeyGenerator(KeyGeneratorKind.None)]
18+
public sealed class TestEntity : Entity
19+
{
20+
[Field, Key]
21+
public int Id { get; private set; }
22+
23+
[Field(Length = 50)]
24+
public string Name { get; private set; }
25+
26+
public TestEntity(Session session, int id)
27+
: base(session, id)
28+
{
29+
Name = id.ToString();
30+
}
31+
}
32+
}
33+
34+
namespace Xtensive.Orm.Tests.Storage
35+
{
36+
[TestFixture]
37+
public class TemporaryTablePopulationTest : AutoBuildTest
38+
{
39+
protected override void CheckRequirements() =>
40+
Require.AnyFeatureSupported(ProviderFeatures.TemporaryTables | ProviderFeatures.TemporaryTableEmulation);
41+
42+
protected override DomainConfiguration BuildConfiguration()
43+
{
44+
var domainConfiguration = DomainConfigurationFactory.Create();
45+
domainConfiguration.MaxNumberOfConditions = 2; // basically force to use temp table
46+
domainConfiguration.Types.Register(typeof(TestEntity));
47+
domainConfiguration.UpgradeMode = DomainUpgradeMode.Recreate;
48+
return domainConfiguration;
49+
}
50+
51+
protected override void PopulateData()
52+
{
53+
using(var session = Domain.OpenSession())
54+
using(var tx = session.OpenTransaction()) {
55+
_ = new TestEntity(session, -300);
56+
57+
tx.Complete();
58+
}
59+
}
60+
61+
[Test]
62+
[TestCase(WellKnown.MultiRowInsertSmallBatchSize - 1)]
63+
[TestCase(WellKnown.MultiRowInsertSmallBatchSize)]
64+
[TestCase(WellKnown.MultiRowInsertSmallBatchSize + 2)]
65+
[TestCase(WellKnown.MultiRowInsertSmallBatchSize * 2 + 2)]
66+
[TestCase(WellKnown.MultiRowInsertBigBatchSize - 1)]
67+
[TestCase(WellKnown.MultiRowInsertBigBatchSize)]
68+
[TestCase(WellKnown.MultiRowInsertBigBatchSize + 2)]
69+
[TestCase(WellKnown.MultiRowInsertBigBatchSize + WellKnown.MultiRowInsertSmallBatchSize * 2)]
70+
[TestCase(WellKnown.MultiRowInsertBigBatchSize + WellKnown.MultiRowInsertSmallBatchSize * 2 + 2)]
71+
[TestCase(WellKnown.MultiRowInsertBigBatchSize * 2)]
72+
[TestCase(WellKnown.MultiRowInsertBigBatchSize * 2 + WellKnown.MultiRowInsertSmallBatchSize * 2)]
73+
[TestCase(WellKnown.MultiRowInsertBigBatchSize * 2 + WellKnown.MultiRowInsertSmallBatchSize * 2 + 2)]
74+
public void MainTest(int numOfValues)
75+
{
76+
var commands = new List<(string Command, int ParamtersCount)>();
77+
78+
var values = Enumerable.Range(0, numOfValues).ToArray(numOfValues);
79+
80+
var sessionConfig = new SessionConfiguration("DefaultNoBatching", SessionOptions.Default) { BatchSize = 1 };
81+
82+
using (var session = Domain.OpenSession(sessionConfig))
83+
using (var tx = session.OpenTransaction()) {
84+
session.Events.DbCommandExecuting += LogCommands;
85+
_ = session.Query.All<TestEntity>().Where(e => e.Id.In(values)).ToList();
86+
session.Events.DbCommandExecuting += LogCommands;
87+
}
88+
89+
var (bigBatches, smallBatches, sigleRows) = GetExpectedCommandsCount(numOfValues);
90+
91+
Assert.That(commands.Count,
92+
Is.EqualTo(bigBatches + smallBatches + sigleRows));
93+
94+
var bigBatchesCount = bigBatches;
95+
var smallBatchesCount = smallBatches;
96+
var singleRowsCount = sigleRows;
97+
98+
for(var i = 0; i < commands.Count; i++) {
99+
var command = commands[i];
100+
if (bigBatchesCount != 0) {
101+
Assert.That(command.ParamtersCount, Is.EqualTo(WellKnown.MultiRowInsertBigBatchSize));
102+
bigBatchesCount--;
103+
}
104+
else if (smallBatchesCount != 0) {
105+
Assert.That(command.ParamtersCount, Is.EqualTo(WellKnown.MultiRowInsertSmallBatchSize));
106+
smallBatchesCount--;
107+
}
108+
else {
109+
Assert.That(command.ParamtersCount, Is.EqualTo(1));
110+
}
111+
}
112+
113+
void LogCommands(object sender, DbCommandEventArgs args)
114+
{
115+
var c = args.Command;
116+
if (c.CommandText.Contains("INSERT")) {
117+
commands.Add((c.CommandText, c.Parameters.Count));
118+
}
119+
}
120+
}
121+
122+
[Test]
123+
public void BatchesParameterCountControlCompatibilityTest()
124+
{
125+
Require.AllFeaturesSupported(ProviderFeatures.DmlBatches);
126+
127+
var maxParametersCount = StorageProviderInfo.Instance.Info.MaxQueryParameterCount;
128+
if(maxParametersCount == int.MaxValue) {
129+
throw new IgnoreException("Test does not support unlimited parameter count.");
130+
}
131+
132+
var commands = new List<(string Command, int ParamtersCount)>();
133+
134+
// to not fit into one batch of statements
135+
var possibleParametersInBatch = SessionConfiguration.DefaultBatchSize * WellKnown.MultiRowInsertBigBatchSize;
136+
137+
var numOfValues = (maxParametersCount > possibleParametersInBatch)
138+
? ((possibleParametersInBatch / WellKnown.MultiRowInsertBigBatchSize) + 1) * WellKnown.MultiRowInsertBigBatchSize
139+
: ((maxParametersCount / WellKnown.MultiRowInsertBigBatchSize) + 1) * WellKnown.MultiRowInsertBigBatchSize;
140+
141+
var values = Enumerable.Range(0, numOfValues).ToArray(numOfValues);
142+
143+
using (var session = Domain.OpenSession())
144+
using (var tx = session.OpenTransaction()) {
145+
session.Events.DbCommandExecuting += LogCommands;
146+
_ = session.Query.All<TestEntity>().Where(e => e.Id.In(values)).ToList();
147+
session.Events.DbCommandExecuting += LogCommands;
148+
}
149+
150+
Assert.That(commands.Count, Is.EqualTo(2));
151+
152+
commands = new List<(string Command, int ParamtersCount)>();
153+
154+
// to not fit into one batch of statements
155+
numOfValues = (maxParametersCount > possibleParametersInBatch)
156+
? (possibleParametersInBatch / WellKnown.MultiRowInsertBigBatchSize) * WellKnown.MultiRowInsertBigBatchSize
157+
: (maxParametersCount / WellKnown.MultiRowInsertBigBatchSize) * WellKnown.MultiRowInsertBigBatchSize;
158+
values = Enumerable.Range(0, numOfValues).ToArray(numOfValues);
159+
160+
using (var session = Domain.OpenSession())
161+
using (var tx = session.OpenTransaction()) {
162+
session.Events.DbCommandExecuting += LogCommands;
163+
_ = session.Query.All<TestEntity>().Where(e => e.Id.In(values)).ToList();
164+
session.Events.DbCommandExecuting += LogCommands;
165+
}
166+
167+
Assert.That(commands.Count, Is.EqualTo(1));
168+
169+
void LogCommands(object sender, DbCommandEventArgs args)
170+
{
171+
var c = args.Command;
172+
if (c.CommandText.Contains("INSERT")) {
173+
commands.Add((c.CommandText, c.Parameters.Count));
174+
}
175+
}
176+
}
177+
178+
private (int bigBatches, int smallBatches, int sigleRows) GetExpectedCommandsCount(int numberOfValues)
179+
{
180+
var numberOfBigs = numberOfValues / WellKnown.MultiRowInsertBigBatchSize;
181+
var valuesLeft = numberOfValues - numberOfBigs * WellKnown.MultiRowInsertBigBatchSize;
182+
var numberOfSmalls = valuesLeft / WellKnown.MultiRowInsertSmallBatchSize;
183+
valuesLeft = valuesLeft - numberOfSmalls * WellKnown.MultiRowInsertSmallBatchSize;
184+
return (numberOfBigs, numberOfSmalls, valuesLeft);
185+
}
186+
}
187+
}

Orm/Xtensive.Orm/Orm/Configuration/DomainConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ public override void Lock(bool recursive)
692692
// because override sequence of Lock() is so broken.
693693
ValidateMappingConfiguration(multischema, multidatabase);
694694
ValidateIgnoreConfiguration();
695+
ValidateMaxNumberOfConditions();
695696

696697
types.Lock(true);
697698
sessions.Lock(true);

Orm/Xtensive.Orm/Orm/Providers/CommandProcessing/CommandFactory.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,10 @@ private static object GetParameterValue(QueryParameterBinding binding, Parameter
201201
private object GetParameterValue(SqlPersistTask task, PersistParameterBinding binding)
202202
{
203203
switch (binding.BindingType) {
204-
case PersistParameterBindingType.Regular when task.Tuple!=null:
204+
case PersistParameterBindingType.Regular when task.Tuple != null:
205205
return task.Tuple.GetValueOrDefault(binding.FieldIndex);
206-
case PersistParameterBindingType.Regular when task.Tuples!=null: {
207-
//tupleSize = task.Tuples[0].Count;
208-
//var columnIndex2 = binding.FieldIndex;
209-
//tupleIndex = Math.DivRem(binding.FieldIndex, tupleSize, out var columnIndex1);
206+
case PersistParameterBindingType.Regular when task.Tuples != null:
210207
return task.Tuples[binding.RowIndex].GetValueOrDefault(binding.FieldIndex);
211-
}
212208
case PersistParameterBindingType.VersionFilter:
213209
return task.OriginalTuple.GetValueOrDefault(binding.FieldIndex);
214210
default:

0 commit comments

Comments
 (0)