Skip to content

Commit 729b734

Browse files
authored
PERF-133: Evaluate new table-valued parameters feature for IN queries (#258)
* PERF-133: Evaluate new table-valued parameters feature for IN queries * Create SqlMetaData[] * Improve GetSqlDbType() * CreateTypesIfNotExistsAsync() * Improve GetSqlDbType() * Use WellKnownTypes * Fix empty IN list case * Fix Enum case * SqlDataRecordList * Eliminate duplicated values * Upgrade to mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04 * Improve SqlDataRecordList.ToString() * Suppress TemporaryTablePopulationTest * Refactor * Refactor * Use string constants * Fix creating _DO_List * remove PRIMARY KEY from types * Improve SqlDataRecordList.ToString() * Exclude NULL values * NULL value * Improve SqlDataRecordList * Fix computing max length * Refactor * Refactor * fix double TYpeName assignment * Refactor * Refactor Driver class * Refactor Driver class * ANE * PRIMARY KEY again * IncludeAlgorithm.TableValuedParameter * Revert Version.props
1 parent 7b3395f commit 729b734

File tree

24 files changed

+348
-215
lines changed

24 files changed

+348
-215
lines changed

.github/actions/mssql-fts/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/mssql/server:2022-CU10-ubuntu-22.04
1+
FROM mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04
22
USER root
33

44
ENV ACCEPT_EULA=Y \

Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Driver.cs

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,61 +5,48 @@
55
// Created: 2009.07.07
66

77
using System;
8+
using System.Collections.Generic;
9+
using System.Threading.Tasks;
810
using Xtensive.Sql.Compiler;
911
using Xtensive.Sql.Info;
12+
using ISqlExecutor = Xtensive.Orm.Providers.ISqlExecutor;
1013

11-
namespace Xtensive.Sql.Drivers.SqlServer.v10
14+
namespace Xtensive.Sql.Drivers.SqlServer.v10;
15+
16+
internal class Driver(CoreServerInfo coreServerInfo, ErrorMessageParser errorMessageParser, bool checkConnectionIsAlive)
17+
: v09.Driver(coreServerInfo, errorMessageParser, checkConnectionIsAlive)
1218
{
13-
internal class Driver : v09.Driver
19+
protected override SqlCompiler CreateCompiler() => new Compiler(this);
20+
protected override Model.Extractor CreateExtractor() => new Extractor(this);
21+
protected override SqlTranslator CreateTranslator() => new Translator(this);
22+
protected override Sql.TypeMapper CreateTypeMapper() => new TypeMapper(this);
23+
protected override Info.ServerInfoProvider CreateServerInfoProvider() => new ServerInfoProvider(this);
24+
25+
public override Task CreateTypesIfNotExistAsync(ISqlExecutor executor) =>
26+
executor.ExecuteNonQueryAsync($"""
27+
IF NOT EXISTS(SELECT 1 FROM sys.types WHERE name = '{TypeMapper.LongListTypeName}')
28+
CREATE TYPE [{TypeMapper.LongListTypeName}] AS TABLE ([Value] BIGINT NOT NULL PRIMARY KEY);
29+
IF NOT EXISTS(SELECT 1 FROM sys.types WHERE name = '{TypeMapper.StringListTypeName}')
30+
CREATE TYPE [{TypeMapper.StringListTypeName}] AS TABLE ([Value] NVARCHAR(256) NOT NULL PRIMARY KEY);
31+
""");
32+
33+
protected override void RegisterCustomMappings(TypeMappingRegistryBuilder builder)
1434
{
15-
protected override SqlCompiler CreateCompiler()
16-
{
17-
return new Compiler(this);
18-
}
19-
20-
protected override Model.Extractor CreateExtractor()
21-
{
22-
return new Extractor(this);
23-
}
24-
25-
protected override SqlTranslator CreateTranslator()
26-
{
27-
return new Translator(this);
28-
}
29-
30-
protected override Sql.TypeMapper CreateTypeMapper()
31-
{
32-
return new TypeMapper(this);
33-
}
34-
35-
protected override Info.ServerInfoProvider CreateServerInfoProvider()
36-
{
37-
return new ServerInfoProvider(this);
38-
}
35+
base.RegisterCustomMappings(builder);
36+
builder.Add(typeof(List<long>), null, builder.Mapper.BindLongList, null);
37+
builder.Add(typeof(List<string>), null, builder.Mapper.BindStringList, null);
3938

4039
// As far as SqlGeometry and SqlGeography have no support in .Net Standard
4140
// these two methods are useless
41+
// builder.Add(new GeometryMapper());
42+
// builder.Add(new GeographyMapper());
43+
}
4244

43-
//protected override void RegisterCustomMappings(TypeMappingRegistryBuilder builder)
44-
//{
45-
// base.RegisterCustomMappings(builder);
46-
// builder.Add(new GeometryMapper());
47-
// builder.Add(new GeographyMapper());
48-
//}
49-
50-
//protected override void RegisterCustomReverseMappings(TypeMappingRegistryBuilder builder)
51-
//{
52-
// base.RegisterCustomReverseMappings(builder);
53-
54-
// builder.AddReverse(CustomSqlType.Geometry, Type.GetType("Microsoft.SqlServer.Types.SqlGeometry, Microsoft.SqlServer.Types, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"));
55-
// builder.AddReverse(CustomSqlType.Geography, Type.GetType("Microsoft.SqlServer.Types.SqlGeography, Microsoft.SqlServer.Types, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"));
56-
//}
57-
58-
// Constructors
45+
//protected override void RegisterCustomReverseMappings(TypeMappingRegistryBuilder builder)
46+
//{
47+
// base.RegisterCustomReverseMappings(builder);
5948

60-
public Driver(CoreServerInfo coreServerInfo, ErrorMessageParser errorMessageParser, bool checkConnectionIsAlive)
61-
: base(coreServerInfo, errorMessageParser, checkConnectionIsAlive)
62-
{
63-
}
64-
}
65-
}
49+
// builder.AddReverse(CustomSqlType.Geometry, Type.GetType("Microsoft.SqlServer.Types.SqlGeometry, Microsoft.SqlServer.Types, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"));
50+
// builder.AddReverse(CustomSqlType.Geography, Type.GetType("Microsoft.SqlServer.Types.SqlGeography, Microsoft.SqlServer.Types, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"));
51+
//}
52+
}

Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ public override IndexInfo GetIndexInfo()
1919
return result;
2020
}
2121

22+
public override QueryInfo GetQueryInfo()
23+
{
24+
var info = base.GetQueryInfo();
25+
info.Features |= QueryFeatures.TableValuedParameters;
26+
return info;
27+
}
28+
2229
public override DataTypeCollection GetDataTypesInfo()
2330
{
2431
var types = base.GetDataTypesInfo();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Linq;
5+
using Microsoft.Data.SqlClient.Server;
6+
using Tuple = Xtensive.Tuples.Tuple;
7+
8+
namespace Xtensive.Sql.Drivers.SqlServer.v10;
9+
10+
public class SqlDataRecordList : List<SqlDataRecord>
11+
{
12+
public override string ToString() =>
13+
$"[{string.Join(", ", this.Select(o => o.GetValue(0) switch {
14+
string s => $"\"{s}\"",
15+
var ns => ns.ToString()
16+
}))}]";
17+
18+
public SqlDataRecordList(IReadOnlyList<Tuple> tuples, SqlDbType sqlDbType)
19+
: base(tuples.Count)
20+
{
21+
SqlMetaData[] metaDatas = null;
22+
HashSet<object> added = new();
23+
foreach (var valueObj in tuples.Select(t => t.GetValueOrDefault(0)).Where(o => o != null)) {
24+
metaDatas ??= [
25+
sqlDbType == SqlDbType.BigInt
26+
? new SqlMetaData("Value", sqlDbType)
27+
: new SqlMetaData("Value", sqlDbType, tuples.Max(t => (t.GetValueOrDefault(0) as string)?.Length ?? 20))
28+
];
29+
var castValue = valueObj switch {
30+
byte n => (long) n,
31+
short n => (long) n,
32+
ushort n => (long) n,
33+
int n => (long) n,
34+
uint n => (long) n,
35+
decimal d => (long) d,
36+
Enum e => Convert.ToInt64(e),
37+
var o => o
38+
};
39+
if (added.Add(castValue)) {
40+
SqlDataRecord record = new(metaDatas);
41+
record.SetValue(0, castValue);
42+
Add(record);
43+
}
44+
}
45+
}
46+
}

Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/TypeMapper.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
// Created: 2009.07.02
66

77
using System;
8+
using System.Collections.Generic;
89
using System.Data;
910
using System.Data.Common;
11+
using Microsoft.Data.SqlClient;
12+
using Tuple = Xtensive.Tuples.Tuple;
1013

1114
namespace Xtensive.Sql.Drivers.SqlServer.v10
1215
{
13-
internal class TypeMapper : v09.TypeMapper
16+
internal class TypeMapper(SqlDriver driver) : v09.TypeMapper(driver)
1417
{
18+
public const string
19+
LongListTypeName = "_DO_LongList",
20+
StringListTypeName = "_DO_StringList";
21+
1522
public override void BindDateTime(DbParameter parameter, object value)
1623
{
1724
parameter.DbType = DbType.DateTime2;
@@ -28,11 +35,15 @@ public override DateTime ReadDateTime(DbDataReader reader, int index)
2835
return base.ReadDateTime(reader, index);
2936
}
3037

31-
// Constructors
32-
33-
public TypeMapper(SqlDriver driver)
34-
: base(driver)
38+
private static void BindList(DbParameter parameter, object value, SqlDbType sqlDbType)
3539
{
40+
var sqlParameter = (SqlParameter) parameter;
41+
sqlParameter.SqlDbType = SqlDbType.Structured;
42+
sqlParameter.Value = new SqlDataRecordList((List<Tuple>) value, sqlDbType) switch { var o => o.Count == 0 ? null : o };
43+
sqlParameter.TypeName = sqlDbType == SqlDbType.BigInt ? LongListTypeName : StringListTypeName;
3644
}
45+
46+
public override void BindLongList(DbParameter parameter, object value) => BindList(parameter, value, SqlDbType.BigInt);
47+
public override void BindStringList(DbParameter parameter, object value) => BindList(parameter, value, SqlDbType.NVarChar);
3748
}
38-
}
49+
}

Orm/Xtensive.Orm.Tests/Linq/InTest.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ public void MartinTest()
8282
.ToList();
8383
}
8484

85+
[Test]
86+
public void InTest1000()
87+
{
88+
var someInvoiceId = Session.Query.All<Invoice>().First().InvoiceId;
89+
var list = Enumerable.Range(someInvoiceId - 500, 1000).ToList();
90+
var count = (from invoice in Session.Query.All<Invoice>()
91+
where invoice.InvoiceId.In(list)
92+
select invoice).Count();
93+
Assert.Greater(count, 0);
94+
}
95+
8596
[Test]
8697
public void LongSequenceIntTest()
8798
{

Orm/Xtensive.Orm.Tests/Storage/TemporaryTablePopulationTest.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ namespace Xtensive.Orm.Tests.Storage
3636
[TestFixture]
3737
public class TemporaryTablePopulationTest : AutoBuildTest
3838
{
39-
protected override void CheckRequirements() =>
39+
protected override void CheckRequirements()
40+
{
4041
Require.AnyFeatureSupported(ProviderFeatures.TemporaryTables | ProviderFeatures.TemporaryTableEmulation);
42+
Require.AnyFeatureNotSupported(ProviderFeatures.TableValuedParameters);
43+
}
4144

4245
protected override DomainConfiguration BuildConfiguration()
4346
{

Orm/Xtensive.Orm/Orm/IncludeAlgorithm.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,9 @@ public enum IncludeAlgorithm
2323
/// Inclusion is described via temporary table.
2424
/// </summary>
2525
TemporaryTable = 2,
26+
/// <summary>
27+
/// Inclusion is described via Table-Valued Parameter.
28+
/// </summary>
29+
TableValuedParameter = 3,
2630
}
27-
}
31+
}

0 commit comments

Comments
 (0)