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 +}