diff --git a/Extensions/Xtensive.Orm.BulkOperations/Internals/BaseSqlVisitor.cs b/Extensions/Xtensive.Orm.BulkOperations/Internals/BaseSqlVisitor.cs
index 66ff0ddcb1..f91308b9a8 100644
--- a/Extensions/Xtensive.Orm.BulkOperations/Internals/BaseSqlVisitor.cs
+++ b/Extensions/Xtensive.Orm.BulkOperations/Internals/BaseSqlVisitor.cs
@@ -460,14 +460,14 @@ public virtual void Visit(SqlSelect node)
foreach (SqlColumn column in node.Columns)
VisitInternal(column);
VisitInternal(node.From);
- foreach (SqlColumn column in node.GroupBy)
+ foreach (SqlColumn column in node.GroupByReadOnly)
VisitInternal(column);
VisitInternal(node.Having);
foreach (SqlHint hint in node.Hints)
VisitInternal(hint);
VisitInternal(node.Limit);
VisitInternal(node.Offset);
- foreach (SqlOrder order in node.OrderBy)
+ foreach (SqlOrder order in node.OrderByReadOnly)
VisitInternal(order);
VisitInternal(node.Where);
}
diff --git a/Orm/Xtensive.Orm.Tests.Sql/CloneTests.cs b/Orm/Xtensive.Orm.Tests.Sql/CloneTests.cs
index 7afe4f6351..fedf442437 100644
--- a/Orm/Xtensive.Orm.Tests.Sql/CloneTests.cs
+++ b/Orm/Xtensive.Orm.Tests.Sql/CloneTests.cs
@@ -360,7 +360,7 @@ public void SqlSelectCloneTest()
s.Columns.Add(tr2.Asterisk);
s.From = tr1.InnerJoin(tr2, tr1["ID"]==tr2["ID"]);
s.Where = SqlDml.Like(tr1["Name"], "Marat");
- s.Hints.Add(SqlDml.FastFirstRowsHint(10));
+ s.AddHint(SqlDml.FastFirstRowsHint(10));
SqlSelect sClone = (SqlSelect)s.Clone();
@@ -544,7 +544,7 @@ public void SqlDeleteCloneTest()
SqlTableRef t = SqlDml.TableRef(table1);
SqlDelete d = SqlDml.Delete(t);
d.Where = t[0] < 6;
- d.Hints.Add(SqlDml.FastFirstRowsHint(10));
+ d.AddHint(SqlDml.FastFirstRowsHint(10));
SqlDelete dClone = (SqlDelete) d.Clone();
Assert.AreNotEqual(d, dClone);
@@ -599,7 +599,7 @@ public void SqlInsertCloneTest()
SqlTableRef t = SqlDml.TableRef(table1);
SqlInsert i = SqlDml.Insert(t);
i.AddValueRow(( t[0], 1 ), ( t[1], "Anonym" ));
- i.Hints.Add(SqlDml.FastFirstRowsHint(10));
+ i.AddHint(SqlDml.FastFirstRowsHint(10));
SqlInsert iClone = (SqlInsert)i.Clone();
Assert.AreNotEqual(i, iClone);
@@ -623,7 +623,7 @@ public void SqlUpdateCloneTest()
u.Values[t[0]] = 1;
u.Values[t[1]] = "Anonym";
u.Where = t.Columns["ID"]==1;
- u.Hints.Add(SqlDml.FastFirstRowsHint(10));
+ u.AddHint(SqlDml.FastFirstRowsHint(10));
SqlUpdate uClone = (SqlUpdate)u.Clone();
Assert.AreNotEqual(u, uClone);
diff --git a/Orm/Xtensive.Orm.Tests.Sql/MySQL/SakilaExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/MySQL/SakilaExtractorTest.cs
index 1edcd2ff6f..0ecd5aea32 100644
--- a/Orm/Xtensive.Orm.Tests.Sql/MySQL/SakilaExtractorTest.cs
+++ b/Orm/Xtensive.Orm.Tests.Sql/MySQL/SakilaExtractorTest.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2020 Xtensive LLC.
+// Copyright (C) 2011-2020 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Malisa Ncube
@@ -986,7 +986,7 @@ USE INDEX(idx_last_name)
select.Columns.AddRange(c["customer_id"], c["store_id"], c["first_name"], c["last_name"], c["email"]);
select.Where = c["last_name"]>"JOHNSON";
- select.Hints.Add(SqlDml.NativeHint("idx_last_name"));
+ select.AddHint(SqlDml.NativeHint("idx_last_name"));
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
}
diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLTests.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLTests.cs
index 8ae805a714..414a25bb55 100644
--- a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLTests.cs
+++ b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLTests.cs
@@ -3805,9 +3805,9 @@ public void Test197()
var select = SqlDml.Select(abcd);
select.Limit = 10;
select.Columns.Add(SqlDml.Asterisk);
- select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Hash, ab));
- select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Merge, cd));
- select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
+ select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Hash, ab));
+ select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Merge, cd));
+ select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
@@ -3836,9 +3836,9 @@ public void Test198()
var select = SqlDml.Select(abcd);
select.Limit = 10;
select.Columns.Add(SqlDml.Asterisk);
- select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Hash, b));
- select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Merge, d));
- select.Hints.Add(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
+ select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Hash, b));
+ select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Merge, d));
+ select.AddHint(SqlDml.JoinHint(SqlJoinMethod.Loop, abcd));
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
}
@@ -3874,8 +3874,8 @@ public void Test200()
select.Limit = 10;
select.Columns.Add(c["EmailAddress"]);
select.Where = SqlDml.Like(c["EmailAddress"], "a%");
- select.Hints.Add(SqlDml.FastFirstRowsHint(10));
- select.Hints.Add(SqlDml.NativeHint("KEEP PLAN, ROBUST PLAN"));
+ select.AddHint(SqlDml.FastFirstRowsHint(10));
+ select.AddHint(SqlDml.NativeHint("KEEP PLAN, ROBUST PLAN"));
Assert.IsTrue(CompareExecuteDataReader(nativeSql, select));
}
@@ -4046,7 +4046,7 @@ public void Test208()
outerSelect.Columns.Add(categoryName, "SubcategoryName");
outerSelect.Lock = SqlLockType.Exclusive;
- outerSelect.Hints.Add(new SqlIndexHint("IndexName", subcategories));
+ outerSelect.AddHint(new SqlIndexHint("IndexName", subcategories));
Assert.IsTrue(CompareExecuteDataReader(nativeSql, outerSelect));
}
diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs
index f430061ca7..44ba630518 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs
@@ -231,9 +231,8 @@ internal protected override SqlProvider VisitJoin(JoinProvider provider)
query.Columns.AddRange(joinedTable.AliasedColumns);
query.Comment = SqlComment.Join(left.Request.Statement.Comment, right.Request.Statement.Comment);
- foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints))
- {
- query.Hints.Add(sqlHint);
+ foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints)) {
+ query.AddHint(sqlHint);
}
return CreateProvider(query, provider, left, right);
}
@@ -288,10 +287,9 @@ internal protected override SqlProvider VisitPredicateJoin(PredicateJoinProvider
query.Where &= right.Request.Statement.Where;
query.Columns.AddRange(joinedTable.AliasedColumns);
query.Comment = SqlComment.Join(left.Request.Statement.Comment, right.Request.Statement.Comment);
-
- foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints))
- {
- query.Hints.Add(sqlHint);
+
+ foreach (var sqlHint in left.Request.Statement.Hints.Concat(right.Request.Statement.Hints)) {
+ query.AddHint(sqlHint);
}
return CreateProvider(query, bindings, provider, left, right);
}
@@ -372,7 +370,7 @@ internal protected override SqlProvider VisitIndexHint(IndexHintProvider provide
var query = compiledSource.Request.Statement;
var indexName = index.MappingName;
- query.Hints.Add(new SqlIndexHint(indexName, tableRef));
+ query.AddHint(new SqlIndexHint(indexName, tableRef));
return CreateProvider(query, provider, compiledSource);
}
diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlSelectProcessor.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlSelectProcessor.cs
index 4cbf68dabb..fbee7f2529 100644
--- a/Orm/Xtensive.Orm/Orm/Providers/SqlSelectProcessor.cs
+++ b/Orm/Xtensive.Orm/Orm/Providers/SqlSelectProcessor.cs
@@ -435,9 +435,9 @@ public void Visit(SqlSelect node)
{
foreach (var column in node.Columns)
Visit(column);
- foreach (var column in node.GroupBy)
+ foreach (var column in node.GroupByReadOnly)
Visit(column);
- foreach (var column in node.OrderBy)
+ foreach (var column in node.OrderByReadOnly)
Visit(column);
if (node.From != null)
Visit(node.From);
@@ -455,7 +455,7 @@ public void Visit(SqlSelect node)
var isCurrentRoot = ReferenceEquals(node, rootSelect);
var keepOrderBy = isCurrentRoot || hasPaging;
- if (!keepOrderBy)
+ if (!keepOrderBy && node.OrderByReadOnly.Count > 0)
node.OrderBy.Clear();
if (!isCurrentRoot) {
@@ -464,7 +464,7 @@ public void Visit(SqlSelect node)
}
var addOrderBy = hasPaging
- && node.OrderBy.Count==0
+ && node.OrderByReadOnly.Count==0
&& providerInfo.Supports(ProviderFeatures.PagingRequiresOrderBy);
if (addOrderBy)
@@ -596,4 +596,4 @@ private SqlSelectProcessor(SqlSelect rootSelect, ProviderInfo providerInfo)
this.providerInfo = providerInfo;
}
}
-}
\ No newline at end of file
+}
diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs
index dfc78a4ff9..606daca295 100644
--- a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs
+++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs
@@ -1687,13 +1687,13 @@ protected virtual void VisitSelectWhere(SqlSelect node)
/// Statement to visit.
protected virtual void VisitSelectGroupBy(SqlSelect node)
{
- if (node.GroupBy.Count <= 0) {
+ if (node.GroupByReadOnly.Count <= 0) {
return;
}
// group by
translator.SelectGroupBy(context, node);
using (context.EnterCollectionScope()) {
- foreach (var item in node.GroupBy) {
+ foreach (var item in node.GroupByReadOnly) {
AppendCollectionDelimiterIfNecessary(AppendColumnDelimiter);
var cr = item as SqlColumnRef;
if (cr is not null) {
@@ -1718,13 +1718,13 @@ protected virtual void VisitSelectGroupBy(SqlSelect node)
/// Statement to visit.
protected virtual void VisitSelectOrderBy(SqlSelect node)
{
- if (node.OrderBy.Count <= 0) {
+ if (node.OrderByReadOnly.Count <= 0) {
return;
}
translator.SelectOrderBy(context, node);
using (context.EnterCollectionScope()) {
- foreach (var item in node.OrderBy) {
+ foreach (var item in node.OrderByReadOnly) {
AppendCollectionDelimiterIfNecessary(AppendColumnDelimiter);
item.AcceptVisitor(this);
}
diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlLockType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlLockType.cs
index a97c27a46a..1740e9deba 100644
--- a/Orm/Xtensive.Orm/Sql/Dml/SqlLockType.cs
+++ b/Orm/Xtensive.Orm/Sql/Dml/SqlLockType.cs
@@ -9,7 +9,7 @@
namespace Xtensive.Sql.Dml
{
[Flags]
- public enum SqlLockType
+ public enum SqlLockType : byte
{
Empty = 0,
Shared = 1,
@@ -18,4 +18,4 @@ public enum SqlLockType
ThrowIfLocked = 8,
SkipLocked = 16,
}
-}
\ No newline at end of file
+}
diff --git a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlDelete.cs b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlDelete.cs
index 8c9504c00f..b1fe8e161c 100644
--- a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlDelete.cs
+++ b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlDelete.cs
@@ -72,7 +72,7 @@ internal override SqlDelete Clone(SqlNodeCloneContext context) =>
if (t.Hints.Count > 0)
foreach (SqlHint hint in t.Hints)
- clone.Hints.Add(hint.Clone(c));
+ clone.AddHint(hint.Clone(c));
return clone;
});
diff --git a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlInsert.cs b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlInsert.cs
index 1f997ddf81..9161afee80 100644
--- a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlInsert.cs
+++ b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlInsert.cs
@@ -47,7 +47,7 @@ internal override SqlInsert Clone(SqlNodeCloneContext context) =>
if (t.Hints.Count > 0) {
foreach (SqlHint hint in t.Hints)
- clone.Hints.Add(hint.Clone(c));
+ clone.AddHint(hint.Clone(c));
}
return clone;
});
diff --git a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlQueryStatement.cs b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlQueryStatement.cs
index 81a13268ff..3375bfed73 100644
--- a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlQueryStatement.cs
+++ b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlQueryStatement.cs
@@ -2,30 +2,21 @@
// All rights reserved.
// For conditions of distribution and use, see license.
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+namespace Xtensive.Sql.Dml;
-namespace Xtensive.Sql.Dml
+///
+/// Base class for DML statements.
+///
+[Serializable]
+public abstract class SqlQueryStatement(SqlNodeType nodeType) : SqlStatement(nodeType)
{
+ private List hints;
+
///
- /// Base class for DML statements.
+ /// Gets the collection of join hints.
///
- [Serializable]
- public abstract class SqlQueryStatement : SqlStatement
- {
- private IList hints;
-
- ///
- /// Gets the collection of join hints.
- ///
- /// The collection of join hints.
- public IList Hints => hints ??= new Collection();
-
- // Constructors
+ /// The collection of join hints.
+ public IReadOnlyList Hints => hints ?? [];
- protected SqlQueryStatement(SqlNodeType nodeType) : base(nodeType)
- {
- }
- }
+ public void AddHint(SqlHint hint) => (hints ??= new(1)).Add(hint);
}
diff --git a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlSelect.cs b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlSelect.cs
index 9294631612..3117beca9a 100644
--- a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlSelect.cs
+++ b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlSelect.cs
@@ -61,8 +61,7 @@ public SqlExpression Where
get { return where; }
set
{
- if (value is not null)
- SqlValidator.EnsureIsBooleanExpression(value);
+ SqlValidator.EnsureIsBooleanExpression(value);
where = value;
}
}
@@ -73,6 +72,8 @@ public SqlExpression Where
/// The collection of columns.
public SqlColumnCollection GroupBy => groupBy ??= new();
+ public IReadOnlyList GroupByReadOnly => groupBy ??= [];
+
///
/// Gets or sets the having clause.
///
@@ -82,8 +83,7 @@ public SqlExpression Having
get { return having; }
set
{
- if (value is not null)
- SqlValidator.EnsureIsBooleanExpression(value);
+ SqlValidator.EnsureIsBooleanExpression(value);
having = value;
}
}
@@ -94,6 +94,8 @@ public SqlExpression Having
/// The order by clause.
public SqlOrderCollection OrderBy => orderBy ??= new();
+ public IReadOnlyList OrderByReadOnly => orderBy ??= [];
+
///
/// Gets or sets a value indicating whether this is distinct.
///
@@ -168,7 +170,7 @@ internal override SqlSelect Clone(SqlNodeCloneContext context) =>
if (t.Hints.Count > 0)
foreach (SqlHint hint in t.Hints)
- clone.Hints.Add(hint.Clone(c));
+ clone.AddHint(hint.Clone(c));
return clone;
});
@@ -183,14 +185,14 @@ public SqlSelect ShallowClone()
: SqlDml.Select(From);
result.Columns.AddRange(Columns);
result.Distinct = Distinct;
- result.GroupBy.AddRange(GroupBy);
+ result.GroupBy.AddRange(GroupByReadOnly);
result.Having = Having;
result.Offset = Offset;
result.Limit = Limit;
- foreach (var order in OrderBy)
+ foreach (var order in OrderByReadOnly)
result.OrderBy.Add(order);
foreach (var hint in Hints)
- result.Hints.Add(hint);
+ result.AddHint(hint);
result.Where = Where;
result.Lock = Lock;
result.Comment = Comment;
diff --git a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlUpdate.cs b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlUpdate.cs
index 8f778573a9..0fc57b5ee9 100644
--- a/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlUpdate.cs
+++ b/Orm/Xtensive.Orm/Sql/Dml/Statements/SqlUpdate.cs
@@ -83,7 +83,7 @@ internal override SqlUpdate Clone(SqlNodeCloneContext context) =>
clone.Limit = t.where.Clone(c);
if (t.Hints.Count > 0)
foreach (SqlHint hint in t.Hints)
- clone.Hints.Add(hint.Clone(c));
+ clone.AddHint(hint.Clone(c));
return clone;
});
diff --git a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs
index f066f9a8f2..76619bf22b 100644
--- a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs
+++ b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs
@@ -7,7 +7,7 @@
namespace Xtensive.Sql
{
[Serializable]
- public enum SqlNodeType
+ public enum SqlNodeType : byte
{
Action,
Add,
@@ -117,4 +117,4 @@ public enum SqlNodeType
Fragment,
Metadata,
}
-}
\ No newline at end of file
+}