diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/DriverFactory.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/DriverFactory.cs index dd3c5e24c4..8be25beb36 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/DriverFactory.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/DriverFactory.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2021 Xtensive LLC. +// Copyright (C) 2011-2022 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 @@ -122,7 +122,10 @@ private static SqlDriver CreateDriverInstance(string connectionString, Version v 5 when version.Minor == 1 => new v5_1.Driver(coreServerInfo), 5 when version.Minor == 5 => new v5_5.Driver(coreServerInfo), 5 when version.Minor == 6 => new v5_6.Driver(coreServerInfo), - _ => new v5_6.Driver(coreServerInfo) + 5 when version.Minor == 7 => new v5_7.Driver(coreServerInfo), + 6 or 7 => throw new NotSupportedException(string.Format(Strings.ExVersionXOfMySQLIsNotSupported, version)), + 8 => new v8_0.Driver(coreServerInfo), + _ => new v8_0.Driver(coreServerInfo) }; } diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.Designer.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.Designer.cs index ee0df2c30d..82f4da2092 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.Designer.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace Xtensive.Sql.Drivers.MySql.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -113,5 +113,14 @@ internal static string ExUserNameRequired { return ResourceManager.GetString("ExUserNameRequired", resourceCulture); } } + + /// + /// Looks up a localized string similar to Version {0} of MySQL is not supported.. + /// + internal static string ExVersionXOfMySQLIsNotSupported { + get { + return ResourceManager.GetString("ExVersionXOfMySQLIsNotSupported", resourceCulture); + } + } } } diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.resx b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.resx index b8e8b5ec35..aec58aa70e 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.resx +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/Resources/Strings.resx @@ -135,4 +135,7 @@ MySQL Username is required. + + Version {0} of MySQL is not supported. + \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.cs index e908319bd3..4a4a2aa5ae 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2020 Xtensive LLC. +// Copyright (C) 2011-2022 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 @@ -400,7 +400,7 @@ private static void ReadTableData(DbDataReader reader, Catalog catalog) var schemaName = reader.GetString(0); var schema = catalog.Schemas[schemaName]; var tableName = reader.GetString(1); - schema.CreateTable(tableName); + _ = schema.CreateTable(tableName); } // ---- ReadTableColumnData @@ -451,8 +451,8 @@ private void ReadTableColumnData(DbDataReader reader, ref ColumnReaderState true, + "N" or "NO" or "DISABLED" or "NONUNIQUE" => false, + _ => throw new ArgumentOutOfRangeException(string.Format(Strings.ExInvalidBooleanStringX, value)), + }; } - private static bool IsFullTextIndex(IDataRecord row, int index) - { - var value = ReadStringOrNull(row, index); - if (!string.IsNullOrEmpty(value)) { - value = value.ToUpperInvariant(); - return value == "FULLTEXT"; - } - return false; - } + private static bool IsFullTextIndex(IDataRecord row, int index) => + ReadStringOrNull(row, index).Equals("FULLTEXT", StringComparison.OrdinalIgnoreCase); - private static bool ReadAutoIncrement(IDataRecord row, int index) - { - var value = ReadStringOrNull(row, index); - if (!string.IsNullOrEmpty(value)) { - value = value.ToUpperInvariant(); - return value == "AUTO_INCREMENT"; - } - return false; - } + private static bool ReadIfAutoIncrement(IDataRecord row, int index) => + ReadStringOrNull(row, index).Equals("AUTO_INCREMENT", StringComparison.OrdinalIgnoreCase); - private static long ReadLong(IDataRecord row, int index) - { - var value = row.GetDecimal(index); - return value > long.MaxValue ? long.MaxValue : (long) value; - } + private static int ReadAutoIncrementValue(IDataRecord row, int index) => + row.IsDBNull(index) ? 1 : ReadInt(row, index); private static int ReadInt(IDataRecord row, int index) { diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Compiler.cs new file mode 100644 index 0000000000..383b42888b --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Compiler.cs @@ -0,0 +1,55 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +using Xtensive.Sql.Compiler; +using Xtensive.Sql.Dml; + +namespace Xtensive.Sql.Drivers.MySql.v5_7 +{ + internal class Compiler : v5_6.Compiler + { + /// + public override void Visit(SqlQueryExpression node) + { + using (context.EnterScope(node)) { + var wrapLeft = node.Left is SqlSelect sL + && (sL.OrderBy.Count > 0 || sL.HasLimit || sL.Lock != SqlLockType.Empty); + var wrapRight = node.Left is SqlSelect sR + && (sR.OrderBy.Count > 0 || sR.HasLimit || sR.Lock != SqlLockType.Empty); + + AppendTranslatedEntry(node); + if (wrapLeft) { + _ = context.Output.Append("("); + node.Left.AcceptVisitor(this); + _ = context.Output.Append(")"); + } + else { + node.Left.AcceptVisitor(this); + } + + AppendTranslated(node.NodeType); + AppendTranslated(node, QueryExpressionSection.All); + + if (wrapRight) { + _ = context.Output.Append("("); + node.Right.AcceptVisitor(this); + _ = context.Output.Append(")"); + } + else { + node.Right.AcceptVisitor(this); + } + AppendTranslatedExit(node); + } + } + + // Constructors + + public Compiler(SqlDriver driver) + : base(driver) + { + } + } +} diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Driver.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Driver.cs new file mode 100644 index 0000000000..7719d0cafd --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Driver.cs @@ -0,0 +1,46 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +using Xtensive.Sql.Compiler; +using Xtensive.Sql.Info; + +namespace Xtensive.Sql.Drivers.MySql.v5_7 +{ + internal class Driver : MySql.Driver + { + protected override Sql.TypeMapper CreateTypeMapper() + { + return new TypeMapper(this); + } + + protected override SqlCompiler CreateCompiler() + { + return new Compiler(this); + } + + protected override SqlTranslator CreateTranslator() + { + return new Translator(this); + } + + protected override Model.Extractor CreateExtractor() + { + return new Extractor(this); + } + + protected override Info.ServerInfoProvider CreateServerInfoProvider() + { + return new ServerInfoProvider(this); + } + + // Constructors + + public Driver(CoreServerInfo coreServerInfo) + : base(coreServerInfo) + { + } + } +} diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Extractor.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Extractor.cs new file mode 100644 index 0000000000..512dd5e064 --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Extractor.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v5_7 +{ + internal class Extractor : v5_6.Extractor + { + // Constructors + + public Extractor(SqlDriver driver) + : base(driver) + { + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/ServerInfoProvider.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/ServerInfoProvider.cs new file mode 100644 index 0000000000..7e1b076b64 --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/ServerInfoProvider.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v5_7 +{ + internal class ServerInfoProvider : v5_6.ServerInfoProvider + { + // Constructors + + public ServerInfoProvider(SqlDriver driver) + : base(driver) + { + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Translator.cs new file mode 100644 index 0000000000..9ea62b6741 --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/Translator.cs @@ -0,0 +1,38 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +using System; +using Xtensive.Sql.Compiler; +using Xtensive.Sql.Dml; + +namespace Xtensive.Sql.Drivers.MySql.v5_7 +{ + internal class Translator : v5_6.Translator + { + /// + public override void Translate(SqlCompilerContext context, SqlCast node, NodeSection section) + { + if (node.Type.Type == SqlType.DateTime) { + var output = context.Output; + _ = section switch { + NodeSection.Entry => output.AppendOpeningPunctuation("CAST("), + NodeSection.Exit => output.Append("AS ") + .Append(Translate(node.Type)) + .AppendClosingPunctuation("(6))"), + _ => throw new ArgumentOutOfRangeException(nameof(section)), + }; + } + base.Translate(context, node, section); + } + + // Constructors + + public Translator(SqlDriver driver) + : base(driver) + { + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/TypeMapper.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/TypeMapper.cs new file mode 100644 index 0000000000..2a0d6f655a --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_7/TypeMapper.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v5_7 +{ + internal class TypeMapper : v5_6.TypeMapper + { + // Constructors + + public TypeMapper(SqlDriver driver) + : base(driver) + { + } + } +} diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Compiler.cs new file mode 100644 index 0000000000..d67a0840b1 --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Compiler.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v8_0 +{ + internal class Compiler : v5_7.Compiler + { + + // Constructors + + public Compiler(SqlDriver driver) + : base(driver) + { + } + } +} diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Driver.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Driver.cs new file mode 100644 index 0000000000..4e508f6309 --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Driver.cs @@ -0,0 +1,46 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +using Xtensive.Sql.Compiler; +using Xtensive.Sql.Info; + +namespace Xtensive.Sql.Drivers.MySql.v8_0 +{ + internal class Driver : MySql.Driver + { + protected override Sql.TypeMapper CreateTypeMapper() + { + return new TypeMapper(this); + } + + protected override SqlCompiler CreateCompiler() + { + return new Compiler(this); + } + + protected override SqlTranslator CreateTranslator() + { + return new Translator(this); + } + + protected override Model.Extractor CreateExtractor() + { + return new Extractor(this); + } + + protected override Info.ServerInfoProvider CreateServerInfoProvider() + { + return new ServerInfoProvider(this); + } + + // Constructors + + public Driver(CoreServerInfo coreServerInfo) + : base(coreServerInfo) + { + } + } +} diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Extractor.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Extractor.cs new file mode 100644 index 0000000000..9c34206d51 --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Extractor.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v8_0 +{ + internal class Extractor : v5_7.Extractor + { + // Constructors + + public Extractor(SqlDriver driver) + : base(driver) + { + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/ServerInfoProvider.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/ServerInfoProvider.cs new file mode 100644 index 0000000000..ddc9d1f1ab --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/ServerInfoProvider.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v8_0 +{ + internal class ServerInfoProvider : v5_7.ServerInfoProvider + { + // Constructors + + public ServerInfoProvider(SqlDriver driver) + : base(driver) + { + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Translator.cs new file mode 100644 index 0000000000..e69cd1edbc --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/Translator.cs @@ -0,0 +1,41 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +using System; +using Xtensive.Sql.Compiler; +using Xtensive.Sql.Dml; + +namespace Xtensive.Sql.Drivers.MySql.v8_0 +{ + internal class Translator : v5_7.Translator + { + /// + public override void Translate(IOutput output, SqlLockType lockType) + { + var forShare = lockType.Supports(SqlLockType.Shared); + var forUpdate = lockType.SupportsAny(SqlLockType.Update | SqlLockType.Exclusive); + + if (!forShare && !forUpdate) { + throw new NotSupportedException($"Lock '{lockType.ToString(true)}' is not supported."); + } + + _ = output + .Append(forShare ? "FOR SHARE" : "FOR UPDATE") + .Append(lockType.Supports(SqlLockType.SkipLocked) + ? " SKIP LOCKED" + : lockType.Supports(SqlLockType.ThrowIfLocked) + ? " NOWAIT" + : string.Empty); + } + + // Constructors + + public Translator(SqlDriver driver) + : base(driver) + { + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/TypeMapper.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/TypeMapper.cs new file mode 100644 index 0000000000..ce4bb1815a --- /dev/null +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v8_0/TypeMapper.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2022.02.03 + +namespace Xtensive.Sql.Drivers.MySql.v8_0 +{ + internal class TypeMapper : v5_7.TypeMapper + { + // Constructors + + public TypeMapper(SqlDriver driver) + : base(driver) + { + } + } +} diff --git a/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj b/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj index d2b6fc6ca7..68c6b51e0c 100644 --- a/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj +++ b/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj @@ -1,4 +1,4 @@ - + true $(OutputPath)$(TargetFramework)\$(AssemblyName).xml @@ -23,15 +23,21 @@ - - True - True - Strings.resx - + + ResXFileCodeGenerator + Strings.Designer.cs + + + + True + True + Strings.resx + + diff --git a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ChinookTest.cs b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ChinookTest.cs index 1695460888..41ee3c306b 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ChinookTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ChinookTest.cs @@ -957,5 +957,262 @@ public void Test052() Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); } + + [Test] + public void Test053() + { + Require.ProviderVersionAtLeast(new Version(8, 0)); + + var nativeSql = + "SELECT `a`.`ArtistId` FROM ((SELECT ArtistId FROM dotest.album where AlbumId >= 0 and AlbumId < 50 LIMIT 10)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 50 and AlbumId < 100 LIMIT 10)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 100 and AlbumId < 200 LIMIT 10)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 20 and AlbumId < 300 LIMIT 10)) `a`;"; + + var albums = SqlDml.TableRef(schema.Tables["album"]); + + var s1 = SqlDml.Select(albums); + s1.Columns.Add(albums.Columns["ArtistId"]); + s1.Where = albums.Columns["AlbumId"] >= 0 && albums.Columns["AlbumId"] < 50; + s1.Limit = 10; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s2 = SqlDml.Select(albums); + s2.Columns.Add(albums.Columns["ArtistId"]); + s2.Where = albums.Columns["AlbumId"] >= 50 && albums.Columns["AlbumId"] < 100; + s2.Limit = 10; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s3 = SqlDml.Select(albums); + s3.Columns.Add(albums.Columns["ArtistId"]); + s3.Where = albums.Columns["AlbumId"] >= 100 && albums.Columns["AlbumId"] < 200; + s3.Limit = 10; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s4 = SqlDml.Select(albums); + s4.Columns.Add(albums.Columns["ArtistId"]); + s4.Where = albums.Columns["AlbumId"] >= 200 && albums.Columns["AlbumId"] < 300; + s4.Limit = 10; + + var qr = SqlDml.QueryRef(s1.Union(s2).Union(s3.Union(s4)), "a"); + var select = SqlDml.Select(qr); + select.Columns.Add(qr["ArtistId"]); + + Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); + } + + [Test] + public void Test054() + { + Require.ProviderVersionAtLeast(new Version(8, 0)); + + var nativeSql = + "SELECT `a`.`ArtistId` FROM ((SELECT ArtistId FROM dotest.album where AlbumId >= 0 and AlbumId < 50 LIMIT 10 OFFSET 10)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 50 and AlbumId < 100 LIMIT 10 OFFSET 10)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 100 and AlbumId < 200 LIMIT 10 OFFSET 10)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 20 and AlbumId < 300 LIMIT 10 OFFSET 10)) `a`;"; + + var albums = SqlDml.TableRef(schema.Tables["album"]); + + var s1 = SqlDml.Select(albums); + s1.Columns.Add(albums.Columns["ArtistId"]); + s1.Where = albums.Columns["AlbumId"] >= 0 && albums.Columns["AlbumId"] < 50; + s1.Limit = 10; + s1.Offset = 10; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s2 = SqlDml.Select(albums); + s2.Columns.Add(albums.Columns["ArtistId"]); + s2.Where = albums.Columns["AlbumId"] >= 50 && albums.Columns["AlbumId"] < 100; + s2.Limit = 10; + s2.Offset = 10; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s3 = SqlDml.Select(albums); + s3.Columns.Add(albums.Columns["ArtistId"]); + s3.Where = albums.Columns["AlbumId"] >= 100 && albums.Columns["AlbumId"] < 200; + s3.Limit = 10; + s3.Offset = 10; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s4 = SqlDml.Select(albums); + s4.Columns.Add(albums.Columns["ArtistId"]); + s4.Where = albums.Columns["AlbumId"] >= 200 && albums.Columns["AlbumId"] < 300; + s4.Limit = 10; + s4.Offset = 10; + + var qr = SqlDml.QueryRef(s1.Union(s2).Union(s3.Union(s4)), "a"); + var select = SqlDml.Select(qr); + select.Columns.Add(qr["ArtistId"]); + + Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); + } + + [Test] + public void Test055() + { + Require.ProviderVersionAtLeast(new Version(8, 0)); + + var nativeSql = + "SELECT `a`.`ArtistId` FROM ((SELECT ArtistId FROM dotest.album where AlbumId >= 0 and AlbumId < 50 FOR SHARE)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 50 and AlbumId < 100 FOR SHARE)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 100 and AlbumId < 200 FOR SHARE)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 20 and AlbumId < 300 FOR SHARE)) `a`;"; + + var albums = SqlDml.TableRef(schema.Tables["album"]); + + var s1 = SqlDml.Select(albums); + s1.Columns.Add(albums.Columns["ArtistId"]); + s1.Where = albums.Columns["AlbumId"] >= 0 && albums.Columns["AlbumId"] < 50; + s1.Lock = SqlLockType.Shared; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s2 = SqlDml.Select(albums); + s2.Columns.Add(albums.Columns["ArtistId"]); + s2.Where = albums.Columns["AlbumId"] >= 50 && albums.Columns["AlbumId"] < 100; + s2.Lock = SqlLockType.Shared; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s3 = SqlDml.Select(albums); + s3.Columns.Add(albums.Columns["ArtistId"]); + s3.Where = albums.Columns["AlbumId"] >= 100 && albums.Columns["AlbumId"] < 200; + s3.Lock = SqlLockType.Shared; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s4 = SqlDml.Select(albums); + s4.Columns.Add(albums.Columns["ArtistId"]); + s4.Where = albums.Columns["AlbumId"] >= 200 && albums.Columns["AlbumId"] < 300; + s4.Lock = SqlLockType.Shared; + + var qr = SqlDml.QueryRef(s1.Union(s2).Union(s3.Union(s4)), "a"); + var select = SqlDml.Select(qr); + select.Columns.Add(qr["ArtistId"]); + + Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); + } + + [Test] + public void Test056() + { + Require.ProviderVersionAtLeast(new Version(8, 0)); + + var nativeSql = + "SELECT `a`.`ArtistId` FROM ((SELECT ArtistId FROM dotest.album where AlbumId >= 0 and AlbumId < 50 ORDER BY ArtistId)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 50 and AlbumId < 100 ORDER BY ArtistId)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 100 and AlbumId < 200 ORDER BY ArtistId)" + + " UNION (SELECT ArtistId FROM dotest.album where AlbumId >= 20 and AlbumId < 300 ORDER BY ArtistId)) `a`;"; + + var albums = SqlDml.TableRef(schema.Tables["album"]); + + var s1 = SqlDml.Select(albums); + s1.Columns.Add(albums.Columns["ArtistId"]); + s1.Where = albums.Columns["AlbumId"] >= 0 && albums.Columns["AlbumId"] < 50; + s1.OrderBy.Add(albums.Columns["ArtistId"]); + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s2 = SqlDml.Select(albums); + s2.Columns.Add(albums.Columns["ArtistId"]); + s2.Where = albums.Columns["AlbumId"] >= 50 && albums.Columns["AlbumId"] < 100; + s2.OrderBy.Add(albums.Columns["ArtistId"]); + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s3 = SqlDml.Select(albums); + s3.Columns.Add(albums.Columns["ArtistId"]); + s3.Where = albums.Columns["AlbumId"] >= 100 && albums.Columns["AlbumId"] < 200; + s3.OrderBy.Add(albums.Columns["ArtistId"]); + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s4 = SqlDml.Select(albums); + s4.Columns.Add(albums.Columns["ArtistId"]); + s4.Where = albums.Columns["AlbumId"] >= 200 && albums.Columns["AlbumId"] < 300; + s4.OrderBy.Add(albums.Columns["ArtistId"]); + + var qr = SqlDml.QueryRef(s1.Union(s2).Union(s3.Union(s4)), "a"); + var select = SqlDml.Select(qr); + select.Columns.Add(qr["ArtistId"]); + + Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); + } + + + [Test] + public void Test057() + { + Require.ProviderVersionAtLeast(new Version(8, 0)); + + var nativeSql = + "SELECT `a`.`ArtistId` FROM (SELECT ArtistId FROM dotest.album where AlbumId >= 0 and AlbumId < 50 GROUP BY ArtistId" + + " UNION SELECT ArtistId FROM dotest.album where AlbumId >= 50 and AlbumId < 100 GROUP BY ArtistId" + + " UNION SELECT ArtistId FROM dotest.album where AlbumId >= 100 and AlbumId < 200 GROUP BY ArtistId" + + " UNION SELECT ArtistId FROM dotest.album where AlbumId >= 20 and AlbumId < 300 GROUP BY ArtistId) `a`;"; + + var albums = SqlDml.TableRef(schema.Tables["album"]); + + var s1 = SqlDml.Select(albums); + s1.Columns.Add(albums.Columns["ArtistId"]); + s1.Where = albums.Columns["AlbumId"] >= 0 && albums.Columns["AlbumId"] < 50; + s1.GroupBy.Add(albums.Columns["ArtistId"]); + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s2 = SqlDml.Select(albums); + s2.Columns.Add(albums.Columns["ArtistId"]); + s2.Where = albums.Columns["AlbumId"] >= 50 && albums.Columns["AlbumId"] < 100; + s2.GroupBy.Add(albums.Columns["ArtistId"]); + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s3 = SqlDml.Select(albums); + s3.Columns.Add(albums.Columns["ArtistId"]); + s3.Where = albums.Columns["AlbumId"] >= 100 && albums.Columns["AlbumId"] < 200; + s3.GroupBy.Add(albums.Columns["ArtistId"]); + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s4 = SqlDml.Select(albums); + s4.Columns.Add(albums.Columns["ArtistId"]); + s4.Where = albums.Columns["AlbumId"] >= 200 && albums.Columns["AlbumId"] < 300; + s4.GroupBy.Add(albums.Columns["ArtistId"]); + + var qr = SqlDml.QueryRef(s1.Union(s2).Union(s3.Union(s4)), "a"); + var select = SqlDml.Select(qr); + select.Columns.Add(qr["ArtistId"]); + + Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); + } + + [Test] + public void Test058() + { + var nativeSql = + "SELECT `a`.`AlbumId` FROM (SELECT AlbumId FROM dotest.album where AlbumId >= 0 and AlbumId < 50" + + " UNION SELECT AlbumId FROM dotest.album where AlbumId >= 50 and AlbumId< 100" + + " UNION SELECT AlbumId FROM dotest.album where AlbumId >= 100 and AlbumId< 200" + + " UNION SELECT AlbumId FROM dotest.album where AlbumId >= 20 and AlbumId< 300) `a`;"; + + var albums = SqlDml.TableRef(schema.Tables["album"]); + + var s1 = SqlDml.Select(albums); + s1.Columns.Add(albums.Columns["AlbumId"]); + s1.Where = albums.Columns["AlbumId"] >= 0 && albums.Columns["AlbumId"] < 50; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s2 = SqlDml.Select(albums); + s2.Columns.Add(albums.Columns["AlbumId"]); + s2.Where = albums.Columns["AlbumId"] >= 50 && albums.Columns["AlbumId"] < 100; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s3 = SqlDml.Select(albums); + s3.Columns.Add(albums.Columns["AlbumId"]); + s3.Where = albums.Columns["AlbumId"] >= 100 && albums.Columns["AlbumId"] < 200; + + albums = SqlDml.TableRef(schema.Tables["album"]); + var s4 = SqlDml.Select(albums); + s4.Columns.Add(albums.Columns["AlbumId"]); + s4.Where = albums.Columns["AlbumId"] >= 200 && albums.Columns["AlbumId"] < 300; + + var qr = SqlDml.QueryRef(s1.Union(s2).Union(s3.Union(s4)), "a"); + var select = SqlDml.Select(qr); + select.Columns.Add(qr["AlbumId"]); + + Assert.IsTrue(CompareExecuteNonQuery(nativeSql, select)); + } } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/TagTest.cs b/Orm/Xtensive.Orm.Tests/Linq/TagTest.cs index 012756c3ae..e85294b920 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/TagTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/TagTest.cs @@ -678,8 +678,9 @@ void SqlCapturer(object sender, DbCommandEventArgs args) [Test] public void SessionTagSingle() { - var allCommands = new List(); + Require.ProviderIsNot(StorageProvider.MySql); + var allCommands = new List(); var id = 0L; using (var populateSession = Domain.OpenSession()) @@ -751,6 +752,8 @@ void SqlCapturer(object sender, DbCommandEventArgs args) [Test] public void SessionTagSingleOrDefault() { + Require.ProviderIsNot(StorageProvider.MySql); + var allCommands = new List(); var id = 0L; @@ -822,6 +825,7 @@ void SqlCapturer(object sender, DbCommandEventArgs args) [Test] public void SessionTagPrefetchEntity() { + Require.ProviderIsNot(StorageProvider.MySql); var allCommands = new List(); using (var populateSession = Domain.OpenSession()) diff --git a/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs b/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs index f08ceb74d5..be14016706 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov // Created: 2009.08.24 @@ -28,11 +28,34 @@ public static bool IsNullReference(this SqlExpression expression) return ReferenceEquals(expression, null); } + /// + /// Checks whether contains all flags of given . + /// + /// All flags. + /// Flags to check existance. + /// + /// if contains all flags of , + /// otherwise, . + /// public static bool Supports(this SqlLockType available, SqlLockType required) { return (available & required)==required; } + /// + /// Checks whether contains any flag of given . + /// + /// All flags. + /// Flags to check existance. + /// + /// if contains any flag of , + /// otherwise, . + /// + public static bool SupportsAny(this SqlLockType available, SqlLockType required) + { + return (available | required) == required; + } + public static string ToString(this SqlLockType lockType, bool humanReadable) { if (!humanReadable) diff --git a/ReadMe.md b/ReadMe.md index 5d50d06e25..51a3c362c9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -7,7 +7,7 @@ Supported databases: - MS Azure SQL Database - Oracle 10g, 11g - PostgreSQL 8.3, 8.4, 9.0, 9.1, 9.2, 10, 11 -- MySQL 5.5, 5.6 +- MySQL 5.5, 5.6, 5.7, 8.0 - Firebird 2.5 - Sqlite 3