Skip to content

Commit 950e833

Browse files
authored
PERF-281: Optimize SqlQueryStatement (#387)
1 parent 766da33 commit 950e833

File tree

14 files changed

+61
-70
lines changed

14 files changed

+61
-70
lines changed

Extensions/Xtensive.Orm.BulkOperations/Internals/BaseSqlVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,14 +460,14 @@ public virtual void Visit(SqlSelect node)
460460
foreach (SqlColumn column in node.Columns)
461461
VisitInternal(column);
462462
VisitInternal(node.From);
463-
foreach (SqlColumn column in node.GroupBy)
463+
foreach (SqlColumn column in node.GroupByReadOnly)
464464
VisitInternal(column);
465465
VisitInternal(node.Having);
466466
foreach (SqlHint hint in node.Hints)
467467
VisitInternal(hint);
468468
VisitInternal(node.Limit);
469469
VisitInternal(node.Offset);
470-
foreach (SqlOrder order in node.OrderBy)
470+
foreach (SqlOrder order in node.OrderByReadOnly)
471471
VisitInternal(order);
472472
VisitInternal(node.Where);
473473
}

Orm/Xtensive.Orm.Tests.Sql/CloneTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ public void SqlSelectCloneTest()
360360
s.Columns.Add(tr2.Asterisk);
361361
s.From = tr1.InnerJoin(tr2, tr1["ID"]==tr2["ID"]);
362362
s.Where = SqlDml.Like(tr1["Name"], "Marat");
363-
s.Hints.Add(SqlDml.FastFirstRowsHint(10));
363+
s.AddHint(SqlDml.FastFirstRowsHint(10));
364364

365365
SqlSelect sClone = (SqlSelect)s.Clone();
366366

@@ -544,7 +544,7 @@ public void SqlDeleteCloneTest()
544544
SqlTableRef t = SqlDml.TableRef(table1);
545545
SqlDelete d = SqlDml.Delete(t);
546546
d.Where = t[0] < 6;
547-
d.Hints.Add(SqlDml.FastFirstRowsHint(10));
547+
d.AddHint(SqlDml.FastFirstRowsHint(10));
548548
SqlDelete dClone = (SqlDelete) d.Clone();
549549

550550
Assert.AreNotEqual(d, dClone);
@@ -599,7 +599,7 @@ public void SqlInsertCloneTest()
599599
SqlTableRef t = SqlDml.TableRef(table1);
600600
SqlInsert i = SqlDml.Insert(t);
601601
i.AddValueRow(( t[0], 1 ), ( t[1], "Anonym" ));
602-
i.Hints.Add(SqlDml.FastFirstRowsHint(10));
602+
i.AddHint(SqlDml.FastFirstRowsHint(10));
603603
SqlInsert iClone = (SqlInsert)i.Clone();
604604

605605
Assert.AreNotEqual(i, iClone);
@@ -623,7 +623,7 @@ public void SqlUpdateCloneTest()
623623
u.Values[t[0]] = 1;
624624
u.Values[t[1]] = "Anonym";
625625
u.Where = t.Columns["ID"]==1;
626-
u.Hints.Add(SqlDml.FastFirstRowsHint(10));
626+
u.AddHint(SqlDml.FastFirstRowsHint(10));
627627
SqlUpdate uClone = (SqlUpdate)u.Clone();
628628

629629
Assert.AreNotEqual(u, uClone);

Orm/Xtensive.Orm.Tests.Sql/MySQL/SakilaExtractorTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2011-2020 Xtensive LLC.
1+
// Copyright (C) 2011-2020 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Malisa Ncube
@@ -986,7 +986,7 @@ USE INDEX(idx_last_name)
986986
select.Columns.AddRange(c["customer_id"], c["store_id"], c["first_name"], c["last_name"], c["email"]);
987987
select.Where = c["last_name"]>"JOHNSON";
988988

989-
select.Hints.Add(SqlDml.NativeHint("idx_last_name"));
989+
select.AddHint(SqlDml.NativeHint("idx_last_name"));
990990

991991
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
992992
}

Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3805,9 +3805,9 @@ public void Test197()
38053805
var select = SqlDml.Select(abcd);
38063806
select.Limit = 10;
38073807
select.Columns.Add(SqlDml.Asterisk);
3808-
select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Hash, ab));
3809-
select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Merge, cd));
3810-
select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
3808+
select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Hash, ab));
3809+
select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Merge, cd));
3810+
select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
38113811

38123812

38133813
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
@@ -3836,9 +3836,9 @@ public void Test198()
38363836
var select = SqlDml.Select(abcd);
38373837
select.Limit = 10;
38383838
select.Columns.Add(SqlDml.Asterisk);
3839-
select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Hash, b));
3840-
select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Merge, d));
3841-
select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
3839+
select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Hash, b));
3840+
select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Merge, d));
3841+
select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
38423842

38433843
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
38443844
}
@@ -3874,8 +3874,8 @@ public void Test200()
38743874
select.Limit = 10;
38753875
select.Columns.Add(c["EmailAddress"]);
38763876
select.Where = SqlDml.Like(c["EmailAddress"], "a%");
3877-
select.Hints.Add(SqlDml.FastFirstRowsHint(10));
3878-
select.Hints.Add(SqlDml.NativeHint("KEEP PLAN, ROBUST PLAN"));
3877+
select.AddHint(SqlDml.FastFirstRowsHint(10));
3878+
select.AddHint(SqlDml.NativeHint("KEEP PLAN, ROBUST PLAN"));
38793879
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
38803880
}
38813881

@@ -4046,7 +4046,7 @@ public void Test208()
40464046
outerSelect.Columns.Add(categoryName, "SubcategoryName");
40474047

40484048
outerSelect.Lock = SqlLockType.Exclusive;
4049-
outerSelect.Hints.Add(new SqlIndexHint("IndexName", subcategories));
4049+
outerSelect.AddHint(new SqlIndexHint("IndexName", subcategories));
40504050

40514051
Assert.IsTrue(CompareExecuteDataReader(nativeSql, outerSelect));
40524052
}

Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,8 @@ internal protected override SqlProvider VisitJoin(JoinProvider provider)
231231
query.Columns.AddRange(joinedTable.AliasedColumns);
232232
query.Comment = SqlComment.Join(left.Request.Statement.Comment, right.Request.Statement.Comment);
233233

234-
foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints))
235-
{
236-
query.Hints.Add(sqlHint);
234+
foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints)) {
235+
query.AddHint(sqlHint);
237236
}
238237
return CreateProvider(query, provider, left, right);
239238
}
@@ -288,10 +287,9 @@ internal protected override SqlProvider VisitPredicateJoin(PredicateJoinProvider
288287
query.Where &= right.Request.Statement.Where;
289288
query.Columns.AddRange(joinedTable.AliasedColumns);
290289
query.Comment = SqlComment.Join(left.Request.Statement.Comment, right.Request.Statement.Comment);
291-
292-
foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints))
293-
{
294-
query.Hints.Add(sqlHint);
290+
291+
foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints)) {
292+
query.AddHint(sqlHint);
295293
}
296294
return CreateProvider(query, bindings, provider, left, right);
297295
}
@@ -372,7 +370,7 @@ internal protected override SqlProvider VisitIndexHint(IndexHintProvider provide
372370

373371
var query = compiledSource.Request.Statement;
374372
var indexName = index.MappingName;
375-
query.Hints.Add(new SqlIndexHint(indexName, tableRef));
373+
query.AddHint(new SqlIndexHint(indexName, tableRef));
376374

377375
return CreateProvider(query, provider, compiledSource);
378376
}

Orm/Xtensive.Orm/Orm/Providers/SqlSelectProcessor.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,9 @@ public void Visit(SqlSelect node)
435435
{
436436
foreach (var column in node.Columns)
437437
Visit(column);
438-
foreach (var column in node.GroupBy)
438+
foreach (var column in node.GroupByReadOnly)
439439
Visit(column);
440-
foreach (var column in node.OrderBy)
440+
foreach (var column in node.OrderByReadOnly)
441441
Visit(column);
442442
if (node.From != null)
443443
Visit(node.From);
@@ -455,7 +455,7 @@ public void Visit(SqlSelect node)
455455

456456
var isCurrentRoot = ReferenceEquals(node, rootSelect);
457457
var keepOrderBy = isCurrentRoot || hasPaging;
458-
if (!keepOrderBy)
458+
if (!keepOrderBy && node.OrderByReadOnly.Count > 0)
459459
node.OrderBy.Clear();
460460

461461
if (!isCurrentRoot) {
@@ -464,7 +464,7 @@ public void Visit(SqlSelect node)
464464
}
465465

466466
var addOrderBy = hasPaging
467-
&& node.OrderBy.Count==0
467+
&& node.OrderByReadOnly.Count==0
468468
&& providerInfo.Supports(ProviderFeatures.PagingRequiresOrderBy);
469469

470470
if (addOrderBy)
@@ -596,4 +596,4 @@ private SqlSelectProcessor(SqlSelect rootSelect, ProviderInfo providerInfo)
596596
this.providerInfo = providerInfo;
597597
}
598598
}
599-
}
599+
}

Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,13 +1687,13 @@ protected virtual void VisitSelectWhere(SqlSelect node)
16871687
/// <param name="node">Statement to visit.</param>
16881688
protected virtual void VisitSelectGroupBy(SqlSelect node)
16891689
{
1690-
if (node.GroupBy.Count <= 0) {
1690+
if (node.GroupByReadOnly.Count <= 0) {
16911691
return;
16921692
}
16931693
// group by
16941694
translator.SelectGroupBy(context, node);
16951695
using (context.EnterCollectionScope()) {
1696-
foreach (var item in node.GroupBy) {
1696+
foreach (var item in node.GroupByReadOnly) {
16971697
AppendCollectionDelimiterIfNecessary(AppendColumnDelimiter);
16981698
var cr = item as SqlColumnRef;
16991699
if (cr is not null) {
@@ -1718,13 +1718,13 @@ protected virtual void VisitSelectGroupBy(SqlSelect node)
17181718
/// <param name="node">Statement to visit.</param>
17191719
protected virtual void VisitSelectOrderBy(SqlSelect node)
17201720
{
1721-
if (node.OrderBy.Count <= 0) {
1721+
if (node.OrderByReadOnly.Count <= 0) {
17221722
return;
17231723
}
17241724

17251725
translator.SelectOrderBy(context, node);
17261726
using (context.EnterCollectionScope()) {
1727-
foreach (var item in node.OrderBy) {
1727+
foreach (var item in node.OrderByReadOnly) {
17281728
AppendCollectionDelimiterIfNecessary(AppendColumnDelimiter);
17291729
item.AcceptVisitor(this);
17301730
}

Orm/Xtensive.Orm/Sql/Dml/SqlLockType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace Xtensive.Sql.Dml
1010
{
1111
[Flags]
12-
public enum SqlLockType
12+
public enum SqlLockType : byte
1313
{
1414
Empty = 0,
1515
Shared = 1,
@@ -18,4 +18,4 @@ public enum SqlLockType
1818
ThrowIfLocked = 8,
1919
SkipLocked = 16,
2020
}
21-
}
21+
}

Orm/Xtensive.Orm/Sql/Dml/Statements/SqlDelete.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ internal override SqlDelete Clone(SqlNodeCloneContext context) =>
7272

7373
if (t.Hints.Count > 0)
7474
foreach (SqlHint hint in t.Hints)
75-
clone.Hints.Add(hint.Clone(c));
75+
clone.AddHint(hint.Clone(c));
7676

7777
return clone;
7878
});

Orm/Xtensive.Orm/Sql/Dml/Statements/SqlInsert.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ internal override SqlInsert Clone(SqlNodeCloneContext context) =>
4747

4848
if (t.Hints.Count > 0) {
4949
foreach (SqlHint hint in t.Hints)
50-
clone.Hints.Add(hint.Clone(c));
50+
clone.AddHint(hint.Clone(c));
5151
}
5252
return clone;
5353
});

Orm/Xtensive.Orm/Sql/Dml/Statements/SqlQueryStatement.cs

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,21 @@
22
// All rights reserved.
33
// For conditions of distribution and use, see license.
44

5-
using System;
6-
using System.Collections.Generic;
7-
using System.Collections.ObjectModel;
5+
namespace Xtensive.Sql.Dml;
86

9-
namespace Xtensive.Sql.Dml
7+
/// <summary>
8+
/// Base class for DML statements.
9+
/// </summary>
10+
[Serializable]
11+
public abstract class SqlQueryStatement(SqlNodeType nodeType) : SqlStatement(nodeType)
1012
{
13+
private List<SqlHint> hints;
14+
1115
/// <summary>
12-
/// Base class for DML statements.
16+
/// Gets the collection of join hints.
1317
/// </summary>
14-
[Serializable]
15-
public abstract class SqlQueryStatement : SqlStatement
16-
{
17-
private IList<SqlHint> hints;
18-
19-
/// <summary>
20-
/// Gets the collection of join hints.
21-
/// </summary>
22-
/// <value>The collection of join hints.</value>
23-
public IList<SqlHint> Hints => hints ??= new Collection<SqlHint>();
24-
25-
// Constructors
18+
/// <value>The collection of join hints.</value>
19+
public IReadOnlyList<SqlHint> Hints => hints ?? [];
2620

27-
protected SqlQueryStatement(SqlNodeType nodeType) : base(nodeType)
28-
{
29-
}
30-
}
21+
public void AddHint(SqlHint hint) => (hints ??= new(1)).Add(hint);
3122
}

Orm/Xtensive.Orm/Sql/Dml/Statements/SqlSelect.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ public SqlExpression Where
6161
get { return where; }
6262
set
6363
{
64-
if (value is not null)
65-
SqlValidator.EnsureIsBooleanExpression(value);
64+
SqlValidator.EnsureIsBooleanExpression(value);
6665
where = value;
6766
}
6867
}
@@ -73,6 +72,8 @@ public SqlExpression Where
7372
/// <value>The collection of columns.</value>
7473
public SqlColumnCollection GroupBy => groupBy ??= new();
7574

75+
public IReadOnlyList<SqlColumn> GroupByReadOnly => groupBy ??= [];
76+
7677
/// <summary>
7778
/// Gets or sets the having clause.
7879
/// </summary>
@@ -82,8 +83,7 @@ public SqlExpression Having
8283
get { return having; }
8384
set
8485
{
85-
if (value is not null)
86-
SqlValidator.EnsureIsBooleanExpression(value);
86+
SqlValidator.EnsureIsBooleanExpression(value);
8787
having = value;
8888
}
8989
}
@@ -94,6 +94,8 @@ public SqlExpression Having
9494
/// <value>The order by clause.</value>
9595
public SqlOrderCollection OrderBy => orderBy ??= new();
9696

97+
public IReadOnlyList<SqlOrder> OrderByReadOnly => orderBy ??= [];
98+
9799
/// <summary>
98100
/// Gets or sets a value indicating whether this <see cref="SqlSelect"/> is distinct.
99101
/// </summary>
@@ -168,7 +170,7 @@ internal override SqlSelect Clone(SqlNodeCloneContext context) =>
168170

169171
if (t.Hints.Count > 0)
170172
foreach (SqlHint hint in t.Hints)
171-
clone.Hints.Add(hint.Clone(c));
173+
clone.AddHint(hint.Clone(c));
172174

173175
return clone;
174176
});
@@ -183,14 +185,14 @@ public SqlSelect ShallowClone()
183185
: SqlDml.Select(From);
184186
result.Columns.AddRange(Columns);
185187
result.Distinct = Distinct;
186-
result.GroupBy.AddRange(GroupBy);
188+
result.GroupBy.AddRange(GroupByReadOnly);
187189
result.Having = Having;
188190
result.Offset = Offset;
189191
result.Limit = Limit;
190-
foreach (var order in OrderBy)
192+
foreach (var order in OrderByReadOnly)
191193
result.OrderBy.Add(order);
192194
foreach (var hint in Hints)
193-
result.Hints.Add(hint);
195+
result.AddHint(hint);
194196
result.Where = Where;
195197
result.Lock = Lock;
196198
result.Comment = Comment;

Orm/Xtensive.Orm/Sql/Dml/Statements/SqlUpdate.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ internal override SqlUpdate Clone(SqlNodeCloneContext context) =>
8383
clone.Limit = t.where.Clone(c);
8484
if (t.Hints.Count > 0)
8585
foreach (SqlHint hint in t.Hints)
86-
clone.Hints.Add(hint.Clone(c));
86+
clone.AddHint(hint.Clone(c));
8787

8888
return clone;
8989
});

Orm/Xtensive.Orm/Sql/SqlNodeType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace Xtensive.Sql
88
{
99
[Serializable]
10-
public enum SqlNodeType
10+
public enum SqlNodeType : byte
1111
{
1212
Action,
1313
Add,
@@ -117,4 +117,4 @@ public enum SqlNodeType
117117
Fragment,
118118
Metadata,
119119
}
120-
}
120+
}

0 commit comments

Comments
 (0)