From f16d4ce804cdf1fd1d33ea3419e94d6e5f4694c0 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 6 Jun 2022 16:10:44 -0700 Subject: [PATCH 01/86] Add support of DateOnly/TimeOnly types Fix formatting Fix tests --- Directory.Build.props | 9 +- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 3 + .../Xtensive.Orm.Manual.csproj | 2 +- .../Sql.Drivers.Oracle/v09/Translator.cs | 3 + .../v8_0/ServerInfoProvider.cs | 5 + .../Xtensive.Orm.PostgreSql.csproj | 2 +- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 10 + .../v09/ServerInfoProvider.cs | 7 +- .../Sql.Drivers.SqlServer/v09/Translator.cs | 9 + .../v10/ServerInfoProvider.cs | 13 +- .../Sql.Drivers.SqlServer/v10/Translator.cs | 3 + .../Xtensive.Orm.Tests.Core.csproj | 4 +- .../Xtensive.Orm.Tests.Framework.csproj | 2 +- .../Xtensive.Orm.Tests.Sql.csproj | 2 +- .../DateTime/ComparisonTest.cs | 13 +- .../DateTime/DateTimeToIsoTest.cs | 2 +- .../DateTime/DistinctTest.cs | 6 +- .../DateTime/JoinTest.cs | 8 + .../DateTime/OperationsTest.cs | 31 ++ .../DateTime/OrderByTest.cs | 7 +- .../DateTime/PartsExtractionTest.cs | 12 + .../DateTime/WhereTest.cs | 7 + .../DateTimeBaseTest.cs | 53 +-- .../Linq/DateTimeAndDateTimeOffset/Model.cs | 30 +- .../SchemaSharing/EntityManipulation/Model.cs | 10 + .../SimpleEntityManipulationTest.cs | 12 + .../Storage/SetFieldTest.cs | 10 + Orm/Xtensive.Orm/Linq/ExpressionWriter.cs | 12 + .../Orm/Linq/ExpressionExtensions.cs | 9 +- .../Orm/Linq/ItemToTupleConverter{TItem}.cs | 17 +- .../MemberCompilerProvider.cs | 20 +- .../ExpressionProcessor.Helpers.cs | 5 +- .../ExpressionTranslationHelpers.cs | 102 +++--- .../MemberCompilers/DateTimeCompilers.cs | 316 ++++++++++++------ .../Orm/Providers/SqlCompiler.Helpers.cs | 30 +- Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs | 6 + .../Sql/Compiler/Internals/PostCompiler.cs | 5 +- Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs | 2 +- .../Sql/Compiler/SqlTranslator.cs | 12 + Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 8 + .../Sql/Info/DataTypeCollection.cs | 19 +- Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs | 13 +- .../Sql/Internals/SqlValidator.cs | 4 + Orm/Xtensive.Orm/Sql/SqlDml.cs | 44 +++ Orm/Xtensive.Orm/Sql/SqlDriver.cs | 8 + Orm/Xtensive.Orm/Sql/SqlType.cs | 14 + .../Sql/ValueTypeMapping/TypeMapper.cs | 230 ++++++------- .../Tuples/Packed/PackedFieldAccessor.cs | 30 +- Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs | 46 ++- Orm/Xtensive.Orm/Xtensive.Orm.csproj | 2 +- 50 files changed, 840 insertions(+), 389 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5424169cb9..2f84566bb6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,8 @@ true true - net5.0 + net5.0 + $(DO_TargetFrameworks) 9.0 $([MSBuild]::EnsureTrailingSlash( $([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildThisFileDirectory)', 'Orm.sln')))) @@ -96,4 +97,10 @@ + + + + + $(DefineConstants);DO_DATEONLY + diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index 044b637716..4b2b8c5b8c 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -188,6 +188,9 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddMonths: Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); return; + case SqlFunctionType.DateOnlyAddDays: + Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); + return; case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); return; diff --git a/Orm/Xtensive.Orm.Manual/Xtensive.Orm.Manual.csproj b/Orm/Xtensive.Orm.Manual/Xtensive.Orm.Manual.csproj index 0855b47976..5e784f0e88 100644 --- a/Orm/Xtensive.Orm.Manual/Xtensive.Orm.Manual.csproj +++ b/Orm/Xtensive.Orm.Manual/Xtensive.Orm.Manual.csproj @@ -12,7 +12,7 @@ 2 - TRACE;DEBUG + $(DefineConstants);TRACE;DEBUG TRACE diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index 5c3c92167c..44832f2922 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -30,6 +30,9 @@ internal class Translator : SqlTranslator /// public override string DateTimeFormatString => @"'(TIMESTAMP '\'yyyy\-MM\-dd HH\:mm\:ss\.fff\'\)"; + /// + public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)"; + /// public override string TimeSpanFormatString => "(INTERVAL '{0}{1} {2}:{3}:{4}.{5:000}' DAY(6) TO SECOND(3))"; diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs index aaecae7730..4337440532 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs @@ -178,6 +178,11 @@ public override DataTypeCollection GetDataTypesInfo() dtc.Interval = DataTypeInfo.Range(SqlType.Interval, commonFeatures, ValueRange.TimeSpan, "interval"); +#if DO_DATEONLY + dtc.DateOnly = DataTypeInfo.Range(SqlType.Date, commonFeatures, ValueRange.DateOnly, "date"); + dtc.TimeOnly = DataTypeInfo.Range(SqlType.Time, commonFeatures, ValueRange.TimeOnly, "time"); +#endif + dtc.Char = DataTypeInfo.Stream(SqlType.Char, commonFeatures, MaxCharLength, "character", "char", "bpchar"); dtc.VarChar = DataTypeInfo.Stream(SqlType.VarChar, commonFeatures, MaxCharLength, "character varying", "varchar"); dtc.VarCharMax = DataTypeInfo.Regular(SqlType.VarCharMax, commonFeatures, "text"); diff --git a/Orm/Xtensive.Orm.PostgreSql/Xtensive.Orm.PostgreSql.csproj b/Orm/Xtensive.Orm.PostgreSql/Xtensive.Orm.PostgreSql.csproj index 47da25e64c..60f1fd7fbf 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Xtensive.Orm.PostgreSql.csproj +++ b/Orm/Xtensive.Orm.PostgreSql/Xtensive.Orm.PostgreSql.csproj @@ -15,7 +15,7 @@ 2 - TRACE;DEBUG + $(DefineConstants);TRACE;DEBUG TRACE diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index cfc4f60a44..5505da3655 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -150,6 +150,7 @@ public override void Visit(SqlAlterTable node) /// public override void Visit(SqlFunctionCall node) { + var arguments = node.Arguments; switch (node.FunctionType) { case SqlFunctionType.CharLength: (SqlDml.FunctionCall("DATALENGTH", node.Arguments) / 2).AcceptVisitor(this); @@ -199,6 +200,15 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); return; + case SqlFunctionType.DateOnlyAddDays: + Visit(DateAddDay(arguments[0], arguments[1])); + return; + case SqlFunctionType.TimeOnlyAddHours: + Visit(DateAddHour(arguments[0], arguments[1])); + return; + case SqlFunctionType.TimeOnlyAddMinutes: + Visit(DateAddMinute(arguments[0], arguments[1])); + return; case SqlFunctionType.DateTimeTruncate: DateTimeTruncate(node.Arguments[0]).AcceptVisitor(this); return; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs index 908ef98576..29bf200f16 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs @@ -251,9 +251,14 @@ public override DataTypeCollection GetDataTypesInfo() ValueRange.Double, "float"); types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, - new ValueRange(new DateTime(1753, 1, 1), new DateTime(9999, 12,31)), + new ValueRange(new DateTime(1753, 1, 1), new DateTime(9999, 12, 31)), "datetime", "smalldatetime"); +#if DO_DATEONLY + types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index,new ValueRange(new DateOnly(1, 1, 1), new DateOnly(9999, 12, 31)), "date"); + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index,new ValueRange(TimeOnly.MinValue, TimeOnly.MaxValue), "time"); +#endif + types.Char = DataTypeInfo.Stream(SqlType.Char, common | index, 4000, "nchar", "char"); types.VarChar = DataTypeInfo.Stream(SqlType.VarChar, common | index, 4000, "nvarchar", "varchar"); types.VarCharMax = DataTypeInfo.Regular(SqlType.VarCharMax, common, "nvarchar(max)", "ntext", "varchar(max)", "text", "xml"); diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs index cd34db9e3b..14245d7f24 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs @@ -20,6 +20,8 @@ namespace Xtensive.Sql.Drivers.SqlServer.v09 internal class Translator : SqlTranslator { public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fff\'' as datetime)'"; + public override string DateOnlyFormatString => @"'cast ('\'yyyy\-MM\-dd\'' as date)'"; + public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fff\'' as time)'"; public override string TimeSpanFormatString => string.Empty; public override void Initialize() @@ -567,6 +569,13 @@ public override void Translate(SqlCompilerContext context, object literalValue) case long v: _ = output.Append($"CAST({v} as BIGINT)"); break; +#if DO_DATEONLY + case DateOnly dateOnly: + var dateOnlyRange = (ValueRange) Driver.ServerInfo.DataTypes.DateTime.ValueRange; + var newDateOnly = ValueRangeValidator.Correct(dateOnly.ToDateTime(TimeOnly.MinValue), dateOnlyRange).Date; + output.Append(newDateOnly.ToString(DateOnlyFormatString, DateTimeFormat)); + break; +#endif default: base.Translate(context, literalValue); break; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs index 57afca22ff..e4653b4f5c 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs @@ -29,15 +29,22 @@ public override DataTypeCollection GetDataTypesInfo() var index = DataTypeFeatures.Indexing | DataTypeFeatures.Clustering | DataTypeFeatures.FillFactor | DataTypeFeatures.KeyConstraint; - types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, - new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)), - "datetime2", "datetime", "date", "time", "smalldatetime"); types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index, new ValueRange(new DateTimeOffset(1, 1, 1, 0, 0, 0, 0, new TimeSpan(0)), new DateTimeOffset(9999, 12, 31, 0, 0, 0, 0, new TimeSpan(0))), "datetimeoffset"); +#if DO_DATEONLY + types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, + new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)), + "datetime2", "datetime", "smalldatetime"); +#else + types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, + new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)), + "datetime2", "datetime", "date", "time", "smalldatetime"); +#endif + types.VarBinaryMax = DataTypeInfo.Regular(SqlType.VarBinaryMax, common, "varbinary(max)", "image"); var geo = DataTypeFeatures.Default | DataTypeFeatures.Nullable | DataTypeFeatures.Multiple | DataTypeFeatures.Spatial; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs index 4cacf3b964..43bd6ee109 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs @@ -17,6 +17,9 @@ internal class Translator : v09.Translator /// public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fffffff\'' as datetime2)'"; + /// + public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fffffff\'' as time)'"; + public string DateTimeOffsetFormatString => @"'cast ('\'yyyy\-MM\-dd HH\:mm\:ss\.fffffff\ zzz\'' as datetimeoffset)'"; /// diff --git a/Orm/Xtensive.Orm.Tests.Core/Xtensive.Orm.Tests.Core.csproj b/Orm/Xtensive.Orm.Tests.Core/Xtensive.Orm.Tests.Core.csproj index e8e39dd906..d5701e209f 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Xtensive.Orm.Tests.Core.csproj +++ b/Orm/Xtensive.Orm.Tests.Core/Xtensive.Orm.Tests.Core.csproj @@ -11,7 +11,7 @@ 2 - TRACE;DEBUG + $(DefineConstants);TRACE;DEBUG TRACE @@ -26,5 +26,5 @@ - + \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests.Framework/Xtensive.Orm.Tests.Framework.csproj b/Orm/Xtensive.Orm.Tests.Framework/Xtensive.Orm.Tests.Framework.csproj index 533ab671b9..255ac46913 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/Xtensive.Orm.Tests.Framework.csproj +++ b/Orm/Xtensive.Orm.Tests.Framework/Xtensive.Orm.Tests.Framework.csproj @@ -2,7 +2,7 @@ true false - TRACE + $(DefineConstants);TRACE Xtensive diff --git a/Orm/Xtensive.Orm.Tests.Sql/Xtensive.Orm.Tests.Sql.csproj b/Orm/Xtensive.Orm.Tests.Sql/Xtensive.Orm.Tests.Sql.csproj index c5c0e6b7db..3c335cd592 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Xtensive.Orm.Tests.Sql.csproj +++ b/Orm/Xtensive.Orm.Tests.Sql/Xtensive.Orm.Tests.Sql.csproj @@ -11,7 +11,7 @@ 2 - TRACE;DEBUG + $(DefineConstants);TRACE;DEBUG TRACE diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs index e0dd74a9fb..9d7406c100 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs @@ -1,9 +1,10 @@ -// Copyright (C) 2016-2021 Xtensive LLC. +// Copyright (C) 2016-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: Alex Groznov // Created: 2016.08.01 +using System; using NUnit.Framework; using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; @@ -23,6 +24,10 @@ public void EqualsTest() RunWrongTest(c => c.MillisecondDateTime==WrongMillisecondDateTime); RunWrongTest(c => c.NullableDateTime==WrongDateTime); RunWrongTest(c => c.NullableDateTime==null); +#if DO_DATEONLY + RunTest(c => c.DateOnly == FirstDateOnly); + RunTest(c => c.NullableDateOnly == NullableDateOnly); +#endif }); } @@ -33,6 +38,9 @@ public void NotEqualTest() RunTest(c=>c.DateTime!=FirstDateTime.AddYears(1)); RunTest(c => c.MillisecondDateTime!=FirstMillisecondDateTime.AddYears(1)); RunTest(c=>c.NullableDateTime!=NullableDateTime.AddYears(1)); +#if DO_DATEONLY + RunTest(c => c.DateOnly != FirstDateOnly.AddYears(1)); +#endif }); } @@ -52,6 +60,9 @@ public void CompareTest() RunWrongTest(c => c.DateTime > FirstDateTime); RunWrongTest(c => c.MillisecondDateTime > FirstMillisecondDateTime); RunWrongTest(c => c.MillisecondDateTime < FirstMillisecondDateTime.Date); +#if DO_DATEONLY + RunTest(c => c.DateOnly > FirstDateOnly.AddDays(-1)); +#endif }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs index b58dd50aee..4e9139e9b7 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Xtensive LLC. +// Copyright (C) 2016 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Alex Groznov diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs index e1c301044d..9e55216189 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Xtensive LLC. +// Copyright (C) 2016 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Alex Groznov @@ -18,6 +18,10 @@ public class DistinctTest : DateTimeBaseTest public void DistinctByDateTimeTest() { ExecuteInsideSession(() => DistinctPrivate(c => c.DateTime)); +#if DO_DATEONLY + ExecuteInsideSession(() => DistinctPrivate(c => c.DateOnly)); + ExecuteInsideSession(() => DistinctPrivate(c => c.TimeOnly)); +#endif } [Test] diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs index 7eb9f0794d..6f1236a72e 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs @@ -17,6 +17,14 @@ public void DateTimeJoinTest() (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateTime, RightDateTime = right.DateTime }, c => c.LeftId, c => c.RightId)); +#if DO_DATEONLY + ExecuteInsideSession(() => JoinPrivate, DateOnly, long>( + left => left.DateOnly, + right => right.DateOnly, + (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateOnly, RightDateTime = right.DateOnly }, + c => c.LeftId, + c => c.RightId)); +#endif } [Test] diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs index b0b2bb46d7..b1c7f2db41 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs @@ -22,6 +22,13 @@ public void AddYearsTest() RunWrongTest(c => c.DateTime.AddYears(1) == FirstDateTime.AddYears(2)); RunWrongTest(c => c.MillisecondDateTime.AddYears(-1) == FirstMillisecondDateTime.AddYears(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddYears(33) == NullableDateTime.AddYears(44)); +#if DO_DATEONLY + RunTest(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(1)); + RunTest(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(33)); + + RunWrongTest(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(2)); + RunWrongTest(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(44)); +#endif }); } @@ -36,6 +43,13 @@ public void AddMonthsTest() RunWrongTest(c => c.DateTime.AddMonths(1) == FirstDateTime.AddMonths(2)); RunWrongTest(c => c.MillisecondDateTime.AddMonths(-1) == FirstMillisecondDateTime.AddMonths(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddMonths(33) == NullableDateTime.AddMonths(44)); +#if DO_DATEONLY + RunTest(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(1)); + RunTest(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(33)); + + RunWrongTest(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(2)); + RunWrongTest(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(44)); +#endif }); } @@ -50,6 +64,13 @@ public void AddDaysTest() RunWrongTest(c => c.DateTime.AddDays(1) == FirstDateTime.AddDays(2)); RunWrongTest(c => c.MillisecondDateTime.AddDays(-1) == FirstMillisecondDateTime.AddDays(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddDays(33) == NullableDateTime.AddDays(44)); +#if DO_DATEONLY + RunTest(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(1)); + RunTest(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(33)); + + RunWrongTest(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(2)); + RunWrongTest(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(44)); +#endif }); } @@ -78,6 +99,11 @@ public void AddMinutesTest() RunWrongTest(c => c.DateTime.AddMinutes(1) == FirstDateTime.AddMinutes(2)); RunWrongTest(c => c.MillisecondDateTime.AddMinutes(-1) == FirstMillisecondDateTime.AddMinutes(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddMinutes(33) == NullableDateTime.AddMinutes(44)); +#if DO_DATEONLY + RunTest(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(1)); + + RunWrongTest(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(2)); +#endif }); } @@ -188,6 +214,11 @@ public void MinusDateTimeTest() RunWrongTest(c => c.DateTime - SecondDateTime == FirstDateTime - WrongDateTime); RunWrongTest(c => c.MillisecondDateTime - SecondDateTime == FirstMillisecondDateTime - WrongDateTime); RunWrongTest(c => c.NullableDateTime - SecondDateTime == NullableDateTime - WrongDateTime); + +#if DO_DATEONLY + RunTest(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - SecondTimeOnly); + RunWrongTest(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - WrongTimeOnly); +#endif }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs index 925a678cd6..d7fd92c34e 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Xtensive LLC. +// Copyright (C) 2016 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Alex Groznov @@ -20,6 +20,11 @@ public void DateTimeOrderByTest() ExecuteInsideSession(() => { OrderByPrivate(c => c.DateTime, c => c.Id); OrderByPrivate(c => c.DateTime, c => c); +#if DO_DATEONLY + OrderByPrivate(c => c.DateOnly, c => c.Id); + OrderByPrivate(c => c.DateOnly, c => c); + OrderByPrivate(c => c.TimeOnly, c => c.Id ); +#endif }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs index fb5e5c8585..6f990d55e1 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs @@ -37,6 +37,13 @@ public void ExtractMonthTest() RunWrongTest(c => c.DateTime.Month == WrongDateTime.Month); RunWrongTest(c => c.MillisecondDateTime.Month == WrongMillisecondDateTime.Month); RunWrongTest(c => c.NullableDateTime.Value.Month == WrongDateTime.Month); +#if DO_DATEONLY + RunTest(c => c.DateOnly.Month == FirstDateOnly.Month); + RunTest(c => c.NullableDateOnly.Value.Month == NullableDateOnly.Month); + + RunWrongTest(c => c.DateOnly.Month == WrongDateOnly.Month); + RunWrongTest(c => c.NullableDateOnly.Value.Month == WrongDateOnly.Month); +#endif }); } @@ -93,6 +100,11 @@ public void ExtractSecondTest() RunWrongTest(c => c.DateTime.Second == WrongDateTime.Second); RunWrongTest(c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Second); RunWrongTest(c => c.NullableDateTime.Value.Second == WrongDateTime.Second); +#if DO_DATEONLY + RunTest(c => c.TimeOnly.Second == FirstTimeOnly.Second); + + RunWrongTest(c => c.TimeOnly.Second == WrongTimeOnly.Second); +#endif }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs index 56dec921c9..ac616be831 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs @@ -21,6 +21,13 @@ public void DateTimeWhereTest() WherePrivate(c => c.DateTime == FirstDateTime, c => c.Id); WherePrivate(c => c.DateTime.Hour == FirstDateTime.Hour, c => c.Id); WherePrivate(c => c.DateTime.Second == FirstDateTime.Second, c => c.Id); + +#if DO_DATEONLY + WherePrivate(c => c.DateOnly == FirstDateOnly, c => c.Id); + WherePrivate(c => c.TimeOnly == FirstTimeOnly, c => c.Id); + WherePrivate(c => c.TimeOnly.Hour == FirstTimeOnly.Hour, c => c.Id); + WherePrivate(c => c.TimeOnly.Second == FirstTimeOnly.Second, c => c.Id); +#endif }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs index 91fa766b84..4339392a3d 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs @@ -25,6 +25,16 @@ public abstract class DateTimeBaseTest : BaseTest protected static readonly DateTime SecondMillisecondDateTime = SecondDateTime.AddMilliseconds(987); protected static readonly DateTime WrongMillisecondDateTime = WrongDateTime.AddMilliseconds(654); +#if DO_DATEONLY + protected static readonly DateOnly FirstDateOnly = DateOnly.FromDateTime(FirstDateTime); + protected static readonly DateOnly NullableDateOnly = DateOnly.FromDateTime(SecondDateTime); + protected static readonly DateOnly WrongDateOnly = DateOnly.FromDateTime(WrongDateTime); + + protected static readonly TimeOnly FirstTimeOnly = TimeOnly.FromDateTime(FirstDateTime); + protected static readonly TimeOnly SecondTimeOnly = TimeOnly.FromDateTime(SecondDateTime); + protected static readonly TimeOnly WrongTimeOnly = TimeOnly.FromDateTime(WrongDateTime); +#endif + protected override void RegisterTypes(DomainConfiguration configuration) { configuration.Types.Register(typeof (SingleDateTimeEntity)); @@ -38,31 +48,36 @@ protected override void PopulateEntities(Session session) new SingleDateTimeEntity { DateTime = FirstDateTime, MillisecondDateTime = FirstMillisecondDateTime, +#if DO_DATEONLY + DateOnly = DateOnly.FromDateTime(FirstDateTime), + NullableDateOnly = DateOnly.FromDateTime(NullableDateTime), + TimeOnly = FirstTimeOnly, +#endif NullableDateTime = NullableDateTime }; - new DateTimeEntity { DateTime = FirstDateTime }; - new DateTimeEntity { DateTime = FirstDateTime }; - new DateTimeEntity { DateTime = FirstDateTime.Date }; - new DateTimeEntity { DateTime = SecondDateTime }; - new DateTimeEntity { DateTime = SecondDateTime.Date }; - new DateTimeEntity { DateTime = new DateTime(FirstDateTime.Year, FirstDateTime.Month, FirstDateTime.Day, FirstDateTime.Hour, FirstDateTime.Minute, 0) }; - new DateTimeEntity { DateTime = new DateTime(FirstDateTime.Ticks, DateTimeKind.Local) }; - new DateTimeEntity { DateTime = FirstDateTime.Add(new TimeSpan(987, 23, 34, 45)) }; - new DateTimeEntity { DateTime = FirstDateTime.AddYears(1) }; - new DateTimeEntity { DateTime = FirstDateTime.AddYears(-2) }; - new DateTimeEntity { DateTime = FirstDateTime.AddMonths(44) }; - new DateTimeEntity { DateTime = FirstDateTime.AddMonths(-55) }; - new DateTimeEntity { DateTime = SecondDateTime.AddHours(5) }; - new DateTimeEntity { DateTime = SecondDateTime.AddHours(-15) }; - new DateTimeEntity { DateTime = SecondDateTime.AddMinutes(59) }; - new DateTimeEntity { DateTime = SecondDateTime.AddMinutes(-49) }; - new DateTimeEntity { DateTime = SecondDateTime.AddSeconds(57) }; - new DateTimeEntity { DateTime = SecondDateTime.AddSeconds(-5) }; + new DateTimeEntity(FirstDateTime); + new DateTimeEntity(FirstDateTime); + new DateTimeEntity(FirstDateTime.Date); + new DateTimeEntity(SecondDateTime); + new DateTimeEntity(SecondDateTime.Date); + new DateTimeEntity(new DateTime(FirstDateTime.Year, FirstDateTime.Month, FirstDateTime.Day, FirstDateTime.Hour, FirstDateTime.Minute, 0)); + new DateTimeEntity(new DateTime(FirstDateTime.Ticks, DateTimeKind.Local)); + new DateTimeEntity(FirstDateTime.Add(new TimeSpan(987, 23, 34, 45))); + new DateTimeEntity(FirstDateTime.AddYears(1)); + new DateTimeEntity(FirstDateTime.AddYears(-2)); + new DateTimeEntity(FirstDateTime.AddMonths(44)); + new DateTimeEntity(FirstDateTime.AddMonths(-55)); + new DateTimeEntity(SecondDateTime.AddHours(5)); + new DateTimeEntity(SecondDateTime.AddHours(-15)); + new DateTimeEntity(SecondDateTime.AddMinutes(59)); + new DateTimeEntity(SecondDateTime.AddMinutes(-49)); + new DateTimeEntity(SecondDateTime.AddSeconds(57)); + new DateTimeEntity(SecondDateTime.AddSeconds(-5)); var dateTime = FirstDateTime.AddYears(10); for (var i = 0; i < 60; ++i) - new DateTimeEntity { DateTime = dateTime.AddSeconds(i) }; + new DateTimeEntity(dateTime.AddSeconds(i)); new MillisecondDateTimeEntity { DateTime = FirstMillisecondDateTime }; new MillisecondDateTimeEntity { DateTime = FirstMillisecondDateTime }; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs index e183016ded..97ae038ac3 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Xtensive LLC. +// Copyright (C) 2016 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Alex Groznov @@ -22,6 +22,17 @@ public class SingleDateTimeEntity : Entity [Field] public DateTime? NullableDateTime { get; set; } + +#if DO_DATEONLY + [Field] + public DateOnly DateOnly { get; set; } + + [Field] + public DateOnly? NullableDateOnly { get; set; } + + [Field] + public TimeOnly TimeOnly { get; set; } +#endif } [HierarchyRoot] @@ -48,6 +59,23 @@ public class DateTimeEntity : Entity [Field] public DateTime DateTime { get; set; } + +#if DO_DATEONLY + [Field] + public DateOnly DateOnly { get; set; } + + [Field] + public TimeOnly TimeOnly { get; set; } +#endif + + public DateTimeEntity(DateTime dateTime) + { + DateTime = dateTime; +#if DO_DATEONLY + DateOnly = DateOnly.FromDateTime(dateTime); + TimeOnly = TimeOnly.FromDateTime(dateTime); +#endif + } } [HierarchyRoot] diff --git a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs index 770455d796..90f0867bb3 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs @@ -4,6 +4,8 @@ // Created by: Alexey Kulakov // Created: 2017.04.05 +using System; + namespace Xtensive.Orm.Tests.Storage.SchemaSharing.EntityManipulation.Model { namespace Part1 @@ -17,6 +19,14 @@ public class TestEntity1 : Entity [Field] public string Text { get; set; } +#if DO_DATEONLY + [Field] + public DateOnly DateOnly { get; set; } + + [Field] + public TimeOnly TimeOnly { get; set; } +#endif + [Field] public string DatabaseName { get; set; } diff --git a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs index e9bf5dd3e2..80fa8d59ab 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs @@ -405,11 +405,23 @@ private void Update(Session session, Key[] createdKeys, int initialCountOfEntiti Assert.That(a.DatabaseName, Is.EqualTo(databaseAndSchema.First)); Assert.That(a.SchemaName, Is.EqualTo(databaseAndSchema.Second)); + var now = DateTime.UtcNow; + a.Text = updatedText; +#if DO_DATEONLY + var dateOnly = DateOnly.FromDateTime(now); + var timeOnly = TimeOnly.FromDateTime(now); + a.DateOnly = dateOnly; + a.TimeOnly = timeOnly; +#endif session.SaveChanges(); Assert.That(session.Query.All().Count(), Is.EqualTo(initialCountOfEntities + 1)); Assert.That(session.Query.All().FirstOrDefault(e => e.Text == updatedText), Is.Not.Null); +#if DO_DATEONLY + Assert.AreEqual(session.Query.All().FirstOrDefault(e => e.Text == updatedText).DateOnly, dateOnly); + Assert.AreEqual(session.Query.All().FirstOrDefault(e => e.Text == updatedText).TimeOnly, timeOnly); +#endif Assert.That(session.Query.All().FirstOrDefault(e => e.Text == text), Is.Null); Assert.That( session.Query.All() diff --git a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs index bb67b45686..0a33ea6034 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs @@ -25,6 +25,11 @@ public class Book : Entity [Field] public DateTime Date { get; set; } +#if DO_DATEONLY + [Field] + public DateOnly DateOnly { get; set; } +#endif + [Field] public Direction? NullableDirection { get; set; } @@ -61,6 +66,11 @@ public void CombinedTest() AssertIsNotCalled(() => { book.Title = "A"; }); AssertIsCalled (() => { book.Date = new DateTime(1,2,3); }); AssertIsNotCalled(() => { book.Date = new DateTime(1,2,3); }); + +#if DO_DATEONLY + AssertIsCalled (() => { book.DateOnly = new DateOnly(1,2,3); }); + AssertIsNotCalled(() => { book.DateOnly = new DateOnly(1,2,3); }); +#endif var image = new byte[] {1, 2, 3}; AssertIsCalled (() => { book.Image = image; }); diff --git a/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs b/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs index 55dd8b66f4..42668d4314 100644 --- a/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs +++ b/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs @@ -361,6 +361,18 @@ protected override Expression VisitConstant(ConstantExpression c) Write(c.Value.ToString()); Write("\")"); } +#if DO_DATEONLY + else if (type == WellKnownTypes.DateOnly) { + Write("DateOnly.Parse(\""); + Write(c.Value.ToString()); + Write("\")"); + } + else if (type == WellKnownTypes.TimeOnly) { + Write("TimeOnly.Parse(\""); + Write(c.Value.ToString()); + Write("\")"); + } +#endif else if (c.Value is Type typeValue) { Write("typeof("); Write(GetTypeName(typeValue)); diff --git a/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs b/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs index 9da5e7637d..99ff1e1e3d 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs @@ -45,9 +45,12 @@ public static bool IsAnonymousConstructor(this Expression expression) public static bool IsNewExpressionSupportedByStorage(this Expression expression) => expression.NodeType == ExpressionType.New - && (expression.Type == WellKnownTypes.TimeSpan - || expression.Type == WellKnownTypes.DateTime - || expression.Type == WellKnownTypes.DateTimeOffset); + && expression.Type switch { var t => + t == WellKnownTypes.TimeSpan || t == WellKnownTypes.DateTime || t == WellKnownTypes.DateTimeOffset +#if DO_DATEONLY + || t == WellKnownTypes.DateOnly || t == WellKnownTypes.TimeOnly +#endif + }; public static bool IsQuery(this Expression expression) => expression.Type.IsOfGenericInterface(WellKnownInterfaces.QueryableOfT); diff --git a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs index cbed9e56ea..775a064917 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs @@ -97,13 +97,16 @@ private static bool TypeIsStorageMappable(Type type) type = type.StripNullable(); return type.IsPrimitive || type.IsEnum || - type==WellKnownTypes.ByteArray || - type==WellKnownTypes.Decimal || - type==WellKnownTypes.String || - type==WellKnownTypes.DateTime || - type==WellKnownTypes.DateTimeOffset || - type==WellKnownTypes.Guid || - type==WellKnownTypes.TimeSpan; + type == WellKnownTypes.ByteArray || + type == WellKnownTypes.Decimal || + type == WellKnownTypes.String || + type == WellKnownTypes.DateTime || + type == WellKnownTypes.DateTimeOffset || +#if DO_DATEONLY + type == WellKnownTypes.DateOnly || type == WellKnownTypes.TimeOnly || +#endif + type == WellKnownTypes.Guid || + type == WellKnownTypes.TimeSpan; } diff --git a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs index 275cec41da..aa1a586aa6 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs @@ -50,9 +50,7 @@ public Delegate GetUntypedCompiler(MemberInfo target) { ArgumentValidator.EnsureArgumentNotNull(target, nameof(target)); - return compilers.TryGetValue(GetCompilerKey(target), out var compiler) - ? compiler - : null; + return compilers.GetValueOrDefault(GetCompilerKey(target)); } public Func GetCompiler(MemberInfo target) @@ -103,14 +101,15 @@ private void UpdateRegistry( { foreach (var (targetMember, compiler) in newRegistrations) { var key = GetCompilerKey(targetMember); - if (conflictHandlingMethod != ConflictHandlingMethod.Overwrite && compilers.ContainsKey(key)) { - if (conflictHandlingMethod == ConflictHandlingMethod.ReportError) { - throw new InvalidOperationException(string.Format( - Strings.ExCompilerForXIsAlreadyRegistered, targetMember.GetFullName(true))); + if (!compilers.TryAdd(key, compiler)) { + switch (conflictHandlingMethod) { + case ConflictHandlingMethod.Overwrite: + compilers[key] = compiler; + break; + case ConflictHandlingMethod.ReportError: + throw new InvalidOperationException(string.Format(Strings.ExCompilerForXIsAlreadyRegistered, targetMember.GetFullName(true))); } - continue; } - compilers[key] = compiler; } } @@ -304,8 +303,7 @@ private static void ValidateCompilerParameter(ParameterInfo parameter, Type requ private static CompilerKey GetCompilerKey(MemberInfo member) { var canonicalMember = member; - var sourceProperty = canonicalMember as PropertyInfo; - if (sourceProperty!=null) { + if (canonicalMember is PropertyInfo sourceProperty) { canonicalMember = sourceProperty.GetGetMethod(); // GetGetMethod returns null in case of non public getter. if (canonicalMember==null) { diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs index 3bb91be105..2f77209b25 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs @@ -194,9 +194,8 @@ private static SqlExpression OracleBlobCompare(SqlExpression left, SqlExpression private SqlExpression CompileMember(MemberInfo member, SqlExpression instance, params SqlExpression[] arguments) { - var memberCompiler = memberCompilerProvider.GetCompiler(member); - if (memberCompiler==null) - throw new NotSupportedException(string.Format(Strings.ExMemberXIsNotSupported, member.GetFullName(true))); + var memberCompiler = memberCompilerProvider.GetCompiler(member) + ?? throw new NotSupportedException(string.Format(Strings.ExMemberXIsNotSupported, member.GetFullName(true))); return memberCompiler.Invoke(instance, arguments); } diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs index 400c48abbd..5cfbbbec05 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2020 Xtensive LLC. +// 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 @@ -13,80 +13,58 @@ namespace Xtensive.Orm.Providers { internal static class ExpressionTranslationHelpers { - public static SqlExpression ToBool(SqlExpression target) - { - return Cast(target, WellKnownTypes.Bool); - } + public static SqlExpression ToBool(SqlExpression target) => + Cast(target, WellKnownTypes.Bool); - public static SqlExpression ToInt(SqlExpression target) - { - return Cast(target, WellKnownTypes.Int32); - } + public static SqlExpression ToInt(SqlExpression target) => + Cast(target, WellKnownTypes.Int32); - public static SqlExpression ToDouble(SqlExpression target) - { - return Cast(target, WellKnownTypes.Double); - } + public static SqlExpression ToDouble(SqlExpression target) => + Cast(target, WellKnownTypes.Double); - public static SqlExpression ToLong(SqlExpression target) - { - return Cast(target, WellKnownTypes.Int64); - } + public static SqlExpression ToLong(SqlExpression target) => + Cast(target, WellKnownTypes.Int64); - public static SqlExpression ToSbyte(SqlExpression target) - { - return Cast(target, WellKnownTypes.SByte); - } + public static SqlExpression ToSbyte(SqlExpression target) => + Cast(target, WellKnownTypes.SByte); - public static SqlExpression ToShort(SqlExpression target) - { - return Cast(target, WellKnownTypes.Int16); - } + public static SqlExpression ToShort(SqlExpression target) => + Cast(target, WellKnownTypes.Int16); - public static SqlExpression ToFloat(SqlExpression target) - { - return Cast(target, WellKnownTypes.Single); - } + public static SqlExpression ToFloat(SqlExpression target) => + Cast(target, WellKnownTypes.Single); - public static SqlExpression ToDecimal(SqlExpression target) - { - return Cast(target, WellKnownTypes.Decimal); - } + public static SqlExpression ToDecimal(SqlExpression target) => + Cast(target, WellKnownTypes.Decimal); - public static SqlExpression ToByte(SqlExpression target) - { - return Cast(target, WellKnownTypes.Byte); - } + public static SqlExpression ToByte(SqlExpression target) => + Cast(target, WellKnownTypes.Byte); - public static SqlExpression ToChar(SqlExpression target) - { - return Cast(target, WellKnownTypes.String); - } + public static SqlExpression ToChar(SqlExpression target) => + Cast(target, WellKnownTypes.String); - public static SqlExpression ToDateTime(SqlExpression target) - { - return Cast(target, WellKnownTypes.DateTime); - } + public static SqlExpression ToDateTime(SqlExpression target) => + Cast(target, WellKnownTypes.DateTime); - public static SqlExpression ToUint(SqlExpression target) - { - return Cast(target, WellKnownTypes.UInt32); - } +#if DO_DATEONLY + public static SqlExpression ToDate(SqlExpression target) => + Cast(target, WellKnownTypes.DateOnly); - public static SqlExpression ToUlong(SqlExpression target) - { - return Cast(target, WellKnownTypes.UInt64); - } + public static SqlExpression ToTime(SqlExpression target) => + Cast(target, WellKnownTypes.TimeOnly); +#endif - public static SqlExpression ToUshort(SqlExpression target) - { - return Cast(target, WellKnownTypes.UInt16); - } + public static SqlExpression ToUint(SqlExpression target) => + Cast(target, WellKnownTypes.UInt32); - public static SqlExpression ToString(SqlExpression target) - { - return Cast(target, WellKnownTypes.String); - } + public static SqlExpression ToUlong(SqlExpression target) => + Cast(target, WellKnownTypes.UInt64); + + public static SqlExpression ToUshort(SqlExpression target) => + Cast(target, WellKnownTypes.UInt16); + + public static SqlExpression ToString(SqlExpression target) => + Cast(target, WellKnownTypes.String); private static SqlExpression Cast(SqlExpression target, Type type) { @@ -94,4 +72,4 @@ private static SqlExpression Cast(SqlExpression target, Type type) return SqlDml.Cast(target, destinationType); } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs index df617de4e7..2028f1c16a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2003-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.02.14 @@ -18,58 +18,40 @@ internal static class DateTimeCompilers #region Extractors [Compiler(typeof(DateTime), "Year", TargetKind.PropertyGet)] - public static SqlExpression DateTimeYear(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Year, _this)); - } + public static SqlExpression DateTimeYear(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Year, _this)); [Compiler(typeof(DateTime), "Month", TargetKind.PropertyGet)] - public static SqlExpression DateTimeMonth(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Month, _this)); - } + public static SqlExpression DateTimeMonth(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Month, _this)); [Compiler(typeof(DateTime), "Day", TargetKind.PropertyGet)] - public static SqlExpression DateTimeDay(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Day, _this)); - } + public static SqlExpression DateTimeDay(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Day, _this)); [Compiler(typeof(DateTime), "Hour", TargetKind.PropertyGet)] - public static SqlExpression DateTimeHour(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Hour, _this)); - } + public static SqlExpression DateTimeHour(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Hour, _this)); [Compiler(typeof(DateTime), "Minute", TargetKind.PropertyGet)] - public static SqlExpression DateTimeMinute(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Minute, _this)); - } + public static SqlExpression DateTimeMinute(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Minute, _this)); [Compiler(typeof(DateTime), "Second", TargetKind.PropertyGet)] - public static SqlExpression DateTimeSecond(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Second, _this)); - } + public static SqlExpression DateTimeSecond(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Second, _this)); [Compiler(typeof(DateTime), "Millisecond", TargetKind.PropertyGet)] - public static SqlExpression DateTimeMillisecond(SqlExpression _this) - { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); - } + public static SqlExpression DateTimeMillisecond(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); [Compiler(typeof(DateTime), "TimeOfDay", TargetKind.PropertyGet)] - public static SqlExpression DateTimeTimeOfDay(SqlExpression _this) - { - return SqlDml.DateTimeMinusDateTime(_this, SqlDml.DateTimeTruncate(_this)); - } + public static SqlExpression DateTimeTimeOfDay(SqlExpression _this) => + SqlDml.DateTimeMinusDateTime(_this, SqlDml.DateTimeTruncate(_this)); [Compiler(typeof(DateTime), "Date", TargetKind.PropertyGet)] - public static SqlExpression DateTimeDate(SqlExpression _this) - { - return SqlDml.DateTimeTruncate(_this); - } + public static SqlExpression DateTimeDate(SqlExpression _this) => + SqlDml.DateTimeTruncate(_this); [Compiler(typeof(DateTime), "DayOfWeek", TargetKind.PropertyGet)] public static SqlExpression DateTimeDayOfWeek(SqlExpression _this) @@ -91,6 +73,36 @@ public static SqlExpression DateTimeDayOfYear(SqlExpression _this) return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfYear, _this)); } +#if DO_DATEONLY + [Compiler(typeof(DateOnly), "Year", TargetKind.PropertyGet)] + public static SqlExpression DateOnlyYear(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Year, _this)); + + [Compiler(typeof(DateOnly), "Month", TargetKind.PropertyGet)] + public static SqlExpression DateOnlyMonth(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Month, _this)); + + [Compiler(typeof(DateOnly), "Day", TargetKind.PropertyGet)] + public static SqlExpression DateOnlyDay(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Day, _this)); + + [Compiler(typeof(TimeOnly), "Hour", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyHour(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Hour, _this)); + + [Compiler(typeof(TimeOnly), "Minute", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyMinute(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Minute, _this)); + + [Compiler(typeof(TimeOnly), "Second", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlySecond(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Second, _this)); + + [Compiler(typeof(TimeOnly), "Millisecond", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyMillisecond(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); +#endif // DO_DATEONLY + #endregion #region Constructors @@ -112,7 +124,7 @@ private static SqlExpression DateTimeConstruct( [Compiler(typeof(DateTime), null, TargetKind.Constructor)] public static SqlExpression DateTimeCtor( [Type(typeof(int))] SqlExpression year, - [Type(typeof(int))] SqlExpression month, + [Type(typeof(int))] SqlExpression month, [Type(typeof(int))] SqlExpression day) { return SqlDml.DateTimeConstruct(year, month, day); @@ -143,6 +155,32 @@ public static SqlExpression DateTimeCtor( return DateTimeConstruct(year, month, day, hour, minute, second, millisecond); } +#if DO_DATEONLY + [Compiler(typeof(DateOnly), null, TargetKind.Constructor)] + public static SqlExpression DateOnlyCtor( + [Type(typeof(int))] SqlExpression year, + [Type(typeof(int))] SqlExpression month, + [Type(typeof(int))] SqlExpression day) => + SqlDml.DateOnlyConstruct(year, month, day); + + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor( + [Type(typeof(int))] SqlExpression hour, + [Type(typeof(int))] SqlExpression minute, + [Type(typeof(int))] SqlExpression second) => + SqlDml.TimeOnlyConstruct(hour, minute, second, 0); + + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor( + [Type(typeof(int))] SqlExpression hour, + [Type(typeof(int))] SqlExpression minute) => + SqlDml.TimeOnlyConstruct(hour, minute, 0, 0); + + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression ticks) => + new SqlFunctionCall(SqlFunctionType.TimeOnlyConstruct, ticks); +#endif // DO_DATEONLY + #endregion #region Operators @@ -220,95 +258,158 @@ public static SqlExpression DateTimeOperatorSubtractionDateTime( return SqlDml.DateTimeMinusDateTime(d1, d2); } - #endregion - - [Compiler(typeof(DateTime), "Add")] - public static SqlExpression DateTimeAdd(SqlExpression _this, - [Type(typeof(TimeSpan))] SqlExpression value) +#if DO_DATEONLY + [Compiler(typeof(DateOnly), Operator.Equality, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorEquality( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) { - return SqlDml.DateTimePlusInterval(_this, value); + return d1 == d2; } - [Compiler(typeof(DateTime), "AddYears")] - public static SqlExpression DateTimeAddYears(SqlExpression _this, - [Type(typeof(int))] SqlExpression value) + [Compiler(typeof(DateOnly), Operator.Inequality, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorInequality( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) { - return SqlDml.DateTimeAddYears(_this, value); + return d1 != d2; } - [Compiler(typeof(DateTime), "AddMonths")] - public static SqlExpression DateTimeAddMonths(SqlExpression _this, - [Type(typeof(int))] SqlExpression value) + [Compiler(typeof(DateOnly), Operator.GreaterThan, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorGreaterThan( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) { - return SqlDml.DateTimeAddMonths(_this, value); + return d1 > d2; } - [Compiler(typeof(DateTime), "AddDays")] - public static SqlExpression DateTimeAddDays(SqlExpression _this, - [Type(typeof(double))] SqlExpression value) + [Compiler(typeof(DateOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorGreaterThanOrEqual( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) { - return SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromDays(value)); + return d1 >= d2; } - [Compiler(typeof(DateTime), "AddHours")] - public static SqlExpression DateTimeAddHours(SqlExpression _this, - [Type(typeof(double))] SqlExpression value) + [Compiler(typeof(DateOnly), Operator.LessThan, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorLessThan( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) { - return SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromHours(value)); + return d1 < d2; } - [Compiler(typeof(DateTime), "AddMinutes")] - public static SqlExpression DateTimeAddMinutes(SqlExpression _this, - [Type(typeof(double))] SqlExpression value) + [Compiler(typeof(DateOnly), Operator.LessThanOrEqual, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorLessThanOrEqual( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) { - return SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromMinutes(value)); + return d1 <= d2; } - [Compiler(typeof(DateTime), "AddSeconds")] - public static SqlExpression DateTimeAddSeconds(SqlExpression _this, - [Type(typeof(double))] SqlExpression value) + [Compiler(typeof(TimeOnly), Operator.Equality, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorEquality( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { - return SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromSeconds(value)); + return d1 == d2; } - [Compiler(typeof(DateTime), "AddMilliseconds")] - public static SqlExpression DateTimeAddMilliseconds(SqlExpression _this, - [Type(typeof(double))] SqlExpression value) + [Compiler(typeof(TimeOnly), Operator.Inequality, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorInequality( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { - return SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromMilliseconds(value)); + return d1 != d2; } - [Compiler(typeof(DateTime), "Subtract")] - public static SqlExpression DateTimeSubtractTimeSpan(SqlExpression _this, - [Type(typeof(TimeSpan))] SqlExpression value) + [Compiler(typeof(TimeOnly), Operator.GreaterThan, TargetKind.Operator)] + public static SqlExpression TimeOnlyyOperatorGreaterThan( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { - return SqlDml.DateTimeMinusInterval(_this, value); + return d1 > d2; } - [Compiler(typeof(DateTime), "Subtract")] - public static SqlExpression DateTimeSubtractDateTime(SqlExpression _this, - [Type(typeof(DateTime))] SqlExpression value) + [Compiler(typeof(TimeOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorGreaterThanOrEqual( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { - return SqlDml.DateTimeMinusDateTime(_this, value); + return d1 >= d2; } - [Compiler(typeof(DateTime), "Now", TargetKind.Static | TargetKind.PropertyGet)] - public static SqlExpression DateTimeNow() + [Compiler(typeof(TimeOnly), Operator.LessThan, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorLessThan( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { - return SqlDml.CurrentTimeStamp(); + return d1 < d2; } - [Compiler(typeof(DateTime), "Today", TargetKind.Static | TargetKind.PropertyGet)] - public static SqlExpression DateTimeToday() + [Compiler(typeof(TimeOnly), Operator.LessThanOrEqual, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorLessThanOrEqual( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { - return SqlDml.CurrentDate(); + return d1 <= d2; } +#endif // DO_DATEONLY + + #endregion + + [Compiler(typeof(DateTime), "Add")] + public static SqlExpression DateTimeAdd(SqlExpression _this, [Type(typeof(TimeSpan))] SqlExpression value) => + SqlDml.DateTimePlusInterval(_this, value); + + [Compiler(typeof(DateTime), "AddYears")] + public static SqlExpression DateTimeAddYears(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateTimeAddYears(_this, value); + + [Compiler(typeof(DateTime), "AddMonths")] + public static SqlExpression DateTimeAddMonths(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateTimeAddMonths(_this, value); + + [Compiler(typeof(DateTime), "AddDays")] + public static SqlExpression DateTimeAddDays(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromDays(value)); + + [Compiler(typeof(DateTime), "AddHours")] + public static SqlExpression DateTimeAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromHours(value)); + + [Compiler(typeof(DateTime), "AddMinutes")] + public static SqlExpression DateTimeAddMinutes(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromMinutes(value)); + + [Compiler(typeof(DateTime), "AddSeconds")] + public static SqlExpression DateTimeAddSeconds(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromSeconds(value)); + + [Compiler(typeof(DateTime), "AddMilliseconds")] + public static SqlExpression DateTimeAddMilliseconds(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.DateTimePlusInterval(_this, TimeSpanCompilers.TimeSpanFromMilliseconds(value)); + + [Compiler(typeof(DateTime), "Subtract")] + public static SqlExpression DateTimeSubtractTimeSpan(SqlExpression _this, [Type(typeof(TimeSpan))] SqlExpression value) => + SqlDml.DateTimeMinusInterval(_this, value); + + [Compiler(typeof(DateTime), "Subtract")] + public static SqlExpression DateTimeSubtractDateTime(SqlExpression _this, [Type(typeof(DateTime))] SqlExpression value) => + SqlDml.DateTimeMinusDateTime(_this, value); + + [Compiler(typeof(DateTime), "Now", TargetKind.Static | TargetKind.PropertyGet)] + public static SqlExpression DateTimeNow() => + SqlDml.CurrentTimeStamp(); + + [Compiler(typeof(DateTime), "Today", TargetKind.Static | TargetKind.PropertyGet)] + public static SqlExpression DateTimeToday() => + SqlDml.CurrentDate(); + [Compiler(typeof(DateTime), "IsLeapYear", TargetKind.Static | TargetKind.Method)] - public static SqlExpression DateTimeIsLeapYear([Type(typeof(int))] SqlExpression year) - { - return ((year % 4==0) && (year % 100!=0)) || (year % 400==0); - } + public static SqlExpression DateTimeIsLeapYear([Type(typeof(int))] SqlExpression year) => + ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); [Compiler(typeof(DateTime), "DaysInMonth", TargetKind.Static | TargetKind.Method)] public static SqlExpression DateTimeDaysInMonth( @@ -321,7 +422,7 @@ public static SqlExpression DateTimeDaysInMonth( var result = SqlDml.Case(); result.Add(SqlDml.In(month, SqlDml.Array(1, 3, 5, 7, 8, 10, 12)), 31); - result.Add(month==2, februaryCase); + result.Add(month == 2, februaryCase); result.Else = 30; return result; @@ -338,13 +439,36 @@ public static SqlExpression DateTimeToStringIso(SqlExpression _this, [Type(typeo { var stringValue = value as SqlLiteral; - if (stringValue==null) + if (stringValue == null) throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); - + if (!stringValue.Value.Equals("s")) throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); return SqlDml.DateTimeToStringIso(_this); } + +#if DO_DATEONLY + [Compiler(typeof(DateOnly), "AddYears")] + public static SqlExpression DateOnlyAddYears(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateTimeAddYears(_this, value); + + [Compiler(typeof(DateOnly), "AddMonths")] + public static SqlExpression DateOnlyAddMonths(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateTimeAddMonths(_this, value); + + [Compiler(typeof(DateOnly), "AddDays")] + public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateOnlyAddDays(_this, value); + + [Compiler(typeof(TimeOnly), "AddHours")] + public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.TimeOnlyAddHours(_this, value); + + [Compiler(typeof(TimeOnly), "AddMinutes")] + public static SqlExpression TimeOnlyAddMinutes(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.TimeOnlyAddMinutes(_this, value); + +#endif // DO_DATEONLY } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs index 470d7b3d64..e7c48caf3c 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs @@ -303,8 +303,18 @@ private SqlExpression GetOrderByExpression(SqlExpression expression, SortProvide } var columnType = columns[index].Type; - if (providerInfo.Supports(ProviderFeatures.DateTimeEmulation) && columnType == WellKnownTypes.DateTime) { - return SqlDml.Cast(expression, SqlType.DateTime); + if (providerInfo.Supports(ProviderFeatures.DateTimeEmulation)) { + if (columnType == WellKnownTypes.DateTime) { + return SqlDml.Cast(expression, SqlType.DateTime); + } +#if DO_DATEONLY + if (columnType == WellKnownTypes.DateOnly) { + return SqlDml.Cast(expression, SqlType.Date); + } + if (columnType == WellKnownTypes.TimeOnly) { + return SqlDml.Cast(expression, SqlType.Time); + } +#endif } if (providerInfo.Supports(ProviderFeatures.DateTimeOffsetEmulation) && columnType == WellKnownTypes.DateTimeOffset) { @@ -328,6 +338,22 @@ private SqlExpression GetJoinExpression(SqlExpression leftExpression, SqlExpress if (columnPair.Second.Type == WellKnownTypes.DateTime) { rightExpression = SqlDml.Cast(rightExpression, SqlType.DateTime); } + +#if DO_DATEONLY + if (columnPair.First.Type == WellKnownTypes.DateOnly) { + leftExpression = SqlDml.Cast(leftExpression, SqlType.Date); + } + else if (columnPair.First.Type == WellKnownTypes.TimeOnly) { + leftExpression = SqlDml.Cast(leftExpression, SqlType.Time); + } + + if (columnPair.Second.Type == WellKnownTypes.DateOnly) { + rightExpression = SqlDml.Cast(rightExpression, SqlType.Date); + } + else if (columnPair.Second.Type == WellKnownTypes.TimeOnly) { + rightExpression = SqlDml.Cast(rightExpression, SqlType.Time); + } +#endif } if (providerInfo.Supports(ProviderFeatures.DateTimeOffsetEmulation)) { diff --git a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs index 6c4c876b67..9e0f7b524b 100644 --- a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs +++ b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs @@ -57,6 +57,12 @@ internal static class WellKnownTypes public static readonly Type NullableDateTime = typeof(DateTime?); public static readonly Type DateTimeOffset = typeof(DateTimeOffset); public static readonly Type NullableDateTimeOffset = typeof(DateTimeOffset?); +#if DO_DATEONLY + public static readonly Type DateOnly = typeof(DateOnly); + public static readonly Type TimeOnly = typeof(TimeOnly); + public static readonly Type NullableDateOnly = typeof(DateOnly?); + public static readonly Type NullableTimeOnly = typeof(TimeOnly?); +#endif public static readonly Type Guid = typeof(Guid); public static readonly Type NullableGuid = typeof(Guid?); diff --git a/Orm/Xtensive.Orm/Sql/Compiler/Internals/PostCompiler.cs b/Orm/Xtensive.Orm/Sql/Compiler/Internals/PostCompiler.cs index 36ed234611..24943d17ee 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/Internals/PostCompiler.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/Internals/PostCompiler.cs @@ -39,10 +39,7 @@ public override void Visit(TextNode node) public override void Visit(VariantNode node) { - if (configuration.AlternativeBranches.Contains(node.Id)) - VisitNodes(node.Alternative); - else - VisitNodes(node.Main); + VisitNodes(configuration.AlternativeBranches.Contains(node.Id) ? node.Alternative : node.Main); } public override void Visit(PlaceholderNode node) diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs index eb24539d64..87bd7eecfd 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs @@ -1170,7 +1170,7 @@ public virtual void Visit(SqlFunctionCall node) { using (context.EnterScope(node)) { AppendTranslatedEntry(node); - + if (node.Arguments.Count > 0) { using (context.EnterCollectionScope()) { var argumentPosition = 0; diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs index f57fe08b7c..d120961333 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs @@ -72,12 +72,16 @@ public abstract class SqlTranslator : SqlDriverBound /// public abstract string DateTimeFormatString { get; } + public virtual string DateOnlyFormatString => throw new NotImplementedException(); + /// /// Gets the time span format string. /// See for details. /// public abstract string TimeSpanFormatString { get; } + public virtual string TimeOnlyFormatString => throw new NotImplementedException(); + /// /// Gets the parameter prefix. /// @@ -1518,6 +1522,14 @@ public virtual void Translate(SqlCompilerContext context, object literalValue) case Guid: case byte[]: throw new NotSupportedException(string.Format(Strings.ExTranslationOfLiteralOfTypeXIsNotSupported, literalType.GetShortName())); +#if DO_DATEONLY + case DateOnly dateOnly: + output.Append(dateOnly.ToString(DateOnlyFormatString, DateTimeFormat)); + break; + case TimeOnly timeOnly: + output.Append(timeOnly.ToString(TimeOnlyFormatString, DateTimeFormat)); + break; +#endif default: _ = output.Append(literalValue.ToString()); break; diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index a5d4701383..01c9084846 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -64,6 +64,9 @@ public enum SqlFunctionType DateTimeConstruct, DateTimeAddYears, DateTimeAddMonths, + DateOnlyAddDays, + TimeOnlyAddHours, + TimeOnlyAddMinutes, DateTimeTruncate, DateTimeToStringIso, IntervalConstruct, @@ -81,6 +84,11 @@ public enum SqlFunctionType DateTimeOffsetToUtcTime, DateTimeToDateTimeOffset, +#if DO_DATEONLY + DateOnlyConstruct, + TimeOnlyConstruct, +#endif + // .NET like rounding functions RoundDecimalToEven, diff --git a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs index 6209cb8d52..d99839c3dc 100644 --- a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs @@ -27,8 +27,7 @@ public class DataTypeCollection : LockableBase, IEnumerable /// The instance. public DataTypeInfo this[string nativeType] { - get - { + get { DataTypeInfo result; nativeTypes.TryGetValue(nativeType, out result); return result; @@ -41,8 +40,7 @@ public DataTypeInfo this[string nativeType] /// The instance. public DataTypeInfo this[SqlType sqlType] { - get - { + get { DataTypeInfo result; sqlTypes.TryGetValue(sqlType, out result); return result; @@ -112,7 +110,7 @@ public void Add(SqlType sqlType, DataTypeInfo dataTypeInfo) public DataTypeInfo Decimal { get; set; } /// - /// Floating point number data from –3.40E + 38 through 3.40E + 38. + /// Floating point number data from –3.40E + 38 through 3.40E + 38. /// Storage size is 4 bytes. /// public DataTypeInfo Float { get; set; } @@ -144,6 +142,11 @@ public void Add(SqlType sqlType, DataTypeInfo dataTypeInfo) /// public DataTypeInfo Interval { get; set; } +#if DO_DATEONLY + public DataTypeInfo DateOnly { get; set; } + public DataTypeInfo TimeOnly { get; set; } +#endif + /// /// Fixed-length Unicode character data of n characters. /// n must be a value from 1 through 4,000. Storage size is two times n bytes. @@ -195,7 +198,7 @@ public override void Lock(bool recursive) base.Lock(recursive); foreach (DataTypeInfo item in this) { - if (item==null) + if (item == null) continue; sqlTypes[item.Type] = item; foreach (var type in item.NativeTypes) @@ -236,6 +239,10 @@ IEnumerator IEnumerable.GetEnumerator() yield return VarBinaryMax; yield return Guid; yield return Interval; +#if DO_DATEONLY + yield return DateOnly; + yield return TimeOnly; +#endif yield break; } diff --git a/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs b/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs index e8c7569b24..02a2056845 100644 --- a/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs +++ b/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2003-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.07.21 @@ -78,5 +78,10 @@ public partial class ValueRange /// public static readonly ValueRange TimeSpan = new ValueRange(System.TimeSpan.MinValue, System.TimeSpan.MaxValue); + +#if DO_DATEONLY + public static readonly ValueRange DateOnly = new(System.DateOnly.MinValue, System.DateOnly.MaxValue); + public static readonly ValueRange TimeOnly = new(System.TimeOnly.MinValue, System.TimeOnly.MaxValue); +#endif } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs index e7d3891f93..54ec399a13 100644 --- a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs +++ b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs @@ -31,6 +31,10 @@ internal static class SqlValidator WellKnownTypes.DateTime, WellKnownTypes.DateTimeOffset, WellKnownTypes.TimeSpan, +#if DO_DATEONLY + WellKnownTypes.DateOnly, + WellKnownTypes.TimeOnly, +#endif WellKnownTypes.ByteArray, WellKnownTypes.Guid }; diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index df19d92fd3..39445b9e94 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -564,6 +564,29 @@ public static SqlFunctionCall DateTimeConstruct(SqlExpression year, SqlExpressio return new SqlFunctionCall(SqlFunctionType.DateTimeConstruct, year, month, day); } +#if DO_DATEONLY + public static SqlFunctionCall DateOnlyConstruct(SqlExpression year, SqlExpression month, SqlExpression day) + { + ArgumentNullException.ThrowIfNull(year); + ArgumentNullException.ThrowIfNull(month); + ArgumentNullException.ThrowIfNull(day); + SqlValidator.EnsureIsArithmeticExpression(year); + SqlValidator.EnsureIsArithmeticExpression(month); + SqlValidator.EnsureIsArithmeticExpression(day); + return new SqlFunctionCall(SqlFunctionType.DateOnlyConstruct, year, month, day); + } + + public static SqlFunctionCall TimeOnlyConstruct(SqlExpression hours, + SqlExpression minutes, + SqlExpression seconds, + SqlExpression milliseconds) + { + var m = milliseconds + 1000L * (seconds + 60L * (minutes + 60L * hours)); + var ticks = 10_000 * m; + return new SqlFunctionCall(SqlFunctionType.TimeOnlyConstruct, ticks); + } +#endif + public static SqlBinary DateTimePlusInterval(SqlExpression left, SqlExpression right) { ArgumentValidator.EnsureArgumentNotNull(left, "left"); @@ -599,6 +622,27 @@ public static SqlFunctionCall DateTimeAddMonths(SqlExpression source, SqlExpress return new SqlFunctionCall(SqlFunctionType.DateTimeAddMonths, source, months); } + public static SqlFunctionCall DateOnlyAddDays(SqlExpression source, SqlExpression days) + { + ArgumentValidator.EnsureArgumentNotNull(source, "source"); + ArgumentValidator.EnsureArgumentNotNull(days, "days"); + return new SqlFunctionCall(SqlFunctionType.DateOnlyAddDays, source, days); + } + + public static SqlFunctionCall TimeOnlyAddHours(SqlExpression source, SqlExpression hours) + { + ArgumentValidator.EnsureArgumentNotNull(source, "source"); + ArgumentValidator.EnsureArgumentNotNull(hours, "hours"); + return new SqlFunctionCall(SqlFunctionType.TimeOnlyAddHours, source, hours); + } + + public static SqlFunctionCall TimeOnlyAddMinutes(SqlExpression source, SqlExpression minutes) + { + ArgumentValidator.EnsureArgumentNotNull(source, "source"); + ArgumentValidator.EnsureArgumentNotNull(minutes, "minutes"); + return new SqlFunctionCall(SqlFunctionType.TimeOnlyAddMinutes, source, minutes); + } + public static SqlFunctionCall DateTimeToStringIso(SqlExpression expression) { ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); diff --git a/Orm/Xtensive.Orm/Sql/SqlDriver.cs b/Orm/Xtensive.Orm/Sql/SqlDriver.cs index 107319c921..bf055dc4d8 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDriver.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDriver.cs @@ -421,6 +421,10 @@ private static void RegisterStandardMappings(TypeMappingRegistryBuilder builder) builder.Add(WellKnownTypes.TimeSpan, mapper.ReadTimeSpan, mapper.BindTimeSpan, mapper.MapTimeSpan); builder.Add(WellKnownTypes.Guid, mapper.ReadGuid, mapper.BindGuid, mapper.MapGuid); builder.Add(WellKnownTypes.ByteArray, mapper.ReadByteArray, mapper.BindByteArray, mapper.MapByteArray); +#if DO_DATEONLY + builder.Add(WellKnownTypes.DateOnly, mapper.ReadDateOnly, mapper.BindDateOnly, mapper.MapDateOnly); + builder.Add(WellKnownTypes.TimeOnly, mapper.ReadTimeOnly, mapper.BindTimeOnly, mapper.MapTimeOnly); +#endif } private static void RegisterStandardReverseMappings(TypeMappingRegistryBuilder builder) @@ -446,6 +450,10 @@ private static void RegisterStandardReverseMappings(TypeMappingRegistryBuilder b builder.AddReverse(SqlType.VarBinary, WellKnownTypes.ByteArray); builder.AddReverse(SqlType.VarBinaryMax, WellKnownTypes.ByteArray); builder.AddReverse(SqlType.Guid, WellKnownTypes.Guid); +#if DO_DATEONLY + builder.AddReverse(SqlType.Date, WellKnownTypes.DateOnly); + builder.AddReverse(SqlType.Time, WellKnownTypes.TimeOnly); +#endif } private Extractor BuildExtractor(SqlConnection connection) diff --git a/Orm/Xtensive.Orm/Sql/SqlType.cs b/Orm/Xtensive.Orm/Sql/SqlType.cs index 9cba773976..895a17a3b5 100644 --- a/Orm/Xtensive.Orm/Sql/SqlType.cs +++ b/Orm/Xtensive.Orm/Sql/SqlType.cs @@ -97,6 +97,20 @@ public struct SqlType : IEquatable /// public static readonly SqlType DateTimeOffset = new SqlType("DateTimeOffset"); + /// + /// Date from January 1, 1753 through December 31, 9999, + /// Storage size is 3 bytes. + /// + public static readonly SqlType Date = new SqlType("Date"); + + /// + /// Time data from to an accuracy of one three-hundredth of a second (equivalent to 3.33 + /// milliseconds or 0.00333 seconds). Values are rounded to increments + /// of .000, .003, or .007 seconds. + /// Storage size is 6 bytes. + /// + public static readonly SqlType Time = new SqlType("Time"); + /// /// Datetime interval. /// diff --git a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs index ebf1c576fd..e47ddb0206 100644 --- a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs +++ b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2021 Xtensive LLC. +// 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 @@ -165,85 +165,53 @@ public virtual void BindByteArray(DbParameter parameter, object value) #region ReadXxx methods - public virtual object ReadBoolean(DbDataReader reader, int index) - { - return reader.GetBoolean(index); - } + public virtual object ReadBoolean(DbDataReader reader, int index) => + reader.GetBoolean(index); - public virtual object ReadChar(DbDataReader reader, int index) - { - return reader.GetString(index).SingleOrDefault(); - } + public virtual object ReadChar(DbDataReader reader, int index) => + reader.GetString(index).SingleOrDefault(); - public virtual object ReadString(DbDataReader reader, int index) - { - return reader.GetString(index); - } + public virtual object ReadString(DbDataReader reader, int index) => + reader.GetString(index); - public virtual object ReadByte(DbDataReader reader, int index) - { - return reader.GetByte(index); - } + public virtual object ReadByte(DbDataReader reader, int index) => + reader.GetByte(index); - public virtual object ReadSByte(DbDataReader reader, int index) - { - return Convert.ToSByte(reader[index]); - } + public virtual object ReadSByte(DbDataReader reader, int index) => + Convert.ToSByte(reader[index]); - public virtual object ReadShort(DbDataReader reader, int index) - { - return reader.GetInt16(index); - } + public virtual object ReadShort(DbDataReader reader, int index) => + reader.GetInt16(index); - public virtual object ReadUShort(DbDataReader reader, int index) - { - return Convert.ToUInt16(reader[index]); - } + public virtual object ReadUShort(DbDataReader reader, int index) => + Convert.ToUInt16(reader[index]); - public virtual object ReadInt(DbDataReader reader, int index) - { - return reader.GetInt32(index); - } + public virtual object ReadInt(DbDataReader reader, int index) => + reader.GetInt32(index); - public virtual object ReadUInt(DbDataReader reader, int index) - { - return Convert.ToUInt32(reader[index]); - } + public virtual object ReadUInt(DbDataReader reader, int index) => + Convert.ToUInt32(reader[index]); - public virtual object ReadLong(DbDataReader reader, int index) - { - return reader.GetInt64(index); - } + public virtual object ReadLong(DbDataReader reader, int index) => + reader.GetInt64(index); - public virtual object ReadULong(DbDataReader reader, int index) - { - return Convert.ToUInt64(reader[index]); - } + public virtual object ReadULong(DbDataReader reader, int index) => + Convert.ToUInt64(reader[index]); - public virtual object ReadFloat(DbDataReader reader, int index) - { - return reader.GetFloat(index); - } + public virtual object ReadFloat(DbDataReader reader, int index) => + reader.GetFloat(index); - public virtual object ReadDouble(DbDataReader reader, int index) - { - return reader.GetDouble(index); - } + public virtual object ReadDouble(DbDataReader reader, int index) => + reader.GetDouble(index); - public virtual object ReadDecimal(DbDataReader reader, int index) - { - return reader.GetDecimal(index); - } + public virtual object ReadDecimal(DbDataReader reader, int index) => + reader.GetDecimal(index); - public virtual object ReadDateTime(DbDataReader reader, int index) - { - return reader.GetDateTime(index); - } + public virtual object ReadDateTime(DbDataReader reader, int index) => + reader.GetDateTime(index); - public virtual object ReadDateTimeOffset(DbDataReader reader, int index) - { - return (DateTimeOffset) reader.GetValue(index); - } + public virtual object ReadDateTimeOffset(DbDataReader reader, int index) => + (DateTimeOffset) reader.GetValue(index); public virtual object ReadTimeSpan(DbDataReader reader, int index) { @@ -257,10 +225,8 @@ public virtual object ReadTimeSpan(DbDataReader reader, int index) return TimeSpan.FromTicks(value / 100); } - public virtual object ReadGuid(DbDataReader reader, int index) - { - return reader.GetGuid(index); - } + public virtual object ReadGuid(DbDataReader reader, int index) => + reader.GetGuid(index); public virtual object ReadByteArray(DbDataReader reader, int index) { @@ -282,70 +248,44 @@ public virtual object ReadByteArray(DbDataReader reader, int index) #region MapXxx methods - public virtual SqlValueType MapBoolean(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Boolean); - } + public virtual SqlValueType MapBoolean(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Boolean); - public virtual SqlValueType MapChar(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.VarChar, 1); - } + public virtual SqlValueType MapChar(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.VarChar, 1); - public virtual SqlValueType MapString(int? length, int? precision, int? scale) - { - return ChooseStreamType(SqlType.VarChar, SqlType.VarCharMax, length, VarCharMaxLength); - } + public virtual SqlValueType MapString(int? length, int? precision, int? scale) => + ChooseStreamType(SqlType.VarChar, SqlType.VarCharMax, length, VarCharMaxLength); - public virtual SqlValueType MapByte(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.UInt8); - } + public virtual SqlValueType MapByte(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.UInt8); - public virtual SqlValueType MapSByte(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Int8); - } + public virtual SqlValueType MapSByte(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Int8); - public virtual SqlValueType MapShort(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Int16); - } + public virtual SqlValueType MapShort(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Int16); - public virtual SqlValueType MapUShort(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.UInt16); - } + public virtual SqlValueType MapUShort(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.UInt16); - public virtual SqlValueType MapInt(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Int32); - } + public virtual SqlValueType MapInt(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Int32); - public virtual SqlValueType MapUInt(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.UInt32); - } + public virtual SqlValueType MapUInt(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.UInt32); - public virtual SqlValueType MapLong(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Int64); - } + public virtual SqlValueType MapLong(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Int64); - public virtual SqlValueType MapULong(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.UInt64); - } + public virtual SqlValueType MapULong(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.UInt64); - public virtual SqlValueType MapFloat(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Float); - } + public virtual SqlValueType MapFloat(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Float); - public virtual SqlValueType MapDouble(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Double); - } + public virtual SqlValueType MapDouble(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Double); public virtual SqlValueType MapDecimal(int? length, int? precision, int? scale) { @@ -363,31 +303,47 @@ public virtual SqlValueType MapDecimal(int? length, int? precision, int? scale) return new SqlValueType(SqlType.Decimal, null, null, precision, scale); } - public virtual SqlValueType MapDateTime(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.DateTime); - } + public virtual SqlValueType MapDateTime(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.DateTime); - public virtual SqlValueType MapDateTimeOffset(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.DateTimeOffset); - } + public virtual SqlValueType MapDateTimeOffset(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.DateTimeOffset); - public virtual SqlValueType MapTimeSpan(int? length, int? precision, int? scale) - { - return new SqlValueType(SqlType.Int64); - } + public virtual SqlValueType MapTimeSpan(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Int64); + + public virtual SqlValueType MapGuid(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Guid); + + public virtual SqlValueType MapByteArray(int? length, int? precision, int? scale) => + ChooseStreamType(SqlType.VarBinary, SqlType.VarBinaryMax, length, VarBinaryMaxLength); - public virtual SqlValueType MapGuid(int? length, int? precision, int? scale) +#if DO_DATEONLY + public virtual void BindDateOnly(DbParameter parameter, object value) { - return new SqlValueType(SqlType.Guid); + parameter.DbType = DbType.Date; + parameter.Value = value != null ? ((DateOnly)value).ToDateTime(TimeOnly.MinValue) : DBNull.Value; } - public virtual SqlValueType MapByteArray(int? length, int? precision, int? scale) + public virtual void BindTimeOnly(DbParameter parameter, object value) { - return ChooseStreamType(SqlType.VarBinary, SqlType.VarBinaryMax, length, VarBinaryMaxLength); + parameter.DbType = DbType.Time; + parameter.Value = value != null ? ((TimeOnly)value).ToTimeSpan() : DBNull.Value; } + public virtual object ReadDateOnly(DbDataReader reader, int index) => + DateOnly.FromDateTime(reader.GetFieldValue(index)); + + public virtual object ReadTimeOnly(DbDataReader reader, int index) => + TimeOnly.FromTimeSpan(reader.GetFieldValue(index)); + + public virtual SqlValueType MapDateOnly(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Date); + + public virtual SqlValueType MapTimeOnly(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Time); +#endif // DO_DATEONLY + #endregion protected static SqlValueType ChooseStreamType(SqlType varType, SqlType varMaxType, int? length, int? varTypeMaxLength) diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs index 96c05a0401..f467983803 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs @@ -11,7 +11,7 @@ namespace Xtensive.Tuples.Packed { internal abstract class PackedFieldAccessor { - public static readonly PackedFieldAccessor[] All = new PackedFieldAccessor[18]; + public static readonly PackedFieldAccessor[] All = new PackedFieldAccessor[20]; /// /// Getter delegate. @@ -587,4 +587,32 @@ public DateTimeOffsetFieldAccessor() : base(GetSize() * 8, 17) { } } + +#if DO_DATEONLY + internal sealed class DateOnlyFieldAccessor : ValueFieldAccessor + { + protected override DateOnly Decode(long value) => + DateOnly.FromDayNumber((int)value); + + protected override long Encode(DateOnly value) => + value.DayNumber; + + public DateOnlyFieldAccessor() + : base(sizeof(int) * 8, 18) + { } + } + + internal sealed class TimeOnlyFieldAccessor : ValueFieldAccessor + { + protected override TimeOnly Decode(long value) => + new TimeOnly(value); + + protected override long Encode(TimeOnly value) => + value.Ticks; + + public TimeOnlyFieldAccessor() + : base(sizeof(long) * 8, 19) + { } + } +#endif } diff --git a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs index e554c77124..6342d49e25 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs @@ -34,22 +34,28 @@ private ref struct Counters private static class ValueFieldAccessorResolver { - private static readonly ValueFieldAccessor BoolAccessor = new BooleanFieldAccessor(); - private static readonly ValueFieldAccessor ByteAccessor = new ByteFieldAccessor(); - private static readonly ValueFieldAccessor SByteAccessor = new SByteFieldAccessor(); - private static readonly ValueFieldAccessor Int16Accessor = new ShortFieldAccessor(); - private static readonly ValueFieldAccessor UInt16Accessor = new UShortFieldAccessor(); - private static readonly ValueFieldAccessor Int32Accessor = new IntFieldAccessor(); - private static readonly ValueFieldAccessor UInt32Accessor = new UIntFieldAccessor(); - private static readonly ValueFieldAccessor Int64Accessor = new LongFieldAccessor(); - private static readonly ValueFieldAccessor UInt64Accessor = new ULongFieldAccessor(); - private static readonly ValueFieldAccessor SingleAccessor = new FloatFieldAccessor(); - private static readonly ValueFieldAccessor DoubleAccessor = new DoubleFieldAccessor(); - private static readonly ValueFieldAccessor DateTimeAccessor = new DateTimeFieldAccessor(); - private static readonly ValueFieldAccessor TimeSpanAccessor = new TimeSpanFieldAccessor(); - private static readonly ValueFieldAccessor DecimalAccessor = new DecimalFieldAccessor(); - private static readonly ValueFieldAccessor GuidAccessor = new GuidFieldAccessor(); - private static readonly ValueFieldAccessor DateTimeOffsetAccessor = new DateTimeOffsetFieldAccessor(); + private static readonly ValueFieldAccessor + BoolAccessor = new BooleanFieldAccessor(), + ByteAccessor = new ByteFieldAccessor(), + SByteAccessor = new SByteFieldAccessor(), + Int16Accessor = new ShortFieldAccessor(), + UInt16Accessor = new UShortFieldAccessor(), + Int32Accessor = new IntFieldAccessor(), + UInt32Accessor = new UIntFieldAccessor(), + Int64Accessor = new LongFieldAccessor(), + UInt64Accessor = new ULongFieldAccessor(), + SingleAccessor = new FloatFieldAccessor(), + DoubleAccessor = new DoubleFieldAccessor(), + DateTimeAccessor = new DateTimeFieldAccessor(), + TimeSpanAccessor = new TimeSpanFieldAccessor(), + DecimalAccessor = new DecimalFieldAccessor(), + GuidAccessor = new GuidFieldAccessor(), + DateTimeOffsetAccessor = new DateTimeOffsetFieldAccessor() +#if DO_DATEONLY + , DateOnlyAccessor = new DateOnlyFieldAccessor() + , TimeOnlyAccessor = new TimeOnlyFieldAccessor() +#endif + ; private static readonly int NullableTypeMetadataToken = WellKnownTypes.NullableOfT.MetadataToken; @@ -74,6 +80,10 @@ ValueFieldAccessor ResolveByType(Type type) ReferenceEquals(type, WellKnownTypes.Double) ? DoubleAccessor : ReferenceEquals(type, WellKnownTypes.DateTime) ? DateTimeAccessor : ReferenceEquals(type, WellKnownTypes.TimeSpan) ? TimeSpanAccessor : +#if DO_DATEONLY + ReferenceEquals(type, WellKnownTypes.DateOnly) ? DateOnlyAccessor : + ReferenceEquals(type, WellKnownTypes.TimeOnly) ? TimeOnlyAccessor : +#endif ReferenceEquals(type, WellKnownTypes.Decimal) ? DecimalAccessor : ReferenceEquals(type, WellKnownTypes.Guid) ? GuidAccessor : ReferenceEquals(type, WellKnownTypes.DateTimeOffset) ? DateTimeOffsetAccessor : null; @@ -94,6 +104,10 @@ ValueFieldAccessor ResolveByNullableType(Type type) ReferenceEquals(type, WellKnownTypes.NullableDouble) ? DoubleAccessor : ReferenceEquals(type, WellKnownTypes.NullableDateTime) ? DateTimeAccessor : ReferenceEquals(type, WellKnownTypes.NullableTimeSpan) ? TimeSpanAccessor : +#if DO_DATEONLY + ReferenceEquals(type, WellKnownTypes.NullableDateOnly) ? DateOnlyAccessor : + ReferenceEquals(type, WellKnownTypes.NullableTimeOnly) ? TimeOnlyAccessor : +#endif ReferenceEquals(type, WellKnownTypes.NullableDecimal) ? DecimalAccessor : ReferenceEquals(type, WellKnownTypes.NullableGuid) ? GuidAccessor : ReferenceEquals(type, WellKnownTypes.NullableDateTimeOffset) ? DateTimeOffsetAccessor : null; diff --git a/Orm/Xtensive.Orm/Xtensive.Orm.csproj b/Orm/Xtensive.Orm/Xtensive.Orm.csproj index 0cf0e1de2b..7e8dc6e0e9 100644 --- a/Orm/Xtensive.Orm/Xtensive.Orm.csproj +++ b/Orm/Xtensive.Orm/Xtensive.Orm.csproj @@ -16,7 +16,7 @@ 1591 - TRACE;DEBUG + $(DefineConstants);TRACE;DEBUG $(TargetsForTfmSpecificContentInPackage);IncludeWeaverFiles From d7eb8adb123d939f8b9145fc8273e75afcd98ce1 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 19 Jul 2022 18:51:26 -0700 Subject: [PATCH 02/86] Some refactoring --- Directory.Build.props | 1 - .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 6 ++-- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 32 +++++++++---------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 85f0735d62..7fa72fcfe8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -100,7 +100,6 @@ - $(DefineConstants);DO_DATEONLY diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index 4b2b8c5b8c..de0a60744e 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -188,12 +188,12 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddMonths: Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); return; - case SqlFunctionType.DateOnlyAddDays: - Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); - return; case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); return; + case SqlFunctionType.DateOnlyAddDays: + Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); + return; case SqlFunctionType.DateTimeConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime), node.Arguments[0] - 2001), diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index 5505da3655..dcd7a48e3f 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -161,10 +161,10 @@ public override void Visit(SqlFunctionCall node) return; case SqlFunctionType.Round: // Round should always be called with 2 arguments - if (node.Arguments.Count == 1) { + if (arguments.Count == 1) { Visit(SqlDml.FunctionCall( translator.TranslateToString(SqlFunctionType.Round), - node.Arguments[0], + arguments[0], SqlDml.Literal(0))); return; } @@ -174,31 +174,31 @@ public override void Visit(SqlFunctionCall node) // It's stupid, isn't it? Visit(SqlDml.FunctionCall( translator.TranslateToString(SqlFunctionType.Round), - node.Arguments[0], + arguments[0], SqlDml.Literal(0), SqlDml.Literal(1))); return; case SqlFunctionType.Substring: - if (node.Arguments.Count == 2) { - node = SqlDml.Substring(node.Arguments[0], node.Arguments[1]); - SqlExpression len = SqlDml.CharLength(node.Arguments[0]); - node.Arguments.Add(len); + if (arguments.Count == 2) { + node = SqlDml.Substring(node.Arguments[0], arguments[1]); + SqlExpression len = SqlDml.CharLength(arguments[0]); + arguments.Add(len); Visit(node); return; } break; case SqlFunctionType.IntervalToMilliseconds: - Visit(CastToLong(node.Arguments[0]) / NanosecondsPerMillisecond); + Visit(CastToLong(arguments[0]) / NanosecondsPerMillisecond); return; case SqlFunctionType.IntervalConstruct: case SqlFunctionType.IntervalToNanoseconds: - Visit(CastToLong(node.Arguments[0])); + Visit(CastToLong(arguments[0])); return; case SqlFunctionType.DateTimeAddMonths: - Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); + Visit(DateAddMonth(arguments[0], arguments[1])); return; case SqlFunctionType.DateTimeAddYears: - Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); + Visit(DateAddYear(arguments[0], arguments[1])); return; case SqlFunctionType.DateOnlyAddDays: Visit(DateAddDay(arguments[0], arguments[1])); @@ -210,16 +210,16 @@ public override void Visit(SqlFunctionCall node) Visit(DateAddMinute(arguments[0], arguments[1])); return; case SqlFunctionType.DateTimeTruncate: - DateTimeTruncate(node.Arguments[0]).AcceptVisitor(this); + DateTimeTruncate(arguments[0]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), - node.Arguments[0] - 2001), - node.Arguments[1] - 1), - node.Arguments[2] - 1)); + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1)); return; case SqlFunctionType.DateTimeToStringIso: - Visit(DateTimeToStringIso(node.Arguments[0])); + Visit(DateTimeToStringIso(arguments[0])); return; } From cf29f28ae01fb1842e9fb02f8b7b8b108e0a8d02 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 19 Jul 2022 18:52:22 -0700 Subject: [PATCH 03/86] Update Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs Co-authored-by: Oleg Shuruev --- .../Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs index 29bf200f16..8e37d50e42 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs @@ -255,8 +255,8 @@ public override DataTypeCollection GetDataTypesInfo() "datetime", "smalldatetime"); #if DO_DATEONLY - types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index,new ValueRange(new DateOnly(1, 1, 1), new DateOnly(9999, 12, 31)), "date"); - types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index,new ValueRange(TimeOnly.MinValue, TimeOnly.MaxValue), "time"); + types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, new ValueRange(new DateOnly(1, 1, 1), new DateOnly(9999, 12, 31)), "date"); + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, new ValueRange(TimeOnly.MinValue, TimeOnly.MaxValue), "time"); #endif types.Char = DataTypeInfo.Stream(SqlType.Char, common | index, 4000, "nchar", "char"); From c9e0948928b14ce69f874c77662bb728ea2f6e1b Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 19 Jul 2022 18:56:53 -0700 Subject: [PATCH 04/86] Code style --- .../Storage/SetFieldTest.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs index 0a33ea6034..885fddf024 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs @@ -62,24 +62,24 @@ public void CombinedTest() }; var book = new Book(); - AssertIsCalled (() => { book.Title = "A"; }); + AssertIsCalled(() => { book.Title = "A"; }); AssertIsNotCalled(() => { book.Title = "A"; }); - AssertIsCalled (() => { book.Date = new DateTime(1,2,3); }); - AssertIsNotCalled(() => { book.Date = new DateTime(1,2,3); }); + AssertIsCalled(() => { book.Date = new DateTime(1, 2, 3); }); + AssertIsNotCalled(() => { book.Date = new DateTime(1, 2, 3); }); #if DO_DATEONLY - AssertIsCalled (() => { book.DateOnly = new DateOnly(1,2,3); }); - AssertIsNotCalled(() => { book.DateOnly = new DateOnly(1,2,3); }); + AssertIsCalled(() => { book.DateOnly = new DateOnly(1, 2, 3); }); + AssertIsNotCalled(() => { book.DateOnly = new DateOnly(1, 2, 3); }); #endif - - var image = new byte[] {1, 2, 3}; - AssertIsCalled (() => { book.Image = image; }); - AssertIsCalled (() => { book.Image = image; }); + + var image = new byte[] { 1, 2, 3 }; + AssertIsCalled(() => { book.Image = image; }); + AssertIsCalled(() => { book.Image = image; }); AssertIsNotCalled(() => { book.Pair = null; }); - AssertIsCalled (() => { book.Pair = book; }); + AssertIsCalled(() => { book.Pair = book; }); AssertIsNotCalled(() => { book.Pair = book; }); - AssertIsCalled (() => { book.Pair = null; }); + AssertIsCalled(() => { book.Pair = null; }); AssertIsNotCalled(() => { book.Pair = null; }); } } @@ -99,7 +99,7 @@ private void AssertIsCalled(Action action) { int oldCallCount = fieldSetCallCount; action.Invoke(); - if (fieldSetCallCount==oldCallCount) + if (fieldSetCallCount == oldCallCount) Assert.Fail("Expected event didn't occur."); } @@ -107,7 +107,7 @@ private void AssertIsNotCalled(Action action) { int oldCallCount = fieldSetCallCount; action.Invoke(); - if (fieldSetCallCount!=oldCallCount) + if (fieldSetCallCount != oldCallCount) Assert.Fail("Event occurred, although it shouldn't."); } } From 97a2dd3ab72981fec9eded7343206eb9ea3b37bb Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 19 Jul 2022 18:58:49 -0700 Subject: [PATCH 05/86] use is null --- .../Orm/Linq/MemberCompilation/MemberCompilerProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs index aa1a586aa6..d3bff30fb7 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs @@ -306,7 +306,7 @@ private static CompilerKey GetCompilerKey(MemberInfo member) if (canonicalMember is PropertyInfo sourceProperty) { canonicalMember = sourceProperty.GetGetMethod(); // GetGetMethod returns null in case of non public getter. - if (canonicalMember==null) { + if (canonicalMember is null) { return default; } } From 2e4e002dc8b407e8c79c5aa2c4e0d0fcd6cbbe07 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 19 Jul 2022 21:43:48 -0700 Subject: [PATCH 06/86] node.Arguments --- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index dcd7a48e3f..ce6d97f9fd 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -153,7 +153,7 @@ public override void Visit(SqlFunctionCall node) var arguments = node.Arguments; switch (node.FunctionType) { case SqlFunctionType.CharLength: - (SqlDml.FunctionCall("DATALENGTH", node.Arguments) / 2).AcceptVisitor(this); + (SqlDml.FunctionCall("DATALENGTH", arguments) / 2).AcceptVisitor(this); return; case SqlFunctionType.PadLeft: case SqlFunctionType.PadRight: From 4b8160b247cbb30edcba683a090bdc819c8a6a19 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 19 Jul 2022 21:52:56 -0700 Subject: [PATCH 07/86] Update Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs Co-authored-by: Oleg Shuruev --- .../Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs index e4653b4f5c..e93baaa3a3 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs @@ -42,7 +42,7 @@ public override DataTypeCollection GetDataTypesInfo() #else types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)), - "datetime2", "datetime", "date", "time", "smalldatetime"); + "datetime2", "datetime", "smalldatetime", "date", "time"); #endif types.VarBinaryMax = DataTypeInfo.Regular(SqlType.VarBinaryMax, common, "varbinary(max)", "image"); From 413bab29c554fef383bdb611fd7ea07661f75ed4 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 30 Nov 2022 18:21:21 +0500 Subject: [PATCH 08/86] Move TimeOnly and DateOnly compilers to separate files --- .../MemberCompilers/DateOnlyCompilers.cs | 153 ++++++++++++++ .../MemberCompilers/DateTimeCompilers.cs | 178 ---------------- .../MemberCompilers/TimeOnlyCompilers.cs | 198 ++++++++++++++++++ 3 files changed, 351 insertions(+), 178 deletions(-) create mode 100644 Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs create mode 100644 Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs new file mode 100644 index 0000000000..03141cd082 --- /dev/null +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -0,0 +1,153 @@ +// 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. + +using System; +using System.Linq.Expressions; +using Xtensive.Sql; +using Xtensive.Sql.Dml; +using Operator = Xtensive.Reflection.WellKnown.Operator; + +namespace Xtensive.Orm.Providers.Expressions.MemberCompilers +{ +#if DO_DATEONLY + + [CompilerContainer(typeof(SqlExpression))] + internal static class DateTimeCompilers + { + #region Extractors + + [Compiler(typeof(DateOnly), "Year", TargetKind.PropertyGet)] + public static SqlExpression DateOnlyYear(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Year, _this)); + + [Compiler(typeof(DateOnly), "Month", TargetKind.PropertyGet)] + public static SqlExpression DateOnlyMonth(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Month, _this)); + + [Compiler(typeof(DateOnly), "Day", TargetKind.PropertyGet)] + public static SqlExpression DateOnlyDay(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Day, _this)); + + + [Compiler(typeof(DateOnly), "DayOfYear", TargetKind.PropertyGet)] + public static SqlExpression DateTimeDayOfYear(SqlExpression _this) + { + return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfYear, _this)); + } + + [Compiler(typeof(DateOnly), "DayOfWeek", TargetKind.PropertyGet)] + public static SqlExpression DateTimeDayOfWeek(SqlExpression _this) + { + throw new NotImplementedException(); + //var baseExpression = ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfWeek, _this)); + //var context = ExpressionTranslationContext.Current; + //if (context == null) { + // return baseExpression; + //} + //if (context.ProviderInfo.ProviderName == WellKnown.Provider.MySql) { + // return baseExpression - 1; //Mysql starts days of week from 1 unlike in .Net. + //} + //return baseExpression; + } + + #endregion + + #region Constructors + + [Compiler(typeof(DateOnly), null, TargetKind.Constructor)] + public static SqlExpression DateOnlyCtor( + [Type(typeof(int))] SqlExpression year, + [Type(typeof(int))] SqlExpression month, + [Type(typeof(int))] SqlExpression day) => + SqlDml.DateOnlyConstruct(year, month, day); + + #endregion + + #region Operators + + [Compiler(typeof(DateOnly), Operator.Equality, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorEquality( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 == d2; + } + + [Compiler(typeof(DateOnly), Operator.Inequality, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorInequality( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 != d2; + } + + [Compiler(typeof(DateOnly), Operator.GreaterThan, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorGreaterThan( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 > d2; + } + + [Compiler(typeof(DateOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorGreaterThanOrEqual( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 >= d2; + } + + [Compiler(typeof(DateOnly), Operator.LessThan, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorLessThan( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 < d2; + } + + [Compiler(typeof(DateOnly), Operator.LessThanOrEqual, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorLessThanOrEqual( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 <= d2; + } + + #endregion + + [Compiler(typeof(DateOnly), "AddYears")] + public static SqlExpression DateOnlyAddYears(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateTimeAddYears(_this, value); + + [Compiler(typeof(DateOnly), "AddMonths")] + public static SqlExpression DateOnlyAddMonths(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateTimeAddMonths(_this, value); + + [Compiler(typeof(DateOnly), "AddDays")] + public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => + SqlDml.DateOnlyAddDays(_this, value); + + [Compiler(typeof(DateOnly), "ToString")] + public static SqlExpression DateTimeToStringIso(SqlExpression _this) + { + throw new NotSupportedException(Strings.ExDateTimeToStringMethodIsNotSupported); + } + + [Compiler(typeof(DateOnly), "ToString")] + public static SqlExpression DateTimeToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) + { + throw new NotImplementedException(); + //var stringValue = value as SqlLiteral; + + //if (stringValue == null) + // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + + //if (!stringValue.Value.Equals("s")) + // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + + //return SqlDml.DateTimeToStringIso(_this); + } + } +#endif +} diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs index 2028f1c16a..0f1c32659c 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs @@ -73,36 +73,6 @@ public static SqlExpression DateTimeDayOfYear(SqlExpression _this) return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfYear, _this)); } -#if DO_DATEONLY - [Compiler(typeof(DateOnly), "Year", TargetKind.PropertyGet)] - public static SqlExpression DateOnlyYear(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Year, _this)); - - [Compiler(typeof(DateOnly), "Month", TargetKind.PropertyGet)] - public static SqlExpression DateOnlyMonth(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Month, _this)); - - [Compiler(typeof(DateOnly), "Day", TargetKind.PropertyGet)] - public static SqlExpression DateOnlyDay(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Day, _this)); - - [Compiler(typeof(TimeOnly), "Hour", TargetKind.PropertyGet)] - public static SqlExpression TimeOnlyHour(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Hour, _this)); - - [Compiler(typeof(TimeOnly), "Minute", TargetKind.PropertyGet)] - public static SqlExpression TimeOnlyMinute(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Minute, _this)); - - [Compiler(typeof(TimeOnly), "Second", TargetKind.PropertyGet)] - public static SqlExpression TimeOnlySecond(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Second, _this)); - - [Compiler(typeof(TimeOnly), "Millisecond", TargetKind.PropertyGet)] - public static SqlExpression TimeOnlyMillisecond(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); -#endif // DO_DATEONLY - #endregion #region Constructors @@ -155,32 +125,6 @@ public static SqlExpression DateTimeCtor( return DateTimeConstruct(year, month, day, hour, minute, second, millisecond); } -#if DO_DATEONLY - [Compiler(typeof(DateOnly), null, TargetKind.Constructor)] - public static SqlExpression DateOnlyCtor( - [Type(typeof(int))] SqlExpression year, - [Type(typeof(int))] SqlExpression month, - [Type(typeof(int))] SqlExpression day) => - SqlDml.DateOnlyConstruct(year, month, day); - - [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] - public static SqlExpression TimeOnlyCtor( - [Type(typeof(int))] SqlExpression hour, - [Type(typeof(int))] SqlExpression minute, - [Type(typeof(int))] SqlExpression second) => - SqlDml.TimeOnlyConstruct(hour, minute, second, 0); - - [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] - public static SqlExpression TimeOnlyCtor( - [Type(typeof(int))] SqlExpression hour, - [Type(typeof(int))] SqlExpression minute) => - SqlDml.TimeOnlyConstruct(hour, minute, 0, 0); - - [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] - public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression ticks) => - new SqlFunctionCall(SqlFunctionType.TimeOnlyConstruct, ticks); -#endif // DO_DATEONLY - #endregion #region Operators @@ -258,105 +202,6 @@ public static SqlExpression DateTimeOperatorSubtractionDateTime( return SqlDml.DateTimeMinusDateTime(d1, d2); } -#if DO_DATEONLY - [Compiler(typeof(DateOnly), Operator.Equality, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorEquality( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 == d2; - } - - [Compiler(typeof(DateOnly), Operator.Inequality, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorInequality( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 != d2; - } - - [Compiler(typeof(DateOnly), Operator.GreaterThan, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorGreaterThan( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 > d2; - } - - [Compiler(typeof(DateOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorGreaterThanOrEqual( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 >= d2; - } - - [Compiler(typeof(DateOnly), Operator.LessThan, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorLessThan( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 < d2; - } - - [Compiler(typeof(DateOnly), Operator.LessThanOrEqual, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorLessThanOrEqual( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 <= d2; - } - - [Compiler(typeof(TimeOnly), Operator.Equality, TargetKind.Operator)] - public static SqlExpression TimeOnlyOperatorEquality( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) - { - return d1 == d2; - } - - [Compiler(typeof(TimeOnly), Operator.Inequality, TargetKind.Operator)] - public static SqlExpression TimeOnlyOperatorInequality( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) - { - return d1 != d2; - } - - [Compiler(typeof(TimeOnly), Operator.GreaterThan, TargetKind.Operator)] - public static SqlExpression TimeOnlyyOperatorGreaterThan( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) - { - return d1 > d2; - } - - [Compiler(typeof(TimeOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] - public static SqlExpression TimeOnlyOperatorGreaterThanOrEqual( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) - { - return d1 >= d2; - } - - [Compiler(typeof(TimeOnly), Operator.LessThan, TargetKind.Operator)] - public static SqlExpression TimeOnlyOperatorLessThan( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) - { - return d1 < d2; - } - - [Compiler(typeof(TimeOnly), Operator.LessThanOrEqual, TargetKind.Operator)] - public static SqlExpression TimeOnlyOperatorLessThanOrEqual( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) - { - return d1 <= d2; - } - -#endif // DO_DATEONLY - #endregion [Compiler(typeof(DateTime), "Add")] @@ -447,28 +292,5 @@ public static SqlExpression DateTimeToStringIso(SqlExpression _this, [Type(typeo return SqlDml.DateTimeToStringIso(_this); } - -#if DO_DATEONLY - [Compiler(typeof(DateOnly), "AddYears")] - public static SqlExpression DateOnlyAddYears(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => - SqlDml.DateTimeAddYears(_this, value); - - [Compiler(typeof(DateOnly), "AddMonths")] - public static SqlExpression DateOnlyAddMonths(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => - SqlDml.DateTimeAddMonths(_this, value); - - [Compiler(typeof(DateOnly), "AddDays")] - public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => - SqlDml.DateOnlyAddDays(_this, value); - - [Compiler(typeof(TimeOnly), "AddHours")] - public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => - SqlDml.TimeOnlyAddHours(_this, value); - - [Compiler(typeof(TimeOnly), "AddMinutes")] - public static SqlExpression TimeOnlyAddMinutes(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => - SqlDml.TimeOnlyAddMinutes(_this, value); - -#endif // DO_DATEONLY } } diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs new file mode 100644 index 0000000000..e8b5399eac --- /dev/null +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -0,0 +1,198 @@ +// 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. + +using System; +using Xtensive.Sql; +using Xtensive.Sql.Dml; +using Operator = Xtensive.Reflection.WellKnown.Operator; + +namespace Xtensive.Orm.Providers.Expressions.MemberCompilers +{ +#if DO_DATEONLY + + [CompilerContainer(typeof(SqlExpression))] + internal static class TimeOnlyCompilers + { + #region Extractors + + [Compiler(typeof(TimeOnly), "Hour", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyHour(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Hour, _this)); + + [Compiler(typeof(TimeOnly), "Minute", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyMinute(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Minute, _this)); + + [Compiler(typeof(TimeOnly), "Second", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlySecond(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Second, _this)); + + [Compiler(typeof(TimeOnly), "Millisecond", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyMillisecond(SqlExpression _this) => + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); + + #endregion + + #region Constructors + + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor( + [Type(typeof(int))] SqlExpression hour, + [Type(typeof(int))] SqlExpression minute, + [Type(typeof(int))] SqlExpression second) => + SqlDml.TimeOnlyConstruct(hour, minute, second, 0); + + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor( + [Type(typeof(int))] SqlExpression hour, + [Type(typeof(int))] SqlExpression minute) => + SqlDml.TimeOnlyConstruct(hour, minute, 0, 0); + + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression ticks) => + new SqlFunctionCall(SqlFunctionType.TimeOnlyConstruct, ticks); + + #endregion + + #region Operators + + [Compiler(typeof(DateOnly), Operator.Equality, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorEquality( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 == d2; + } + + [Compiler(typeof(DateOnly), Operator.Inequality, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorInequality( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 != d2; + } + + [Compiler(typeof(DateOnly), Operator.GreaterThan, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorGreaterThan( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 > d2; + } + + [Compiler(typeof(DateOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorGreaterThanOrEqual( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 >= d2; + } + + [Compiler(typeof(DateOnly), Operator.LessThan, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorLessThan( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 < d2; + } + + [Compiler(typeof(DateOnly), Operator.LessThanOrEqual, TargetKind.Operator)] + public static SqlExpression DateOnlyOperatorLessThanOrEqual( + [Type(typeof(DateOnly))] SqlExpression d1, + [Type(typeof(DateOnly))] SqlExpression d2) + { + return d1 <= d2; + } + + [Compiler(typeof(TimeOnly), Operator.Equality, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorEquality( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) + { + return d1 == d2; + } + + [Compiler(typeof(TimeOnly), Operator.Inequality, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorInequality( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) + { + return d1 != d2; + } + + [Compiler(typeof(TimeOnly), Operator.GreaterThan, TargetKind.Operator)] + public static SqlExpression TimeOnlyyOperatorGreaterThan( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) + { + return d1 > d2; + } + + [Compiler(typeof(TimeOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorGreaterThanOrEqual( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) + { + return d1 >= d2; + } + + [Compiler(typeof(TimeOnly), Operator.LessThan, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorLessThan( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) + { + return d1 < d2; + } + + [Compiler(typeof(TimeOnly), Operator.LessThanOrEqual, TargetKind.Operator)] + public static SqlExpression TimeOnlyOperatorLessThanOrEqual( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) + { + return d1 <= d2; + } + + [Compiler(typeof(TimeOnly), Operator.Subtraction, TargetKind.Operator)] + public static SqlExpression DateTimeOperatorSubtractionDateTime( + [Type(typeof(DateTime))] SqlExpression d1, + [Type(typeof(DateTime))] SqlExpression d2) + { + throw new NotImplementedException(); + //return SqlDml.DateTimeMinusDateTime(d1, d2); + } + + #endregion + + [Compiler(typeof(TimeOnly), "AddHours")] + public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.TimeOnlyAddHours(_this, value); + + [Compiler(typeof(TimeOnly), "AddMinutes")] + public static SqlExpression TimeOnlyAddMinutes(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => + SqlDml.TimeOnlyAddMinutes(_this, value); + + [Compiler(typeof(TimeOnly), "ToString")] + public static SqlExpression TimeOnlyToStringIso(SqlExpression _this) + { + throw new NotSupportedException(Strings.ExDateTimeToStringMethodIsNotSupported); + } + + [Compiler(typeof(TimeOnly), "ToString")] + public static SqlExpression TimeOnlyToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) + { + throw new NotImplementedException(); + + //var stringValue = value as SqlLiteral; + + //if (stringValue == null) + // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + + //if (!stringValue.Value.Equals("s")) + // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + + //return SqlDml.DateTimeToStringIso(_this); + } + } +#endif // DO_DATEONLY +} From 7d41b4945402822149e5312fbc9dc74d85fa853b Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 30 Nov 2022 20:01:32 +0500 Subject: [PATCH 09/86] Replace DO_DATEONLY with NET6_0_OR_GREATER temporary keep previous compilation variable as comment --- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 2 + .../Sql.Drivers.Oracle/v09/Translator.cs | 2 + .../v8_0/ServerInfoProvider.cs | 2 +- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 2 + .../v09/ServerInfoProvider.cs | 2 +- .../Sql.Drivers.SqlServer/v09/Translator.cs | 4 +- .../v10/ServerInfoProvider.cs | 2 +- .../Sql.Drivers.SqlServer/v10/Translator.cs | 2 + .../DateTime/ComparisonTest.cs | 6 +- .../DateTime/DistinctTest.cs | 2 +- .../DateTime/JoinTest.cs | 2 +- .../DateTime/OperationsTest.cs | 10 ++-- .../DateTime/OrderByTest.cs | 2 +- .../DateTime/PartsExtractionTest.cs | 4 +- .../DateTime/WhereTest.cs | 2 +- .../DateTimeBaseTest.cs | 4 +- .../Linq/DateTimeAndDateTimeOffset/Model.cs | 6 +- .../SchemaSharing/EntityManipulation/Model.cs | 2 +- .../SimpleEntityManipulationTest.cs | 4 +- .../Storage/SetFieldTest.cs | 4 +- Orm/Xtensive.Orm/Linq/ExpressionWriter.cs | 2 +- .../Orm/Linq/ExpressionExtensions.cs | 2 +- .../Orm/Linq/ItemToTupleConverter{TItem}.cs | 5 +- .../ExpressionTranslationHelpers.cs | 2 +- .../MemberCompilers/DateOnlyCompilers.cs | 2 +- .../MemberCompilers/TimeOnlyCompilers.cs | 4 +- .../Orm/Providers/SqlCompiler.Helpers.cs | 49 ++++++++-------- Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs | 2 +- .../Sql/Compiler/SqlTranslator.cs | 13 ++++- Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 4 +- .../Sql/Info/DataTypeCollection.cs | 12 +++- Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs | 9 ++- .../Sql/Internals/SqlValidator.cs | 6 +- Orm/Xtensive.Orm/Sql/SqlDml.cs | 4 +- Orm/Xtensive.Orm/Sql/SqlDriver.cs | 4 +- .../Sql/ValueTypeMapping/TypeMapper.cs | 56 ++++++++++--------- .../Tuples/Packed/PackedFieldAccessor.cs | 2 +- Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs | 44 +++++++-------- 38 files changed, 165 insertions(+), 123 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index cd9fb159f6..9d88b98cd7 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -190,9 +190,11 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); return; +#if NET6_0_OR_GREATER //DO_DATEONLY case SqlFunctionType.DateOnlyAddDays: Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); return; +#endif case SqlFunctionType.DateTimeConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime), node.Arguments[0] - 2001), diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index 44832f2922..57a0d54708 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -30,8 +30,10 @@ internal class Translator : SqlTranslator /// public override string DateTimeFormatString => @"'(TIMESTAMP '\'yyyy\-MM\-dd HH\:mm\:ss\.fff\'\)"; +#if NET6_0_OR_GREATER //DO_DATEONLY /// public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)"; +#endif /// public override string TimeSpanFormatString => "(INTERVAL '{0}{1} {2}:{3}:{4}.{5:000}' DAY(6) TO SECOND(3))"; diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs index fa55be43ee..7a28bb5bcb 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs @@ -178,7 +178,7 @@ public override DataTypeCollection GetDataTypesInfo() dtc.Interval = DataTypeInfo.Range(SqlType.Interval, commonFeatures, ValueRange.TimeSpan, "interval"); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY dtc.DateOnly = DataTypeInfo.Range(SqlType.Date, commonFeatures, ValueRange.DateOnly, "date"); dtc.TimeOnly = DataTypeInfo.Range(SqlType.Time, commonFeatures, ValueRange.TimeOnly, "time"); #endif diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index be942201c4..0ccf8361f4 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -199,6 +199,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(arguments[0], arguments[1])); return; +#if NET6_0_OR_GREATER //DO_DATEONLY case SqlFunctionType.DateOnlyAddDays: Visit(DateAddDay(arguments[0], arguments[1])); return; @@ -208,6 +209,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.TimeOnlyAddMinutes: Visit(DateAddMinute(arguments[0], arguments[1])); return; +#endif case SqlFunctionType.DateTimeTruncate: DateTimeTruncate(arguments[0]).AcceptVisitor(this); return; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs index 3910cf8f7e..ca3f1e5cc2 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs @@ -255,7 +255,7 @@ public override DataTypeCollection GetDataTypesInfo() new ValueRange(new DateTime(1753, 1, 1), new DateTime(9999, 12, 31)), "datetime", "smalldatetime"); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, new ValueRange(new DateOnly(1, 1, 1), new DateOnly(9999, 12, 31)), "date"); types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, new ValueRange(TimeOnly.MinValue, TimeOnly.MaxValue), "time"); #endif diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs index 14245d7f24..620b68881b 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs @@ -20,8 +20,10 @@ namespace Xtensive.Sql.Drivers.SqlServer.v09 internal class Translator : SqlTranslator { public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fff\'' as datetime)'"; +#if NET6_0_OR_GREATER //DO_DATEONLY public override string DateOnlyFormatString => @"'cast ('\'yyyy\-MM\-dd\'' as date)'"; public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fff\'' as time)'"; +#endif public override string TimeSpanFormatString => string.Empty; public override void Initialize() @@ -569,7 +571,7 @@ public override void Translate(SqlCompilerContext context, object literalValue) case long v: _ = output.Append($"CAST({v} as BIGINT)"); break; -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY case DateOnly dateOnly: var dateOnlyRange = (ValueRange) Driver.ServerInfo.DataTypes.DateTime.ValueRange; var newDateOnly = ValueRangeValidator.Correct(dateOnly.ToDateTime(TimeOnly.MinValue), dateOnlyRange).Date; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs index e93baaa3a3..560a77e3ed 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs @@ -35,7 +35,7 @@ public override DataTypeCollection GetDataTypesInfo() new DateTimeOffset(9999, 12, 31, 0, 0, 0, 0, new TimeSpan(0))), "datetimeoffset"); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)), "datetime2", "datetime", "smalldatetime"); diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs index 43bd6ee109..14b0eb1507 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs @@ -17,8 +17,10 @@ internal class Translator : v09.Translator /// public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fffffff\'' as datetime2)'"; +#if NET6_0_OR_GREATER //DO_DATEONLY /// public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fffffff\'' as time)'"; +#endif public string DateTimeOffsetFormatString => @"'cast ('\'yyyy\-MM\-dd HH\:mm\:ss\.fffffff\ zzz\'' as datetimeoffset)'"; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs index 9d7406c100..0f6440071b 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs @@ -24,7 +24,7 @@ public void EqualsTest() RunWrongTest(c => c.MillisecondDateTime==WrongMillisecondDateTime); RunWrongTest(c => c.NullableDateTime==WrongDateTime); RunWrongTest(c => c.NullableDateTime==null); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly == FirstDateOnly); RunTest(c => c.NullableDateOnly == NullableDateOnly); #endif @@ -38,7 +38,7 @@ public void NotEqualTest() RunTest(c=>c.DateTime!=FirstDateTime.AddYears(1)); RunTest(c => c.MillisecondDateTime!=FirstMillisecondDateTime.AddYears(1)); RunTest(c=>c.NullableDateTime!=NullableDateTime.AddYears(1)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly != FirstDateOnly.AddYears(1)); #endif }); @@ -60,7 +60,7 @@ public void CompareTest() RunWrongTest(c => c.DateTime > FirstDateTime); RunWrongTest(c => c.MillisecondDateTime > FirstMillisecondDateTime); RunWrongTest(c => c.MillisecondDateTime < FirstMillisecondDateTime.Date); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly > FirstDateOnly.AddDays(-1)); #endif }); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs index 9e55216189..40ed6eeb60 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs @@ -18,7 +18,7 @@ public class DistinctTest : DateTimeBaseTest public void DistinctByDateTimeTest() { ExecuteInsideSession(() => DistinctPrivate(c => c.DateTime)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY ExecuteInsideSession(() => DistinctPrivate(c => c.DateOnly)); ExecuteInsideSession(() => DistinctPrivate(c => c.TimeOnly)); #endif diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs index 6f1236a72e..8fecd9427d 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs @@ -17,7 +17,7 @@ public void DateTimeJoinTest() (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateTime, RightDateTime = right.DateTime }, c => c.LeftId, c => c.RightId)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY ExecuteInsideSession(() => JoinPrivate, DateOnly, long>( left => left.DateOnly, right => right.DateOnly, diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs index b1c7f2db41..230300116f 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs @@ -22,7 +22,7 @@ public void AddYearsTest() RunWrongTest(c => c.DateTime.AddYears(1) == FirstDateTime.AddYears(2)); RunWrongTest(c => c.MillisecondDateTime.AddYears(-1) == FirstMillisecondDateTime.AddYears(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddYears(33) == NullableDateTime.AddYears(44)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(1)); RunTest(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(33)); @@ -43,7 +43,7 @@ public void AddMonthsTest() RunWrongTest(c => c.DateTime.AddMonths(1) == FirstDateTime.AddMonths(2)); RunWrongTest(c => c.MillisecondDateTime.AddMonths(-1) == FirstMillisecondDateTime.AddMonths(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddMonths(33) == NullableDateTime.AddMonths(44)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(1)); RunTest(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(33)); @@ -64,7 +64,7 @@ public void AddDaysTest() RunWrongTest(c => c.DateTime.AddDays(1) == FirstDateTime.AddDays(2)); RunWrongTest(c => c.MillisecondDateTime.AddDays(-1) == FirstMillisecondDateTime.AddDays(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddDays(33) == NullableDateTime.AddDays(44)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(1)); RunTest(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(33)); @@ -99,7 +99,7 @@ public void AddMinutesTest() RunWrongTest(c => c.DateTime.AddMinutes(1) == FirstDateTime.AddMinutes(2)); RunWrongTest(c => c.MillisecondDateTime.AddMinutes(-1) == FirstMillisecondDateTime.AddMinutes(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddMinutes(33) == NullableDateTime.AddMinutes(44)); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(1)); RunWrongTest(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(2)); @@ -215,7 +215,7 @@ public void MinusDateTimeTest() RunWrongTest(c => c.MillisecondDateTime - SecondDateTime == FirstMillisecondDateTime - WrongDateTime); RunWrongTest(c => c.NullableDateTime - SecondDateTime == NullableDateTime - WrongDateTime); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - SecondTimeOnly); RunWrongTest(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - WrongTimeOnly); #endif diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs index d7fd92c34e..5c317c0795 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs @@ -20,7 +20,7 @@ public void DateTimeOrderByTest() ExecuteInsideSession(() => { OrderByPrivate(c => c.DateTime, c => c.Id); OrderByPrivate(c => c.DateTime, c => c); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY OrderByPrivate(c => c.DateOnly, c => c.Id); OrderByPrivate(c => c.DateOnly, c => c); OrderByPrivate(c => c.TimeOnly, c => c.Id ); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs index 6f990d55e1..f66db476e7 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs @@ -37,7 +37,7 @@ public void ExtractMonthTest() RunWrongTest(c => c.DateTime.Month == WrongDateTime.Month); RunWrongTest(c => c.MillisecondDateTime.Month == WrongMillisecondDateTime.Month); RunWrongTest(c => c.NullableDateTime.Value.Month == WrongDateTime.Month); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.DateOnly.Month == FirstDateOnly.Month); RunTest(c => c.NullableDateOnly.Value.Month == NullableDateOnly.Month); @@ -100,7 +100,7 @@ public void ExtractSecondTest() RunWrongTest(c => c.DateTime.Second == WrongDateTime.Second); RunWrongTest(c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Second); RunWrongTest(c => c.NullableDateTime.Value.Second == WrongDateTime.Second); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY RunTest(c => c.TimeOnly.Second == FirstTimeOnly.Second); RunWrongTest(c => c.TimeOnly.Second == WrongTimeOnly.Second); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs index ac616be831..949f7a5a05 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs @@ -22,7 +22,7 @@ public void DateTimeWhereTest() WherePrivate(c => c.DateTime.Hour == FirstDateTime.Hour, c => c.Id); WherePrivate(c => c.DateTime.Second == FirstDateTime.Second, c => c.Id); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY WherePrivate(c => c.DateOnly == FirstDateOnly, c => c.Id); WherePrivate(c => c.TimeOnly == FirstTimeOnly, c => c.Id); WherePrivate(c => c.TimeOnly.Hour == FirstTimeOnly.Hour, c => c.Id); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs index 4339392a3d..8909b2b2f0 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs @@ -25,7 +25,7 @@ public abstract class DateTimeBaseTest : BaseTest protected static readonly DateTime SecondMillisecondDateTime = SecondDateTime.AddMilliseconds(987); protected static readonly DateTime WrongMillisecondDateTime = WrongDateTime.AddMilliseconds(654); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY protected static readonly DateOnly FirstDateOnly = DateOnly.FromDateTime(FirstDateTime); protected static readonly DateOnly NullableDateOnly = DateOnly.FromDateTime(SecondDateTime); protected static readonly DateOnly WrongDateOnly = DateOnly.FromDateTime(WrongDateTime); @@ -48,7 +48,7 @@ protected override void PopulateEntities(Session session) new SingleDateTimeEntity { DateTime = FirstDateTime, MillisecondDateTime = FirstMillisecondDateTime, -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY DateOnly = DateOnly.FromDateTime(FirstDateTime), NullableDateOnly = DateOnly.FromDateTime(NullableDateTime), TimeOnly = FirstTimeOnly, diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs index 97ae038ac3..692f31e742 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs @@ -23,7 +23,7 @@ public class SingleDateTimeEntity : Entity [Field] public DateTime? NullableDateTime { get; set; } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY [Field] public DateOnly DateOnly { get; set; } @@ -60,7 +60,7 @@ public class DateTimeEntity : Entity [Field] public DateTime DateTime { get; set; } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY [Field] public DateOnly DateOnly { get; set; } @@ -71,7 +71,7 @@ public class DateTimeEntity : Entity public DateTimeEntity(DateTime dateTime) { DateTime = dateTime; -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY DateOnly = DateOnly.FromDateTime(dateTime); TimeOnly = TimeOnly.FromDateTime(dateTime); #endif diff --git a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs index 90f0867bb3..83513a07b9 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs @@ -19,7 +19,7 @@ public class TestEntity1 : Entity [Field] public string Text { get; set; } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY [Field] public DateOnly DateOnly { get; set; } diff --git a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs index 80fa8d59ab..ab320fd583 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs @@ -408,7 +408,7 @@ private void Update(Session session, Key[] createdKeys, int initialCountOfEntiti var now = DateTime.UtcNow; a.Text = updatedText; -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY var dateOnly = DateOnly.FromDateTime(now); var timeOnly = TimeOnly.FromDateTime(now); a.DateOnly = dateOnly; @@ -418,7 +418,7 @@ private void Update(Session session, Key[] createdKeys, int initialCountOfEntiti Assert.That(session.Query.All().Count(), Is.EqualTo(initialCountOfEntities + 1)); Assert.That(session.Query.All().FirstOrDefault(e => e.Text == updatedText), Is.Not.Null); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY Assert.AreEqual(session.Query.All().FirstOrDefault(e => e.Text == updatedText).DateOnly, dateOnly); Assert.AreEqual(session.Query.All().FirstOrDefault(e => e.Text == updatedText).TimeOnly, timeOnly); #endif diff --git a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs index 885fddf024..82ea6a7cac 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs @@ -25,7 +25,7 @@ public class Book : Entity [Field] public DateTime Date { get; set; } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY [Field] public DateOnly DateOnly { get; set; } #endif @@ -67,7 +67,7 @@ public void CombinedTest() AssertIsCalled(() => { book.Date = new DateTime(1, 2, 3); }); AssertIsNotCalled(() => { book.Date = new DateTime(1, 2, 3); }); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY AssertIsCalled(() => { book.DateOnly = new DateOnly(1, 2, 3); }); AssertIsNotCalled(() => { book.DateOnly = new DateOnly(1, 2, 3); }); #endif diff --git a/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs b/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs index 42668d4314..6e58d162dd 100644 --- a/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs +++ b/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs @@ -361,7 +361,7 @@ protected override Expression VisitConstant(ConstantExpression c) Write(c.Value.ToString()); Write("\")"); } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY else if (type == WellKnownTypes.DateOnly) { Write("DateOnly.Parse(\""); Write(c.Value.ToString()); diff --git a/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs b/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs index 99ff1e1e3d..96f9d736e4 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs @@ -47,7 +47,7 @@ public static bool IsNewExpressionSupportedByStorage(this Expression expression) expression.NodeType == ExpressionType.New && expression.Type switch { var t => t == WellKnownTypes.TimeSpan || t == WellKnownTypes.DateTime || t == WellKnownTypes.DateTimeOffset -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY || t == WellKnownTypes.DateOnly || t == WellKnownTypes.TimeOnly #endif }; diff --git a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs index daec66375e..03751f5eac 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs @@ -102,8 +102,9 @@ private static bool TypeIsStorageMappable(Type type) type == WellKnownTypes.String || type == WellKnownTypes.DateTime || type == WellKnownTypes.DateTimeOffset || -#if DO_DATEONLY - type == WellKnownTypes.DateOnly || type == WellKnownTypes.TimeOnly || +#if NET6_0_OR_GREATER //DO_DATEONLY + type == WellKnownTypes.DateOnly || + type == WellKnownTypes.TimeOnly || #endif type == WellKnownTypes.Guid || type == WellKnownTypes.TimeSpan; diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs index 5cfbbbec05..710578115d 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs @@ -46,7 +46,7 @@ public static SqlExpression ToChar(SqlExpression target) => public static SqlExpression ToDateTime(SqlExpression target) => Cast(target, WellKnownTypes.DateTime); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY public static SqlExpression ToDate(SqlExpression target) => Cast(target, WellKnownTypes.DateOnly); diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index 03141cd082..2cc3c03c76 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -10,7 +10,7 @@ namespace Xtensive.Orm.Providers.Expressions.MemberCompilers { -#if DO_DATEONLY +#if NET6_0_OR_GREATER [CompilerContainer(typeof(SqlExpression))] internal static class DateTimeCompilers diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index e8b5399eac..4f2875e10b 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -9,7 +9,7 @@ namespace Xtensive.Orm.Providers.Expressions.MemberCompilers { -#if DO_DATEONLY +#if NET6_0_OR_GREATER [CompilerContainer(typeof(SqlExpression))] internal static class TimeOnlyCompilers @@ -194,5 +194,5 @@ public static SqlExpression TimeOnlyToStringIso(SqlExpression _this, [Type(typeo //return SqlDml.DateTimeToStringIso(_this); } } -#endif // DO_DATEONLY +#endif } diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs index 8de00d9e56..423fa550e8 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs @@ -307,7 +307,7 @@ private SqlExpression GetOrderByExpression(SqlExpression expression, SortProvide if (columnType == WellKnownTypes.DateTime) { return SqlDml.Cast(expression, SqlType.DateTime); } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY if (columnType == WellKnownTypes.DateOnly) { return SqlDml.Cast(expression, SqlType.Date); } @@ -331,29 +331,8 @@ private SqlExpression GetJoinExpression(SqlExpression leftExpression, SqlExpress Pair columnPair; if (providerInfo.Supports(ProviderFeatures.DateTimeEmulation)) { columnPair = provider.EqualColumns[index]; - if (columnPair.First.Type == WellKnownTypes.DateTime) { - leftExpression = SqlDml.Cast(leftExpression, SqlType.DateTime); - } - - if (columnPair.Second.Type == WellKnownTypes.DateTime) { - rightExpression = SqlDml.Cast(rightExpression, SqlType.DateTime); - } - -#if DO_DATEONLY - if (columnPair.First.Type == WellKnownTypes.DateOnly) { - leftExpression = SqlDml.Cast(leftExpression, SqlType.Date); - } - else if (columnPair.First.Type == WellKnownTypes.TimeOnly) { - leftExpression = SqlDml.Cast(leftExpression, SqlType.Time); - } - - if (columnPair.Second.Type == WellKnownTypes.DateOnly) { - rightExpression = SqlDml.Cast(rightExpression, SqlType.Date); - } - else if (columnPair.Second.Type == WellKnownTypes.TimeOnly) { - rightExpression = SqlDml.Cast(rightExpression, SqlType.Time); - } -#endif + leftExpression = CastToDateTimeVariantIfNeeded(leftExpression, columnPair.First.Type); + rightExpression = CastToDateTimeVariantIfNeeded(rightExpression, columnPair.Second.Type); } if (providerInfo.Supports(ProviderFeatures.DateTimeOffsetEmulation)) { @@ -369,6 +348,28 @@ private SqlExpression GetJoinExpression(SqlExpression leftExpression, SqlExpress } return leftExpression == rightExpression; + + static SqlExpression CastToDateTimeVariantIfNeeded(SqlExpression expression,Type type) + { + SqlType? sqlType = null; + if (type == WellKnownTypes.DateTime) { + sqlType = SqlType.DateTime; + } +#if NET6_0_OR_GREATER //DO_DATEONLY + else if (type == WellKnownTypes.DateOnly) { + sqlType = SqlType.Date; + } + else if(type == WellKnownTypes.TimeOnly) { + sqlType = SqlType.Time; + } +#endif + if (sqlType == null) { + return expression; + } + else { + return SqlDml.Cast(expression, sqlType.Value); + } + } } public SqlExpression GetOuterExpression(ApplyParameter parameter, int columnIndex) diff --git a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs index d35ec9918a..ee4471298d 100644 --- a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs +++ b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs @@ -57,7 +57,7 @@ internal static class WellKnownTypes public static readonly Type NullableDateTime = typeof(DateTime?); public static readonly Type DateTimeOffset = typeof(DateTimeOffset); public static readonly Type NullableDateTimeOffset = typeof(DateTimeOffset?); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY public static readonly Type DateOnly = typeof(DateOnly); public static readonly Type TimeOnly = typeof(TimeOnly); public static readonly Type NullableDateOnly = typeof(DateOnly?); diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs index 71c9ee00f2..3a4cdc75ef 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs @@ -72,7 +72,13 @@ public abstract class SqlTranslator : SqlDriverBound /// public abstract string DateTimeFormatString { get; } +#if NET6_0_OR_GREATER + /// + /// Gets the format string. + /// See for details + /// public virtual string DateOnlyFormatString => throw new NotImplementedException(); +#endif /// /// Gets the time span format string. @@ -80,7 +86,12 @@ public abstract class SqlTranslator : SqlDriverBound /// public abstract string TimeSpanFormatString { get; } +#if NET6_0_OR_GREATER + /// + /// Gets the format string. + /// public virtual string TimeOnlyFormatString => throw new NotImplementedException(); +#endif /// /// Gets the parameter prefix. @@ -1522,7 +1533,7 @@ public virtual void Translate(SqlCompilerContext context, object literalValue) case Guid: case byte[]: throw new NotSupportedException(string.Format(Strings.ExTranslationOfLiteralOfTypeXIsNotSupported, literalType.GetShortName())); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY case DateOnly dateOnly: output.Append(dateOnly.ToString(DateOnlyFormatString, DateTimeFormat)); break; diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index 01c9084846..52853dde2a 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -64,9 +64,11 @@ public enum SqlFunctionType DateTimeConstruct, DateTimeAddYears, DateTimeAddMonths, +#if NET6_0_OR_GREATER //DO_DATEONLY DateOnlyAddDays, TimeOnlyAddHours, TimeOnlyAddMinutes, +#endif DateTimeTruncate, DateTimeToStringIso, IntervalConstruct, @@ -84,7 +86,7 @@ public enum SqlFunctionType DateTimeOffsetToUtcTime, DateTimeToDateTimeOffset, -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY DateOnlyConstruct, TimeOnlyConstruct, #endif diff --git a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs index 3ff469a757..210b44e1bf 100644 --- a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs @@ -142,8 +142,16 @@ public void Add(SqlType sqlType, DataTypeInfo dataTypeInfo) /// public DataTypeInfo Interval { get; set; } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + /// Date data from January 1,1 A.D. through December 31, 9999 A.D., + /// Storage size is 8 to 10 bytes. + /// public DataTypeInfo DateOnly { get; set; } + + /// + /// Time data. Values mignt be rounded to some fractions of a second. + /// public DataTypeInfo TimeOnly { get; set; } #endif @@ -239,7 +247,7 @@ IEnumerator IEnumerable.GetEnumerator() yield return VarBinaryMax; yield return Guid; yield return Interval; -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY yield return DateOnly; yield return TimeOnly; #endif diff --git a/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs b/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs index 02a2056845..d1c9e81916 100644 --- a/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs +++ b/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs @@ -78,9 +78,14 @@ public partial class ValueRange /// public static readonly ValueRange TimeSpan = new ValueRange(System.TimeSpan.MinValue, System.TimeSpan.MaxValue); - -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + /// Standard value range for + /// public static readonly ValueRange DateOnly = new(System.DateOnly.MinValue, System.DateOnly.MaxValue); + /// + /// Standard value range for + /// public static readonly ValueRange TimeOnly = new(System.TimeOnly.MinValue, System.TimeOnly.MaxValue); #endif } diff --git a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs index 54ec399a13..2082832491 100644 --- a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs +++ b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs @@ -29,12 +29,12 @@ internal static class SqlValidator WellKnownTypes.Double, WellKnownTypes.Decimal, WellKnownTypes.DateTime, - WellKnownTypes.DateTimeOffset, - WellKnownTypes.TimeSpan, -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY WellKnownTypes.DateOnly, WellKnownTypes.TimeOnly, #endif + WellKnownTypes.DateTimeOffset, + WellKnownTypes.TimeSpan, WellKnownTypes.ByteArray, WellKnownTypes.Guid }; diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 955b9560f9..5d69f3e228 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -564,7 +564,7 @@ public static SqlFunctionCall DateTimeConstruct(SqlExpression year, SqlExpressio return new SqlFunctionCall(SqlFunctionType.DateTimeConstruct, year, month, day); } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY public static SqlFunctionCall DateOnlyConstruct(SqlExpression year, SqlExpression month, SqlExpression day) { ArgumentNullException.ThrowIfNull(year); @@ -622,6 +622,7 @@ public static SqlFunctionCall DateTimeAddMonths(SqlExpression source, SqlExpress return new SqlFunctionCall(SqlFunctionType.DateTimeAddMonths, source, months); } +#if NET6_0_OR_GREATER //DO_DATEONLY public static SqlFunctionCall DateOnlyAddDays(SqlExpression source, SqlExpression days) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); @@ -642,6 +643,7 @@ public static SqlFunctionCall TimeOnlyAddMinutes(SqlExpression source, SqlExpres ArgumentValidator.EnsureArgumentNotNull(minutes, "minutes"); return new SqlFunctionCall(SqlFunctionType.TimeOnlyAddMinutes, source, minutes); } +#endif public static SqlFunctionCall DateTimeToStringIso(SqlExpression expression) { diff --git a/Orm/Xtensive.Orm/Sql/SqlDriver.cs b/Orm/Xtensive.Orm/Sql/SqlDriver.cs index bf055dc4d8..8e9603009f 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDriver.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDriver.cs @@ -421,7 +421,7 @@ private static void RegisterStandardMappings(TypeMappingRegistryBuilder builder) builder.Add(WellKnownTypes.TimeSpan, mapper.ReadTimeSpan, mapper.BindTimeSpan, mapper.MapTimeSpan); builder.Add(WellKnownTypes.Guid, mapper.ReadGuid, mapper.BindGuid, mapper.MapGuid); builder.Add(WellKnownTypes.ByteArray, mapper.ReadByteArray, mapper.BindByteArray, mapper.MapByteArray); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY builder.Add(WellKnownTypes.DateOnly, mapper.ReadDateOnly, mapper.BindDateOnly, mapper.MapDateOnly); builder.Add(WellKnownTypes.TimeOnly, mapper.ReadTimeOnly, mapper.BindTimeOnly, mapper.MapTimeOnly); #endif @@ -450,7 +450,7 @@ private static void RegisterStandardReverseMappings(TypeMappingRegistryBuilder b builder.AddReverse(SqlType.VarBinary, WellKnownTypes.ByteArray); builder.AddReverse(SqlType.VarBinaryMax, WellKnownTypes.ByteArray); builder.AddReverse(SqlType.Guid, WellKnownTypes.Guid); -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY builder.AddReverse(SqlType.Date, WellKnownTypes.DateOnly); builder.AddReverse(SqlType.Time, WellKnownTypes.TimeOnly); #endif diff --git a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs index e47ddb0206..347e8facc4 100644 --- a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs +++ b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs @@ -132,6 +132,20 @@ public virtual void BindDateTime(DbParameter parameter, object value) parameter.Value = value ?? DBNull.Value; } +#if NET6_0_OR_GREATER //DO_DATEONLY + public virtual void BindDateOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.Date; + parameter.Value = value != null ? ((DateOnly) value).ToDateTime(TimeOnly.MinValue) : DBNull.Value; + } + + public virtual void BindTimeOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.Time; + parameter.Value = value != null ? ((TimeOnly) value).ToTimeSpan() : DBNull.Value; + } +#endif + public virtual void BindDateTimeOffset(DbParameter parameter, object value) { parameter.DbType = DbType.DateTimeOffset; @@ -210,6 +224,14 @@ public virtual object ReadDecimal(DbDataReader reader, int index) => public virtual object ReadDateTime(DbDataReader reader, int index) => reader.GetDateTime(index); +#if NET6_0_OR_GREATER //DO_DATEONLY + public virtual object ReadDateOnly(DbDataReader reader, int index) => + DateOnly.FromDateTime(reader.GetFieldValue(index)); + + public virtual object ReadTimeOnly(DbDataReader reader, int index) => + TimeOnly.FromTimeSpan(reader.GetFieldValue(index)); +#endif + public virtual object ReadDateTimeOffset(DbDataReader reader, int index) => (DateTimeOffset) reader.GetValue(index); @@ -306,6 +328,14 @@ public virtual SqlValueType MapDecimal(int? length, int? precision, int? scale) public virtual SqlValueType MapDateTime(int? length, int? precision, int? scale) => new SqlValueType(SqlType.DateTime); +#if NET6_0_OR_GREATER //DO_DATEONLY + public virtual SqlValueType MapDateOnly(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Date); + + public virtual SqlValueType MapTimeOnly(int? length, int? precision, int? scale) => + new SqlValueType(SqlType.Time); +#endif + public virtual SqlValueType MapDateTimeOffset(int? length, int? precision, int? scale) => new SqlValueType(SqlType.DateTimeOffset); @@ -318,32 +348,6 @@ public virtual SqlValueType MapGuid(int? length, int? precision, int? scale) => public virtual SqlValueType MapByteArray(int? length, int? precision, int? scale) => ChooseStreamType(SqlType.VarBinary, SqlType.VarBinaryMax, length, VarBinaryMaxLength); -#if DO_DATEONLY - public virtual void BindDateOnly(DbParameter parameter, object value) - { - parameter.DbType = DbType.Date; - parameter.Value = value != null ? ((DateOnly)value).ToDateTime(TimeOnly.MinValue) : DBNull.Value; - } - - public virtual void BindTimeOnly(DbParameter parameter, object value) - { - parameter.DbType = DbType.Time; - parameter.Value = value != null ? ((TimeOnly)value).ToTimeSpan() : DBNull.Value; - } - - public virtual object ReadDateOnly(DbDataReader reader, int index) => - DateOnly.FromDateTime(reader.GetFieldValue(index)); - - public virtual object ReadTimeOnly(DbDataReader reader, int index) => - TimeOnly.FromTimeSpan(reader.GetFieldValue(index)); - - public virtual SqlValueType MapDateOnly(int? length, int? precision, int? scale) => - new SqlValueType(SqlType.Date); - - public virtual SqlValueType MapTimeOnly(int? length, int? precision, int? scale) => - new SqlValueType(SqlType.Time); -#endif // DO_DATEONLY - #endregion protected static SqlValueType ChooseStreamType(SqlType varType, SqlType varMaxType, int? length, int? varTypeMaxLength) diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs index f467983803..d27a44d1c4 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs @@ -588,7 +588,7 @@ public DateTimeOffsetFieldAccessor() { } } -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY internal sealed class DateOnlyFieldAccessor : ValueFieldAccessor { protected override DateOnly Decode(long value) => diff --git a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs index 6342d49e25..0751bff4c4 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs @@ -34,28 +34,26 @@ private ref struct Counters private static class ValueFieldAccessorResolver { - private static readonly ValueFieldAccessor - BoolAccessor = new BooleanFieldAccessor(), - ByteAccessor = new ByteFieldAccessor(), - SByteAccessor = new SByteFieldAccessor(), - Int16Accessor = new ShortFieldAccessor(), - UInt16Accessor = new UShortFieldAccessor(), - Int32Accessor = new IntFieldAccessor(), - UInt32Accessor = new UIntFieldAccessor(), - Int64Accessor = new LongFieldAccessor(), - UInt64Accessor = new ULongFieldAccessor(), - SingleAccessor = new FloatFieldAccessor(), - DoubleAccessor = new DoubleFieldAccessor(), - DateTimeAccessor = new DateTimeFieldAccessor(), - TimeSpanAccessor = new TimeSpanFieldAccessor(), - DecimalAccessor = new DecimalFieldAccessor(), - GuidAccessor = new GuidFieldAccessor(), - DateTimeOffsetAccessor = new DateTimeOffsetFieldAccessor() -#if DO_DATEONLY - , DateOnlyAccessor = new DateOnlyFieldAccessor() - , TimeOnlyAccessor = new TimeOnlyFieldAccessor() + private static readonly ValueFieldAccessor BoolAccessor = new BooleanFieldAccessor(); + private static readonly ValueFieldAccessor ByteAccessor = new ByteFieldAccessor(); + private static readonly ValueFieldAccessor SByteAccessor = new SByteFieldAccessor(); + private static readonly ValueFieldAccessor Int16Accessor = new ShortFieldAccessor(); + private static readonly ValueFieldAccessor UInt16Accessor = new UShortFieldAccessor(); + private static readonly ValueFieldAccessor Int32Accessor = new IntFieldAccessor(); + private static readonly ValueFieldAccessor UInt32Accessor = new UIntFieldAccessor(); + private static readonly ValueFieldAccessor Int64Accessor = new LongFieldAccessor(); + private static readonly ValueFieldAccessor UInt64Accessor = new ULongFieldAccessor(); + private static readonly ValueFieldAccessor SingleAccessor = new FloatFieldAccessor(); + private static readonly ValueFieldAccessor DoubleAccessor = new DoubleFieldAccessor(); + private static readonly ValueFieldAccessor DateTimeAccessor = new DateTimeFieldAccessor(); +#if NET6_0_OR_GREATER //DO_DATEONLY + private static readonly ValueFieldAccessor DateOnlyAccessor = new DateOnlyFieldAccessor(); + private static readonly ValueFieldAccessor TimeOnlyAccessor = new TimeOnlyFieldAccessor(); #endif - ; + private static readonly ValueFieldAccessor TimeSpanAccessor = new TimeSpanFieldAccessor(); + private static readonly ValueFieldAccessor DecimalAccessor = new DecimalFieldAccessor(); + private static readonly ValueFieldAccessor GuidAccessor = new GuidFieldAccessor(); + private static readonly ValueFieldAccessor DateTimeOffsetAccessor = new DateTimeOffsetFieldAccessor(); private static readonly int NullableTypeMetadataToken = WellKnownTypes.NullableOfT.MetadataToken; @@ -80,7 +78,7 @@ ValueFieldAccessor ResolveByType(Type type) ReferenceEquals(type, WellKnownTypes.Double) ? DoubleAccessor : ReferenceEquals(type, WellKnownTypes.DateTime) ? DateTimeAccessor : ReferenceEquals(type, WellKnownTypes.TimeSpan) ? TimeSpanAccessor : -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY ReferenceEquals(type, WellKnownTypes.DateOnly) ? DateOnlyAccessor : ReferenceEquals(type, WellKnownTypes.TimeOnly) ? TimeOnlyAccessor : #endif @@ -104,7 +102,7 @@ ValueFieldAccessor ResolveByNullableType(Type type) ReferenceEquals(type, WellKnownTypes.NullableDouble) ? DoubleAccessor : ReferenceEquals(type, WellKnownTypes.NullableDateTime) ? DateTimeAccessor : ReferenceEquals(type, WellKnownTypes.NullableTimeSpan) ? TimeSpanAccessor : -#if DO_DATEONLY +#if NET6_0_OR_GREATER //DO_DATEONLY ReferenceEquals(type, WellKnownTypes.NullableDateOnly) ? DateOnlyAccessor : ReferenceEquals(type, WellKnownTypes.NullableTimeOnly) ? TimeOnlyAccessor : #endif From 58e2a7b6dd533d599b3072eaf6cf8a1ff92acedc Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 30 Nov 2022 20:02:52 +0500 Subject: [PATCH 10/86] No env var DO_DATEONLY support --- Directory.Build.props | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 90b60e781e..5b17ca697b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -128,9 +128,4 @@ - - - - $(DefineConstants);DO_DATEONLY - From 4a29d38aac397530f119e56d9a1767517f93ff93 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 1 Dec 2022 13:44:33 +0500 Subject: [PATCH 11/86] Add implicit conversion of DateOnly/TimeOnly to SqlExpression --- .../Sql/Dml/Expressions/SqlExpression.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs index b0be887cab..a988cf839a 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs @@ -205,6 +205,18 @@ public static implicit operator SqlExpression(DateTime value) return new SqlLiteral(value); } +#if NET6_0_OR_GREATER //DO_DATEONLY + public static implicit operator SqlExpression(DateOnly value) + { + return new SqlLiteral(value); + } + + public static implicit operator SqlExpression(TimeOnly value) + { + return new SqlLiteral(value); + } +#endif + public static implicit operator SqlExpression(DateTimeOffset value) { return new SqlLiteral(value); From f379ba770df4b18f7dcac164a801aea2c68c6025 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 1 Dec 2022 16:14:10 +0500 Subject: [PATCH 12/86] SqlDml level changes - new SqlDml Methods for Time and Date - rename DateOnlyXxx and TimeOnlyXxx to DateXxx and TimeXxx to be related to with SQL - new SqlFunctionTypes & SqlNodeTypes --- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 2 +- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 6 +-- .../MemberCompilers/DateOnlyCompilers.cs | 4 +- .../MemberCompilers/TimeOnlyCompilers.cs | 10 ++--- Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 12 ++--- Orm/Xtensive.Orm/Sql/SqlDml.cs | 45 ++++++++++++++----- Orm/Xtensive.Orm/Sql/SqlNodeType.cs | 1 + 7 files changed, 53 insertions(+), 27 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index 9d88b98cd7..a8496c86a9 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -191,7 +191,7 @@ public override void Visit(SqlFunctionCall node) Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); return; #if NET6_0_OR_GREATER //DO_DATEONLY - case SqlFunctionType.DateOnlyAddDays: + case SqlFunctionType.DateAddDays: Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); return; #endif diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index 0ccf8361f4..85a9572a5d 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -200,13 +200,13 @@ public override void Visit(SqlFunctionCall node) Visit(DateAddYear(arguments[0], arguments[1])); return; #if NET6_0_OR_GREATER //DO_DATEONLY - case SqlFunctionType.DateOnlyAddDays: + case SqlFunctionType.DateAddDays: Visit(DateAddDay(arguments[0], arguments[1])); return; - case SqlFunctionType.TimeOnlyAddHours: + case SqlFunctionType.TimeAddHours: Visit(DateAddHour(arguments[0], arguments[1])); return; - case SqlFunctionType.TimeOnlyAddMinutes: + case SqlFunctionType.TimeAddMinutes: Visit(DateAddMinute(arguments[0], arguments[1])); return; #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index 2cc3c03c76..e812b348cb 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -60,7 +60,7 @@ public static SqlExpression DateOnlyCtor( [Type(typeof(int))] SqlExpression year, [Type(typeof(int))] SqlExpression month, [Type(typeof(int))] SqlExpression day) => - SqlDml.DateOnlyConstruct(year, month, day); + SqlDml.DateConstruct(year, month, day); #endregion @@ -126,7 +126,7 @@ public static SqlExpression DateOnlyAddMonths(SqlExpression _this, [Type(typeof( [Compiler(typeof(DateOnly), "AddDays")] public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => - SqlDml.DateOnlyAddDays(_this, value); + SqlDml.DateAddDays(_this, value); [Compiler(typeof(DateOnly), "ToString")] public static SqlExpression DateTimeToStringIso(SqlExpression _this) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index 4f2875e10b..f75e98c0be 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -41,17 +41,17 @@ public static SqlExpression TimeOnlyCtor( [Type(typeof(int))] SqlExpression hour, [Type(typeof(int))] SqlExpression minute, [Type(typeof(int))] SqlExpression second) => - SqlDml.TimeOnlyConstruct(hour, minute, second, 0); + SqlDml.TimeConstruct(hour, minute, second, 0); [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] public static SqlExpression TimeOnlyCtor( [Type(typeof(int))] SqlExpression hour, [Type(typeof(int))] SqlExpression minute) => - SqlDml.TimeOnlyConstruct(hour, minute, 0, 0); + SqlDml.TimeConstruct(hour, minute, 0, 0); [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression ticks) => - new SqlFunctionCall(SqlFunctionType.TimeOnlyConstruct, ticks); + new SqlFunctionCall(SqlFunctionType.TimeConstruct, ticks); #endregion @@ -166,11 +166,11 @@ public static SqlExpression DateTimeOperatorSubtractionDateTime( [Compiler(typeof(TimeOnly), "AddHours")] public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => - SqlDml.TimeOnlyAddHours(_this, value); + SqlDml.TimeAddHours(_this, value); [Compiler(typeof(TimeOnly), "AddMinutes")] public static SqlExpression TimeOnlyAddMinutes(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => - SqlDml.TimeOnlyAddMinutes(_this, value); + SqlDml.TimeAddMinutes(_this, value); [Compiler(typeof(TimeOnly), "ToString")] public static SqlExpression TimeOnlyToStringIso(SqlExpression _this) diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index 52853dde2a..a312a17797 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -65,9 +65,11 @@ public enum SqlFunctionType DateTimeAddYears, DateTimeAddMonths, #if NET6_0_OR_GREATER //DO_DATEONLY - DateOnlyAddDays, - TimeOnlyAddHours, - TimeOnlyAddMinutes, + DateAddYears, + DateAddMonths, + DateAddDays, + TimeAddHours, + TimeAddMinutes, #endif DateTimeTruncate, DateTimeToStringIso, @@ -87,8 +89,8 @@ public enum SqlFunctionType DateTimeToDateTimeOffset, #if NET6_0_OR_GREATER //DO_DATEONLY - DateOnlyConstruct, - TimeOnlyConstruct, + DateConstruct, + TimeConstruct, #endif // .NET like rounding functions diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 5d69f3e228..1f8c5dc049 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -494,7 +494,7 @@ public static SqlBinary LessThanOrEquals(SqlExpression left, SqlExpression right #endregion - # region DateTime functions + # region Date and/or Time functions public static SqlFunctionCall CurrentDate() { @@ -565,7 +565,7 @@ public static SqlFunctionCall DateTimeConstruct(SqlExpression year, SqlExpressio } #if NET6_0_OR_GREATER //DO_DATEONLY - public static SqlFunctionCall DateOnlyConstruct(SqlExpression year, SqlExpression month, SqlExpression day) + public static SqlFunctionCall DateConstruct(SqlExpression year, SqlExpression month, SqlExpression day) { ArgumentNullException.ThrowIfNull(year); ArgumentNullException.ThrowIfNull(month); @@ -573,17 +573,17 @@ public static SqlFunctionCall DateOnlyConstruct(SqlExpression year, SqlExpressio SqlValidator.EnsureIsArithmeticExpression(year); SqlValidator.EnsureIsArithmeticExpression(month); SqlValidator.EnsureIsArithmeticExpression(day); - return new SqlFunctionCall(SqlFunctionType.DateOnlyConstruct, year, month, day); + return new SqlFunctionCall(SqlFunctionType.DateConstruct, year, month, day); } - public static SqlFunctionCall TimeOnlyConstruct(SqlExpression hours, + public static SqlFunctionCall TimeConstruct(SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) { var m = milliseconds + 1000L * (seconds + 60L * (minutes + 60L * hours)); var ticks = 10_000 * m; - return new SqlFunctionCall(SqlFunctionType.TimeOnlyConstruct, ticks); + return new SqlFunctionCall(SqlFunctionType.TimeConstruct, ticks); } #endif @@ -608,6 +608,15 @@ public static SqlBinary DateTimeMinusDateTime(SqlExpression left, SqlExpression return new SqlBinary(SqlNodeType.DateTimeMinusDateTime, left, right); } +#if NET6_0_OR_GREATER //DO_DATEONLY + public static SqlBinary DateMinusDate(SqlExpression left, SqlExpression right) + { + ArgumentValidator.EnsureArgumentNotNull(left, "left"); + ArgumentValidator.EnsureArgumentNotNull(right, "right"); + return new SqlBinary(SqlNodeType.DateMinusDate, left, right); + } +#endif + public static SqlFunctionCall DateTimeAddYears(SqlExpression source, SqlExpression years) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); @@ -623,25 +632,39 @@ public static SqlFunctionCall DateTimeAddMonths(SqlExpression source, SqlExpress } #if NET6_0_OR_GREATER //DO_DATEONLY - public static SqlFunctionCall DateOnlyAddDays(SqlExpression source, SqlExpression days) + public static SqlFunctionCall DateAddYears(SqlExpression source, SqlExpression years) + { + ArgumentValidator.EnsureArgumentNotNull(source, "source"); + ArgumentValidator.EnsureArgumentNotNull(years, "years"); + return new SqlFunctionCall(SqlFunctionType.DateAddYears, source, years); + } + + public static SqlFunctionCall DateAddMonths(SqlExpression source, SqlExpression months) + { + ArgumentValidator.EnsureArgumentNotNull(source, "source"); + ArgumentValidator.EnsureArgumentNotNull(months, "months"); + return new SqlFunctionCall(SqlFunctionType.DateAddMonths, source, months); + } + + public static SqlFunctionCall DateAddDays(SqlExpression source, SqlExpression days) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); ArgumentValidator.EnsureArgumentNotNull(days, "days"); - return new SqlFunctionCall(SqlFunctionType.DateOnlyAddDays, source, days); + return new SqlFunctionCall(SqlFunctionType.DateAddDays, source, days); } - public static SqlFunctionCall TimeOnlyAddHours(SqlExpression source, SqlExpression hours) + public static SqlFunctionCall TimeAddHours(SqlExpression source, SqlExpression hours) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); ArgumentValidator.EnsureArgumentNotNull(hours, "hours"); - return new SqlFunctionCall(SqlFunctionType.TimeOnlyAddHours, source, hours); + return new SqlFunctionCall(SqlFunctionType.TimeAddHours, source, hours); } - public static SqlFunctionCall TimeOnlyAddMinutes(SqlExpression source, SqlExpression minutes) + public static SqlFunctionCall TimeAddMinutes(SqlExpression source, SqlExpression minutes) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); ArgumentValidator.EnsureArgumentNotNull(minutes, "minutes"); - return new SqlFunctionCall(SqlFunctionType.TimeOnlyAddMinutes, source, minutes); + return new SqlFunctionCall(SqlFunctionType.TimeAddMinutes, source, minutes); } #endif diff --git a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs index 159a5520ab..67454150e7 100644 --- a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs +++ b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs @@ -113,5 +113,6 @@ public enum SqlNodeType While, Fragment, Metadata, + DateMinusDate, } } \ No newline at end of file From f6c8b2cfdfd505536ac1f8bb4c17d45407f5c640 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 1 Dec 2022 16:18:42 +0500 Subject: [PATCH 13/86] Add Sql-level tests for DateOnly and TimeOnly --- .../DateTimeIntervalTest.cs | 123 ++++++++++++++++++ Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs | 18 +++ 2 files changed, 141 insertions(+) diff --git a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs index abd968b9e2..ab3997e06d 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs @@ -15,6 +15,15 @@ public abstract class DateTimeIntervalTest : SqlTest { protected static readonly DateTime DefaultDateTime = new DateTime(2001, 2, 3, 4, 5, 6, 334); protected static readonly DateTime SecondDateTime = new DateTime(2000, 12, 11, 10, 9, 8, 765); + +#if NET6_0_OR_GREATER + protected static readonly DateOnly DefaultDateOnly = new DateOnly(2001, 2, 3); + protected static readonly DateOnly SecondDateOnly = new DateOnly(2000, 12, 11); + + protected static readonly TimeOnly DefaultTimeOnly = new TimeOnly(4, 5, 6, 334); + protected static readonly TimeOnly SecondTimeOnly = new TimeOnly(10, 9, 8, 765); +#endif + protected static readonly TimeSpan DefaultTimeSpan = new TimeSpan(10, 9, 8, 7, 652); protected static readonly int AddYearsConst = 5; protected static readonly int AddMonthsConst = 15; @@ -147,6 +156,120 @@ public virtual void DateTimeExtractDayOfYearTest() DefaultDateTime.DayOfYear); } +#if NET6_0_OR_GREATER + [Test] + public virtual void DateOnlyAddYearsTest() + { + CheckEquality( + SqlDml.DateAddYears(DefaultDateOnly, AddYearsConst), + DefaultDateOnly.AddYears(AddYearsConst)); + } + + [Test] + public virtual void DateOnlyAddMonthsTest() + { + CheckEquality( + SqlDml.DateAddMonths(DefaultDateOnly, AddMonthsConst), + DefaultDateOnly.AddMonths(AddMonthsConst)); + } + + [Test] + public virtual void DateOnlyConstructTest() + { + CheckEquality( + SqlDml.DateConstruct(DefaultDateOnly.Year, DefaultDateOnly.Month, DefaultDateOnly.Day), + DefaultDateOnly); + } + + [Test] + public virtual void DateOnlySubtractDateTimeTest() + { + CheckEquality( + SqlDml.DateMinusDate(DefaultDateOnly, SecondDateOnly), + DefaultDateTime - SecondDateTime); + } + + [Test] + public virtual void DateOnlyExtractYearTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Year, DefaultDateOnly), + DefaultDateOnly.Year); + } + + [Test] + public virtual void DateOnlyExtractMonthTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Month, DefaultDateOnly), + DefaultDateOnly.Month); + } + + [Test] + public virtual void DateOnlyExtractDayTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Day, DefaultDateOnly), + DefaultDateOnly.Day); + } + + [Test] + public virtual void DateOnlyExtractDayOfWeekTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.DayOfWeek, DefaultDateOnly), + (int) DefaultDateOnly.DayOfWeek); + } + + [Test] + public virtual void DateOnlyExtractDayOfYearTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.DayOfYear, DefaultDateOnly), + DefaultDateOnly.DayOfYear); + } + + [Test] + public virtual void TimeOnlyExtractHourTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Hour, DefaultTimeOnly), + DefaultTimeOnly.Hour); + } + + [Test] + public virtual void TimeOnlyExtractMinuteTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Minute, DefaultTimeOnly), + DefaultTimeOnly.Minute); + } + + [Test] + public virtual void TimeOnlyExtractSecondTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Second, DefaultTimeOnly), + DefaultTimeOnly.Second); + } + + [Test] + public virtual void TimeOnlyExtractMillisecondTest() + { + CheckEquality( + SqlDml.Extract(SqlDateTimePart.Millisecond, DefaultTimeOnly), + DefaultTimeOnly.Millisecond); + } + + [Test] + public virtual void TimeOnlyConstructTest() + { + CheckEquality( + SqlDml.TimeConstruct(DefaultTimeOnly.Hour, DefaultTimeOnly.Minute, DefaultTimeOnly.Second, DefaultTimeOnly.Millisecond), + DefaultTimeOnly); + } +#endif + [Test] public virtual void IntervalConstructTest() { diff --git a/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs b/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs index 93eea89ad5..a999810dd9 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs @@ -258,6 +258,24 @@ private static object[] GetTestValues(Type type) new DateTimeOffset(2001, 1, 1, 1, 1, 1, 1, new TimeSpan(3, 10, 0)), null }; +#if NET6_0_OR_GREATER // DO_DATEONLY + if (type == typeof(DateOnly)) { + return new object[] { + new DateOnly(2005, 5, 5), + new DateOnly(1998, 8, 8), + new DateOnly(1856, 4, 1), + null + }; + } + if (type == typeof(TimeOnly)) { + return new object[] { + new TimeOnly(5, 5, 5), + new TimeOnly(8, 8, 8), + new TimeOnly(5, 6, 7), + null + }; + } +#endif throw new ArgumentOutOfRangeException(); } From 26bf4aed539c82dd89757247b064f2062968f616 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 15 Dec 2022 08:29:38 +0500 Subject: [PATCH 14/86] SqlType: Fix compilation error for NET5 --- Orm/Xtensive.Orm/Sql/SqlType.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Orm/Xtensive.Orm/Sql/SqlType.cs b/Orm/Xtensive.Orm/Sql/SqlType.cs index 895a17a3b5..cfe3f34ef4 100644 --- a/Orm/Xtensive.Orm/Sql/SqlType.cs +++ b/Orm/Xtensive.Orm/Sql/SqlType.cs @@ -97,6 +97,7 @@ public struct SqlType : IEquatable /// public static readonly SqlType DateTimeOffset = new SqlType("DateTimeOffset"); +#if NET6_0_OR_GREATER //DO_DATEONLY /// /// Date from January 1, 1753 through December 31, 9999, /// Storage size is 3 bytes. @@ -110,6 +111,7 @@ public struct SqlType : IEquatable /// Storage size is 6 bytes. /// public static readonly SqlType Time = new SqlType("Time"); +#endif /// /// Datetime interval. From 78cfbfb288d70c617f2abcd4741057efb08b6e25 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 16 Jan 2023 17:21:30 +0500 Subject: [PATCH 15/86] Define DataTypeInfo for DateOnly/TimeOnlySupport for various providers --- .../Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs | 5 +++++ .../Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs | 12 ++++++++++++ .../Sql.Drivers.Oracle/v09/ServerInfoProvider.cs | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs index ef04c0ec26..a7b0e1493a 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs @@ -238,6 +238,11 @@ public override DataTypeCollection GetDataTypesInfo() dtc.DateTime = DataTypeInfo.Range(SqlType.DateTime, commonFeatures, ValueRange.DateTime, "timestamp"); +#if NET6_0_OR_GREATER + dtc.DateOnly = DataTypeInfo.Range(SqlType.Date, commonFeatures, ValueRange.DateOnly, "date"); + dtc.TimeOnly = DataTypeInfo.Range(SqlType.Time, commonFeatures, ValueRange.TimeOnly, "time"); +#endif + dtc.Char = DataTypeInfo.Stream(SqlType.Char, commonFeatures, MaxCharLength, "char"); dtc.VarChar = DataTypeInfo.Stream(SqlType.VarChar, commonFeatures, MaxCharLength, "varchar"); dtc.VarCharMax = DataTypeInfo.Stream(SqlType.VarCharMax, lobFeatures, MaxTextLength, "blob sub_type 1"); diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs index 487dea55dc..dd71caeba6 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs @@ -261,9 +261,21 @@ public override DataTypeCollection GetDataTypesInfo() types.Double = DataTypeInfo.Range(SqlType.Double, common | index, ValueRange.Double, "double precision"); +#if NET6_0_OR_GREATER types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, + new ValueRange(new DateTime(1000, 1, 1), new DateTime(9999, 12, 31)), + "datetime"); + types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, + new ValueRange(new DateTime(1000, 1, 1), new DateTime(9999, 12, 31)), + "date"); + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, ValueRange.TimeOnly, "time"); +#else + types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, new ValueRange(new DateTime(1000, 1, 1), new DateTime(9999, 12, 31)), "datetime", "time"); +#endif + + types.Char = DataTypeInfo.Stream(SqlType.Char, common | index, 255, "char"); types.VarChar = DataTypeInfo.Stream(SqlType.VarChar, common | index, 4000, "varchar"); diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs index 029367b750..d05bd938bb 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs @@ -225,8 +225,16 @@ public override DataTypeCollection GetDataTypesInfo() ValueRange.Float, "real"); types.Double = DataTypeInfo.Range(SqlType.Double, common | index, ValueRange.Double, "double precision", "float"); + types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, ValueRange.DateTime, "timestamp"); +#if NET6_0_OR_GREATER + types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, + ValueRange.DateOnly, "DATE"); + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, + ValueRange.TimeOnly, "interval day(0) to second"); +#endif + types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index, ValueRange.DateTimeOffset, "TIMESTAMP WITH TIME ZONE"); types.Interval = DataTypeInfo.Range(SqlType.Interval, common | index, From 46cdb51287e2c4e1d285871526950e53e860d797 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 16 Jan 2023 17:23:13 +0500 Subject: [PATCH 16/86] Add base test class for tests of schema extraction --- .../ExtractorTestBase.cs | 443 ++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs new file mode 100644 index 0000000000..3dec314c94 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -0,0 +1,443 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Xtensive.Sql; +using Xtensive.Sql.Info; +using Xtensive.Sql.Model; + +namespace Xtensive.Orm.Tests.Sql +{ + [TestFixture] + public abstract class ExtractorTestBase : SqlTest + { + protected const int CharLength = 3; + protected const int VarCharLength = 6; + + protected const int BinaryLength = 3; + protected const int VarBinaryLength = 6; + + protected const int DecimalScale = 4; + protected const int DecimalPrecision = 12; + + private readonly List schemasToCheck = new(); + private readonly List cleanups = new(); + + protected readonly Dictionary TypeToColumnName = new(); + + protected virtual string CleanUpScript => null; + protected virtual bool CheckContstraintExtracted => true; + + protected bool IgnoreTests { get; set; } = false; + protected bool NonKeyColumnsSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.NonKeyColumns); + protected bool PartialIndexesSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.Filtered); + protected bool FulltextIndexesSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.FullText); + + protected override void TestFixtureSetUp() + { + if (IgnoreTests) { + throw new IgnoreException(string.Empty); + } + base.TestFixtureSetUp(); + + PopulateTypeToColumnName(); + PopulateSchemasToCheck(); + } + + protected override void TestFixtureTearDown() + { + if (!IgnoreTests) { + if (Driver.ServerInfo.Query.Features.HasFlag(QueryFeatures.DdlBatches)) { + var cleanupScript = BuildCleanUpScrcipt(); + ExecuteQuery(cleanupScript); + } + else { + foreach (var query in cleanups) { + ExecuteQuery(query); + } + } + + ExecuteQuery(CleanUpScript); + } + + base.TestFixtureTearDown(); + + string BuildCleanUpScrcipt() + { + var sb = new StringBuilder(cleanups.Select(s => s.Length).Sum()); + foreach (var s in cleanups) { + _ = sb.AppendLine(s); + } + return sb.ToString(); + } + } + + [Test] + public void SchemaExtractionTest() + { + Require.AllFeaturesSupported(Providers.ProviderFeatures.Multischema); + + var catalog = ExtractCatalog(); + + Assert.That(schemasToCheck.All(s => catalog.Schemas[s] != null), Is.True); + } + + + protected abstract string GetTypesExtractionPrepareScript(string tableName); + protected abstract string GetTypesExtractionCleanUpScript(string tableName); + + // Test expects a table with the given name containing column for each supported by + // the tested storage, column names for each SqlType are in TypeToColumnName dictionary + [Test] + public void TypeExtractionTest() + { + var createTableQuery = GetTypesExtractionPrepareScript("dataTypesTestTable"); + RegisterCleanupScript(GetTypesExtractionCleanUpScript, "dataTypesTestTable"); + + ExecuteQuery(createTableQuery); + + var testTable = ExtractDefaultSchema().Tables["dataTypesTestTable"]; + + foreach (var keyValue in TypeToColumnName) { + var sqlType = keyValue.Key; + var columnName = keyValue.Value; + var tableColumn = testTable.TableColumns[columnName]; + if (tableColumn == null) { + continue; + } + Assert.That(tableColumn.DataType.Type, Is.EqualTo(sqlType)); + if (sqlType == SqlType.Char || sqlType == SqlType.VarChar + || sqlType == SqlType.Binary || sqlType == SqlType.VarBinary) { + Assert.That(tableColumn.DataType.Length, Is.EqualTo(GetExpectedLength(sqlType))); + } + if (sqlType == SqlType.Decimal) { + Assert.That(tableColumn.DataType.Precision, Is.EqualTo(DecimalPrecision)); + Assert.That(tableColumn.DataType.Scale, Is.EqualTo(DecimalScale)); + } + } + + + static int GetExpectedLength(SqlType sqlType1) + { + if (sqlType1 == SqlType.Char) { + return CharLength; + } + else if (sqlType1 == SqlType.VarChar) { + return VarCharLength; + } + else if (sqlType1 == SqlType.Binary) { + return BinaryLength; + } + else if (sqlType1 == SqlType.VarBinary) { + return VarBinaryLength; + } + throw new ArgumentOutOfRangeException(nameof(sqlType1)); + } + } + + + protected abstract string GetForeignKeyExtractionPrepareScript(); + protected abstract string GetForeignKeyExtractionCleanUpScript(); + + // Test expects Storage variant of following structure + // CREATE TABLE B1 (b_id int primary key); + // CREATE TABLE A1 (b_id int references B1(b_id)); + // CREATE TABLE B2 (b_id_1 int, b_id_2 int, + // CONSTRAINT [B2_PK] PRIMARY KEY CLUSTERED (b_id_1, b_id_2) ON [PRIMARY]); + // CREATE TABLE A2 (b_id_1 int, b_id_2 int, + // CONSTRAINT [A2_FK] FOREIGN KEY (b_id_1, b_id_2) + // REFERENCES B2 (b_id_1, b_id_2) ON DELETE CASCADE ON UPDATE NO ACTION); + // CREATE TABLE B3 (b_id_1 int, b_id_2 int, b_id_3 int, + // CONSTRAINT [B3_PK] PRIMARY KEY CLUSTERED (b_id_1, b_id_2, b_id_3) ON [PRIMARY]); + // CREATE TABLE A3 (A_col1 int, b_id_3 int, b_id_1 int, b_id_2 int, + // CONSTRAINT [A3_FK] FOREIGN KEY (b_id_1, b_id_2, b_id_3) + // REFERENCES B3 (b_id_1, b_id_2, b_id_3) ON DELETE NO ACTION ON UPDATE CASCADE); + [Test] + public void ForeignKeyExtractionTest() + { + var query = GetForeignKeyExtractionPrepareScript(); + RegisterCleanupScript(GetForeignKeyExtractionCleanUpScript); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + + // Validating. + var fk1 = (ForeignKey) schema.Tables["A1"].TableConstraints[0]; + Assert.IsNotNull(fk1); + Assert.IsTrue(fk1.Columns[0].Name == "b_id"); + Assert.IsTrue(fk1.ReferencedColumns[0].Name == "b_id"); + Assert.IsTrue(fk1.ReferencedColumns.Count == 1); + Assert.IsTrue(fk1.Columns.Count == 1); + + var fk2 = (ForeignKey) schema.Tables["A2"].TableConstraints[0]; + Assert.IsNotNull(fk1); + Assert.IsTrue(fk2.Name == "A2_FK"); + Assert.IsTrue(fk2.Columns[0].Name == "b_id_1"); + Assert.IsTrue(fk2.ReferencedColumns[0].Name == "b_id_1"); + Assert.IsTrue(fk2.Columns[1].Name == "b_id_2"); + Assert.IsTrue(fk2.ReferencedColumns[1].Name == "b_id_2"); + Assert.IsTrue(fk2.ReferencedColumns.Count == 2); + Assert.IsTrue(fk2.Columns.Count == 2); + Assert.IsTrue(fk2.OnDelete == ReferentialAction.Cascade); + Assert.IsTrue(fk2.OnUpdate == ReferentialAction.NoAction); + + var fk3 = (ForeignKey) schema.Tables["A3"].TableConstraints[0]; + Assert.IsNotNull(fk3); + Assert.IsTrue(fk3.Name == "A3_FK"); + Assert.IsTrue(fk3.Columns[0].Name == "b_id_1"); + Assert.IsTrue(fk3.ReferencedColumns[0].Name == "b_id_1"); + Assert.IsTrue(fk3.Columns[1].Name == "b_id_2"); + Assert.IsTrue(fk3.ReferencedColumns[1].Name == "b_id_2"); + Assert.IsTrue(fk3.Columns[2].Name == "b_id_3"); + Assert.IsTrue(fk3.ReferencedColumns[2].Name == "b_id_3"); + Assert.IsTrue(fk3.ReferencedColumns.Count == 3); + Assert.IsTrue(fk3.Columns.Count == 3); + Assert.IsTrue(fk3.OnDelete == ReferentialAction.NoAction); + Assert.IsTrue(fk3.OnUpdate == ReferentialAction.Cascade); + } + + + protected abstract string GetIndexExtractionPrepareScript(string tableName); + protected abstract string GetIndexExtractionCleanUpScript(string tableName); + + // Test expects storage variant of following structure + // CREATE TABLE table1 (column1 int, column2 int); + // CREATE INDEX table1_index1_desc_asc on table1 (column1 desc, column2 asc); + // CREATE UNIQUE INDEX table1_index1_u_asc_desc on table1 (column1 asc, column2 desc); + // CREATE UNIQUE INDEX table1_index_with_included_columns on table1 (column1 asc) include (column2); + // + // if non-key columns are not supported then skip their declaration. + [Test] + public void IndexExtractionTest() + { + var query = GetIndexExtractionPrepareScript("table1"); + RegisterCleanupScript(GetIndexExtractionCleanUpScript, "table1"); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + + Assert.IsTrue(schema.Tables["table1"] != null); + Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index1_desc_asc"]); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns.Count == 2); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Name == "column1"); + Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); + + Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"]); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns.Count == 2); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[0].Ascending); + Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[1].Ascending); + + Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index_with_included_columns"]); + Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].Columns.Count, + "Key columns"); + if (NonKeyColumnsSupported) { + Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].NonkeyColumns.Count, + "Included columns"); + } + } + + + protected virtual string GetPartialIndexExtractionPrepareScript(string tableName) => null; + protected virtual string GetPartialIndexExtractionCleanUpScript(string tableName) => null; + + [Test] + public void TestPartialIndexExtraction() + { + Require.AllFeaturesSupported(Providers.ProviderFeatures.PartialIndexes); + + var query = GetPartialIndexExtractionPrepareScript("partialIndexTestTable"); + RegisterCleanupScript(GetPartialIndexExtractionCleanUpScript, "partialIndexTestTable"); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + + Assert.IsTrue(schema.Tables["partialIndexTestTable"] != null); + Assert.IsNotNull(schema.Tables["partialIndexTestTable"].Indexes["partialIndexTestTable_index1_filtered"]); + Assert.IsTrue(schema.Tables["partialIndexTestTable"].Indexes["partialIndexTestTable_index1_filtered"].Columns.Count == 2); + Assert.IsTrue(schema.Tables["partialIndexTestTable"].Indexes["partialIndexTestTable_index1_filtered"].Columns[0].Name == "column1"); + Assert.IsNotNull(schema.Tables["partialIndexTestTable"].Indexes["partialIndexTestTable_index1_filtered"].Where); + } + + + protected virtual string GetFulltextIndexExtractionPrepareScript(string tableName) => null; + protected virtual string GetFulltextIndexExtractionCleanUpScript(string tableName) => null; + + // Test expects storage variant of following structure + // CREATE TABLE fullTextTestTable (Id int NOT NULL, + // Name nvarchar(100) NULL, + // Comments nvarchar(1000) NULL, + // CONSTRAINT [PK_fullTextTestTable] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]); + // + // CREATE FULLTEXT INDEX ON fullTextTestTable(Name LANGUAGE 1033, Comments LANGUAGE 1033) + // KEY INDEX PK_fullTextTestTable; + [Test] + public void FulltextIndexExtractionTest() + { + Require.AllFeaturesSupported(Providers.ProviderFeatures.FullText); + + var query = GetFulltextIndexExtractionPrepareScript("fullTextTestTable"); + RegisterCleanupScript(GetFulltextIndexExtractionCleanUpScript, "fullTextTestTable"); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + var ftIndex = (FullTextIndex) schema.Tables["fullTextTestTable"].Indexes.FirstOrDefault(i => i.IsFullText); + Assert.IsNotNull(ftIndex); + Assert.That(ftIndex.Columns.Count, Is.EqualTo(2)); + if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.SqlServer)) { + Assert.That(ftIndex.FullTextCatalog, Is.Null); + Assert.That(ftIndex.ChangeTrackingMode, Is.EqualTo(ChangeTrackingMode.Auto)); + Assert.That(ftIndex.UnderlyingUniqueIndex, Is.EqualTo("PK_fullTextTestTable")); + Assert.That(ftIndex.Columns.All(c => c.Languages.Count == 1 && c.Languages[0].Name == "English"), Is.True); + } + } + + + protected abstract string GetUniqueConstraintExtractionPrepareScript(string tableName); + protected abstract string GetUniqueConstraintExtractionCleanUpScript(string tableName); + + // Test expects storage variant of following structure + // CREATE TABLE uniqueConstraintTable ( + // col_11 int, col_12 int, col_13 int, + // col_21 int, col_22 int, col_23 int, + // CONSTRAINT A_UNIQUE_1 UNIQUE(col_11,col_12,col_13), + // CONSTRAINT A_UNIQUE_2 UNIQUE(col_21,col_22,col_23)) + [Test] + public void UniqueConstraintExtractionTest() + { + var query = GetUniqueConstraintExtractionPrepareScript("uniqueConstraintTable"); + RegisterCleanupScript(GetUniqueConstraintExtractionCleanUpScript, "uniqueConstraintTable"); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + + // Validating. + var uniqueConstraint = (UniqueConstraint) schema.Tables["uniqueConstraintTable"].TableConstraints["A_UNIQUE_1"]; + Assert.IsNotNull(uniqueConstraint); + Assert.IsTrue(uniqueConstraint.Columns[0].Name == "col_11"); + Assert.IsTrue(uniqueConstraint.Columns[1].Name == "col_12"); + Assert.IsTrue(uniqueConstraint.Columns[2].Name == "col_13"); + Assert.IsTrue(uniqueConstraint.Columns.Count == 3); + + uniqueConstraint = (UniqueConstraint) schema.Tables["uniqueConstraintTable"].TableConstraints["A_UNIQUE_2"]; + Assert.IsNotNull(uniqueConstraint); + Assert.IsTrue(uniqueConstraint.Columns[0].Name == "col_21"); + Assert.IsTrue(uniqueConstraint.Columns[1].Name == "col_22"); + Assert.IsTrue(uniqueConstraint.Columns[2].Name == "col_23"); + Assert.IsTrue(uniqueConstraint.Columns.Count == 3); + } + + + protected virtual string GetCheckConstraintExtractionPrepareScript(string tableName) => null; + protected virtual string GetCheckConstraintExtractionCleanUpScript(string tableName) => null; + + // Test expects storage variant of following structure + // CREATE TABLE checkConstraintTable ( + // col_11 int, col_12 int, col_13 int, + // col_21 int, col_22 int, col_23 int, + // CONSTRAINT A_CHECK_1 CHECK(col_11 > 0 OR col_12 > 10 OR col_13 > 20), + // CONSTRAINT A_CHECK_2 CHECK(col_21 <0 AND col_22 < 10 AND col_23 < 20)) + [Test] + public void CheckConstraintExtractionTest() + { + if (!CheckContstraintExtracted) + throw new IgnoreException("CheckConstraints are not extracted"); + + var query = GetCheckConstraintExtractionPrepareScript("checkConstraintTable"); + RegisterCleanupScript(GetCheckConstraintExtractionCleanUpScript, "checkConstraintTable"); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + + // Validating. + var checkConstraint = (CheckConstraint) schema.Tables["checkConstraintTable"].TableConstraints["A_CHECK_1"]; + Assert.IsNotNull(checkConstraint); + Assert.IsNotNull(checkConstraint.Condition); + + checkConstraint = (CheckConstraint) schema.Tables["checkConstraintTable"].TableConstraints["A_CHECK_2"]; + Assert.IsNotNull(checkConstraint); + Assert.IsNotNull(checkConstraint.Condition); + } + + private void PopulateTypeToColumnName() + { + TypeToColumnName[SqlType.Boolean] = "boolean_column"; + TypeToColumnName[SqlType.Int8] = "int8_column"; + TypeToColumnName[SqlType.Int16] = "int16_column"; + TypeToColumnName[SqlType.Int32] = "int32_column"; + TypeToColumnName[SqlType.Int64] = "int64_column"; + TypeToColumnName[SqlType.UInt8] = "uint8_column"; + TypeToColumnName[SqlType.UInt16] = "uint16_column"; + TypeToColumnName[SqlType.UInt32] = "uint32_column"; + TypeToColumnName[SqlType.UInt64] = "uint64_column"; + TypeToColumnName[SqlType.Decimal] = $"decimal_p{DecimalPrecision}_s{DecimalScale}_column"; + + TypeToColumnName[SqlType.Float] = "float_column"; + TypeToColumnName[SqlType.Double] = "double_column"; + + TypeToColumnName[SqlType.DateTime] = "datetime_column"; + TypeToColumnName[SqlType.DateTimeOffset] = "datetimeoffset_column"; +#if NET6_0_OR_GREATER //DO_DATEONLY + TypeToColumnName[SqlType.Date] = "date_column"; + TypeToColumnName[SqlType.Time] = "time_column"; +#endif + + TypeToColumnName[SqlType.Char] = $"char_l{CharLength}_column"; + TypeToColumnName[SqlType.VarChar] = $"varchar_l{VarCharLength}_column"; + TypeToColumnName[SqlType.VarCharMax] = "varcharmax_column"; + + TypeToColumnName[SqlType.Binary] = $"binary_l{BinaryLength}_column"; + TypeToColumnName[SqlType.VarBinary] = $"varbinary_l{VarBinaryLength}_column"; + TypeToColumnName[SqlType.VarBinaryMax] = "varbinarymax_column"; + + TypeToColumnName[SqlType.Guid] = "guid_column"; + + PopulateCustomTypeToColumnName(); + } + + protected virtual void PopulateCustomTypeToColumnName() + { + } + + private void PopulateSchemasToCheck() + { + if (!IsMultischemaSupported) { + return; + } + + if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.PostgreSql)) { + schemasToCheck.Add(WellKnownSchemas.PgSqlDefalutSchema); + } + else { + schemasToCheck.Add(WellKnownSchemas.SqlServerDefaultSchema); + } + schemasToCheck.Add(WellKnownSchemas.Schema1); + schemasToCheck.Add(WellKnownSchemas.Schema2); + schemasToCheck.Add(WellKnownSchemas.Schema3); + schemasToCheck.Add(WellKnownSchemas.Schema4); + schemasToCheck.Add(WellKnownSchemas.Schema5); + schemasToCheck.Add(WellKnownSchemas.Schema6); + schemasToCheck.Add(WellKnownSchemas.Schema7); + schemasToCheck.Add(WellKnownSchemas.Schema8); + schemasToCheck.Add(WellKnownSchemas.Schema9); + schemasToCheck.Add(WellKnownSchemas.Schema10); + schemasToCheck.Add(WellKnownSchemas.Schema11); + schemasToCheck.Add(WellKnownSchemas.Schema12); + } + + protected void ExecuteQuery(string sqlQuery) + { + if (string.IsNullOrEmpty(sqlQuery)) + return; + ExecuteNonQuery(sqlQuery); + } + + protected void RegisterCleanupScript(Func func) => cleanups.Add(func()); + protected void RegisterCleanupScript(Func func, string param) => cleanups.Add(func(param)); + } +} From 579d70b03754f5d902a7a86709746dbe756d9d82 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 16 Jan 2023 17:59:58 +0500 Subject: [PATCH 17/86] Replace old MSSQLExtractor-based test with one class based on ExtractorTestBase --- .../SqlServer/MSSQLExtractorTests.cs | 797 ++++-------------- 1 file changed, 178 insertions(+), 619 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs index 5f6335c3a4..0ac25a2a3e 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs @@ -1,155 +1,139 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. using System; -using System.Collections; -using System.Data.Common; +using System.Text; using NUnit.Framework; using Xtensive.Sql; -using Xtensive.Sql.Model; namespace Xtensive.Orm.Tests.Sql.SqlServer { - public class AssertUtility + public class MSSQLExtratorTest : ExtractorTestBase { - public static void AssertArraysAreEqual(Array a1, Array a2) - { - if (a1==a2) - return; - if (a1==null || a2==null) - throw new AssertionException("One of arrays is null."); - if (a1.Length!=a2.Length) - throw new AssertionException("Lengths are different."); - for (int i = 0; i < a1.Length; i++) - Assert.AreEqual(a1.GetValue(i), a2.GetValue(i)); - } + protected override bool CheckContstraintExtracted => false; - public static void AssertCollectionsAreEqual(IEnumerable col1, IEnumerable col2) - { - if (col1==col2) - return; - if (col2==null || col1==null) - throw new AssertionException("One of arrays is null."); - IEnumerator enumerator1 = col1.GetEnumerator(); - IEnumerator enumerator2 = col2.GetEnumerator(); - enumerator1.Reset(); - enumerator2.Reset(); - while (enumerator1.MoveNext()) { - if (!enumerator2.MoveNext()) - throw new AssertionException("Different count."); - Assert.AreEqual(enumerator1.Current, enumerator2.Current); - } - if (enumerator2.MoveNext()) - throw new AssertionException("Different count."); - } - } + protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.SqlServer); - public abstract class MSSQLExtractorTestBase : SqlTest - { - private bool isTestsIgnored = true; - - public virtual string CleanUpScript + #region Base test class members + protected override string GetTypesExtractionPrepareScript(string tableName) { - get { return null; } + var dataTypes = Driver.ServerInfo.DataTypes; + var sb = new StringBuilder(); + _ = sb.Append($"CREATE TABLE {tableName} ("); + sb.AppendLine($"{TypeToColumnName[SqlType.Boolean]} [bit] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int16]} [smallint] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int32]} [int] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int64]} [bigint] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.UInt8]} [tinyint] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Decimal]} [decimal]({DecimalPrecision}, {DecimalScale}) NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Float]} [real] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Double]} [float] NULL,"); + + if (Driver.CoreServerInfo.ServerVersion.Major > 10) + sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} [datetime2] NULL,"); + else + sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} [datetime] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.DateTimeOffset]} [datetimeoffset] NULL,"); +#if NET6_0_OR_GREATER //DO_DATEONLY + sb.AppendLine($"{TypeToColumnName[SqlType.Date]} [date] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Time]} [time] NULL,"); +#endif + + sb.AppendLine($"{TypeToColumnName[SqlType.Char]} [nchar] ({CharLength}) COLLATE Cyrillic_General_CI_AS NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarChar]} [nvarchar]({VarCharLength}) COLLATE Cyrillic_General_CI_AS NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarCharMax]} [nvarchar](max) COLLATE Cyrillic_General_CI_AS NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Binary]} [binary]({BinaryLength}) NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarBinary]} [varbinary]({VarBinaryLength}) NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarBinaryMax]} [varbinary] (max) NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Guid]} [uniqueidentifier] NULL"); // = "guid_column"; + sb.AppendLine(");"); + + return sb.ToString(); } + protected override string GetTypesExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; - protected void ExecuteQuery(string sqlQuery) + protected override string GetForeignKeyExtractionPrepareScript() { - if (string.IsNullOrEmpty(sqlQuery)) - return; - ExecuteNonQuery(sqlQuery); + return "CREATE TABLE B1 (b_id int primary key);" + + "CREATE TABLE A1 (b_id int references B1(b_id));" + + "CREATE TABLE B2 (b_id_1 int, b_id_2 int, " + + " CONSTRAINT [B2_PK] PRIMARY KEY CLUSTERED (b_id_1, b_id_2) ON [PRIMARY]);" + + "CREATE TABLE A2 (b_id_1 int, b_id_2 int," + + " CONSTRAINT [A2_FK] FOREIGN KEY (b_id_1, b_id_2)" + + " REFERENCES B2 (b_id_1, b_id_2) ON DELETE CASCADE ON UPDATE NO ACTION);" + + "CREATE TABLE B3 (b_id_1 int, b_id_2 int, b_id_3 int," + + " CONSTRAINT [B3_PK] PRIMARY KEY CLUSTERED (b_id_1, b_id_2, b_id_3) ON [PRIMARY]);" + + "CREATE TABLE A3 (A_col1 int, b_id_3 int, b_id_1 int, b_id_2 int," + + " CONSTRAINT [A3_FK] FOREIGN KEY (b_id_1, b_id_2, b_id_3)" + + " REFERENCES B3 (b_id_1, b_id_2, b_id_3) ON DELETE NO ACTION ON UPDATE CASCADE);"; } - - protected override void TestFixtureSetUp() + protected override string GetForeignKeyExtractionCleanUpScript() => + "drop table A1" + + "\n drop table A2" + + "\n drop table A3" + + "\n drop table B1" + + "\n drop table B2" + + "\n drop table B3"; + + protected override string GetIndexExtractionPrepareScript(string tableName) { - base.TestFixtureSetUp(); - isTestsIgnored = false; + return + $"CREATE TABLE {tableName} (column1 int, column2 int);" + + $"\n CREATE INDEX {tableName}_index1_desc_asc on {tableName} (column1 desc, column2 asc);" + + $"\n CREATE UNIQUE INDEX {tableName}_index1_u_asc_desc on {tableName} (column1 asc, column2 desc);" + + $"\n CREATE UNIQUE INDEX {tableName}_index_with_included_columns on {tableName} (column1 asc) include (column2);"; } + protected override string GetIndexExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; - protected override void TestFixtureTearDown() + protected override string GetPartialIndexExtractionPrepareScript(string tableName) { - if (!isTestsIgnored) - ExecuteQuery(CleanUpScript); - base.TestFixtureTearDown(); + return + $"CREATE TABLE {tableName} (column1 int, column2 int);" + + $"\n CREATE INDEX {tableName}_index1_filtered on {tableName} (column1, column2) WHERE column1 > 10;"; } + protected override string GetPartialIndexExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; - protected override void CheckRequirements() + protected override string GetFulltextIndexExtractionPrepareScript(string tableName) { - Require.ProviderIs(StorageProvider.SqlServer); + return $"CREATE TABLE {tableName} (Id int NOT NULL," + + "\n Name nvarchar(100) NULL," + + "\n Comments nvarchar(1000) NULL," + + $"\n CONSTRAINT [PK_{tableName}] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]);" + + $"\n CREATE FULLTEXT INDEX ON {tableName}(Name LANGUAGE 1033, Comments LANGUAGE 1033)" + + $"\n KEY INDEX PK_{tableName} WITH CHANGE_TRACKING AUTO;"; } - } + protected override string GetFulltextIndexExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; - public class MSSQLExtractor_TestSchemaExtraction : MSSQLExtractorTestBase - { - public override string CleanUpScript + protected override string GetUniqueConstraintExtractionPrepareScript(string tableName) { - get - { - return - "\n drop table role1.table1" + - "\n drop table role2.table2" + - "\n drop table role3.table3" + - "\n drop table role3.table31" + - "\n exec sp_droprole role1" + - "\n exec sp_droprole role2" + - "\n exec sp_droprole role3"; - ; - } + return $"CREATE TABLE {tableName} (" + + "\n col_11 int, col_12 int, col_13 int," + + "\n col_21 int, col_22 int, col_23 int," + + "\n CONSTRAINT A_UNIQUE_1 UNIQUE(col_11,col_12,col_13), " + + "\n CONSTRAINT A_UNIQUE_2 UNIQUE(col_21,col_22,col_23));"; } + protected override string GetUniqueConstraintExtractionCleanUpScript(string tableName) => $"drop table {tableName}"; - [Test] - public virtual void Main() + protected override string GetCheckConstraintExtractionPrepareScript(string tableName) { - ExecuteQuery( - " exec sp_addrole 'role1'" + - "\n exec sp_addrole 'role2'" + - "\n exec sp_addrole 'role3'" + - "\n create table role1.table1(test int, test2 int, test3 int)" + - "\n create table role2.table2(test int, test2 int, test3 int)" + - "\n create table role3.table3(test int, test2 int, test3 int)" + - "\n create table role3.table31(test int, test2 int, test3 int)"); - - var model = ExtractCatalog(); - - // Validating. - Assert.IsNotNull(model.Schemas["role1"]); - Assert.IsNotNull(model.Schemas["role1"].Tables["table1"]); - Assert.IsNotNull(model.Schemas["role1"].Tables["table1"].TableColumns["test"]); - Assert.IsNotNull(model.Schemas["role1"].Tables["table1"].TableColumns["test2"]); - Assert.IsNotNull(model.Schemas["role1"].Tables["table1"].TableColumns["test3"]); - Assert.IsTrue(model.Schemas["role1"].Tables.Count==1); - - Assert.IsNotNull(model.Schemas["role2"]); - Assert.IsNotNull(model.Schemas["role2"].Tables["table2"]); - Assert.IsNotNull(model.Schemas["role2"].Tables["table2"].TableColumns["test"]); - Assert.IsNotNull(model.Schemas["role2"].Tables["table2"].TableColumns["test2"]); - Assert.IsNotNull(model.Schemas["role2"].Tables["table2"].TableColumns["test3"]); - Assert.IsTrue(model.Schemas["role2"].Tables.Count==1); - - Assert.IsNotNull(model.Schemas["role3"]); - Assert.IsNotNull(model.Schemas["role3"].Tables["table3"]); - Assert.IsNotNull(model.Schemas["role3"].Tables["table3"].TableColumns["test"]); - Assert.IsNotNull(model.Schemas["role3"].Tables["table3"].TableColumns["test2"]); - Assert.IsNotNull(model.Schemas["role3"].Tables["table3"].TableColumns["test3"]); - Assert.IsTrue(model.Schemas["role3"].Tables.Count==2); + return $"CREATE TABLE {tableName} (" + + "\n col_11 int, col_12 int, col_13 int," + + "\n col_21 int, col_22 int, col_23 int," + + "\n CONSTRAINT A_CHECK_1 CHECK(col_11 > 0 OR col_12 > 10 OR col_13 > 20), " + + "\n CONSTRAINT A_CHECK_2 CHECK(col_21 <0 AND col_22 < 10 AND col_23 < 20));"; } - } + protected override string GetCheckConstraintExtractionCleanUpScript(string tableName) => $"drop table {tableName}"; + #endregion - public class MSSQLExtractor_TestColumnTypeExtraction : MSSQLExtractorTestBase - { - private StringComparer comparer = StringComparer.InvariantCultureIgnoreCase; + #region Additional MS Sql Server specific tests - public override string CleanUpScript + protected virtual string GetMSSqlTypesExtractionPrepareScript(string tableName) { - get { return "drop table dataTypesTestTable"; } - } - - [Test] - public void Main() - { - string createTableQuery = - "CREATE TABLE dataTypesTestTable (" + + return $"CREATE TABLE {tableName} (" + "[int_l4] [int] NULL ," + "[binary_l50] [binary] (50) NULL ," + "[bit_l1] [bit] NULL , " + @@ -175,524 +159,99 @@ public void Main() "[uniqueidentifier_l16] [uniqueidentifier] NULL ," + "[varbinary_l150] [varbinary] (150) NULL ," + "[varchar_l50] [varchar] (50) COLLATE Cyrillic_General_CI_AS NULL)"; - ExecuteQuery(createTableQuery); - var model = ExtractCatalog(); - - Table table = model.DefaultSchema.Tables["dataTypesTestTable"]; - Assert.IsTrue(table.TableColumns["int_l4"].DataType.Type==SqlType.Int32); - Assert.IsTrue(table.TableColumns["binary_l50"].DataType.Length==50); - Assert.IsTrue(table.TableColumns["binary_l50"].DataType.Type==SqlType.Binary); - Assert.IsTrue(table.TableColumns["bit_l1"].DataType.Type==SqlType.Boolean); - Assert.IsTrue(table.TableColumns["char_10"].DataType.Length==5); - Assert.IsTrue(table.TableColumns["char_10"].DataType.Type==SqlType.Char); - Assert.IsTrue(table.TableColumns["datetime_l8"].DataType.Type==SqlType.DateTime); - Assert.IsTrue(table.TableColumns["decimal_p18_s0"].DataType.Type==SqlType.Decimal); - Assert.IsTrue(table.TableColumns["decimal_p18_s0"].DataType.Precision==18); - Assert.IsTrue(table.TableColumns["decimal_p18_s0"].DataType.Scale==0); - Assert.IsTrue(table.TableColumns["decimal_p12_s11_l9"].DataType.Type==SqlType.Decimal); - Assert.IsTrue(table.TableColumns["decimal_p12_s11_l9"].DataType.Precision==12); - Assert.IsTrue(table.TableColumns["decimal_p12_s11_l9"].DataType.Scale==11); - Assert.IsTrue(table.TableColumns["float_p53"].DataType.Type==SqlType.Double); - Assert.IsTrue(table.TableColumns["float_p53"].DataType.Precision==null); - Assert.IsTrue(table.TableColumns["float_p53"].DataType.Scale==null); - Assert.IsTrue(table.TableColumns["image_16"].DataType.Type==SqlType.VarBinaryMax); - Assert.IsTrue(comparer.Compare(table.TableColumns["money_p19_s4_l8"].DataType.TypeName, "money")==0); - Assert.IsTrue(table.TableColumns["money_p19_s4_l8"].DataType.Precision==19); - Assert.IsTrue(table.TableColumns["money_p19_s4_l8"].DataType.Scale==4); - Assert.IsTrue(table.TableColumns["nchar_l100"].DataType.Type==SqlType.Char); - Assert.IsTrue(table.TableColumns["nchar_l100"].DataType.Length==100); - Assert.IsTrue(table.TableColumns["ntext"].DataType.Type==SqlType.VarCharMax); - Assert.IsTrue(table.TableColumns["numeric_p5_s5"].DataType.Type==SqlType.Decimal); - Assert.IsTrue(table.TableColumns["numeric_p5_s5"].DataType.Precision==5); - Assert.IsTrue(table.TableColumns["nvarchar_l50"].DataType.Type==SqlType.VarChar); - Assert.IsTrue(table.TableColumns["nvarchar_l50"].DataType.Length==50); - Assert.IsTrue(table.TableColumns["real_p24_s0_l4"].DataType.Type==SqlType.Float); - Assert.IsTrue(table.TableColumns["real_p24_s0_l4"].DataType.Precision==null); - Assert.IsTrue(table.TableColumns["real_p24_s0_l4"].DataType.Scale==null); - Assert.IsTrue(table.TableColumns["smalldatetime_l4"].DataType.Type==SqlType.DateTime); - Assert.IsTrue(table.TableColumns["smallint_l2"].DataType.Type==SqlType.Int16); - Assert.IsTrue(comparer.Compare(table.TableColumns["small_money_p10_s4_l4"].DataType.TypeName, "smallmoney")==0); - Assert.IsTrue(comparer.Compare(table.TableColumns["sql_variant_"].DataType.TypeName, "sql_variant")==0); - Assert.IsTrue(table.TableColumns["text_16"].DataType.Type==SqlType.VarCharMax); - Assert.IsTrue(comparer.Compare(table.TableColumns["timestamp_l8"].DataType.TypeName, "timestamp")==0); - Assert.IsTrue(table.TableColumns["tinyint_1_p3_s0_l1"].DataType.Type==SqlType.UInt8); - Assert.IsTrue(table.TableColumns["uniqueidentifier_l16"].DataType.Type==SqlType.Guid); - Assert.IsTrue(table.TableColumns["varbinary_l150"].DataType.Type==SqlType.VarBinary); - Assert.IsTrue(table.TableColumns["varbinary_l150"].DataType.Length==150); - Assert.IsTrue(table.TableColumns["varchar_l50"].DataType.Type==SqlType.VarChar); - Assert.IsTrue(table.TableColumns["varchar_l50"].DataType.Length==25); - } - } - - public class MSSQLExtractor_TestExtractingViews : MSSQLExtractorTestBase - { - public override string CleanUpScript - { - get - { - return "\n drop table role1.table1" + - "\n drop view role1.view1" + - "\n drop view role1.view2" + - "\n exec sp_droprole role1"; - } - } - - [Test] - public virtual void Main() - { - ExecuteQuery( - " EXEC sp_addrole 'role1'" + - "\n CREATE TABLE role1.table1(column1 int, column2 int)"); - - ExecuteQuery( - "CREATE VIEW role1.view1 " + - "\n as Select column1 From role1.table1"); - - ExecuteQuery( - "CREATE VIEW role1.view2 " + - "\n as Select column1, column2 From role1.table1"); - - var model = ExtractCatalog(); - Schema schema = model.Schemas["role1"]; - - Assert.IsNotNull(schema); - Assert.IsNotNull(schema.Views["view1"]); - Assert.IsNotNull(schema.Views["view2"]); - Assert.IsNotNull(schema.Views["view1"].ViewColumns["column1"]); - Assert.IsNotNull(schema.Views["view2"].ViewColumns["column1"]); - Assert.IsNotNull(schema.Views["view2"].ViewColumns["column2"]); - } - } - - public class MSSQLExtractor_TestExtractingForeignKeys : MSSQLExtractorTestBase - { - public override string CleanUpScript - { - get - { - return - " drop table A1" + - "\n drop table A2" + - "\n drop table A3" + - "\n drop table B1" + - "\n drop table B2" + - "\n drop table B3"; - } } + protected virtual string GetMSSqlTypesExtractionCleanUpScrypt(string tableName) => $"drop table {tableName}"; [Test] - public void Main() + public void MSSqlTypesExtractionTest() { - string query = "\n create table B1 (b_id int primary key)" + - "\n create table A1 (b_id int references B1(b_id))" + - "\n create table B2 (" + - "\n b_id_1 int, " + - "\n b_id_2 int, " + - "\n CONSTRAINT [B2_PK] PRIMARY KEY CLUSTERED (b_id_1, b_id_2) ON [PRIMARY])" + - "\n create table A2 (" + - "\n b_id_1 int, " + - "\n b_id_2 int, " + - "\n constraint [A2_FK] FOREIGN KEY (b_id_1, b_id_2) " + - "\n REFERENCES B2 (b_id_1, b_id_2) ON DELETE CASCADE ON UPDATE NO ACTION)" + - "\n create table B3 (" + - "\n b_id_1 int," + - "\n b_id_2 int," + - "\n b_id_3 int," + - "\n CONSTRAINT [B3_PK] PRIMARY KEY CLUSTERED (b_id_1, b_id_2, b_id_3) ON [PRIMARY])" + - "\n create table A3 (" + - "\n A_col1 int," + - "\n b_id_3 int," + - "\n b_id_1 int," + - "\n b_id_2 int," + - "\n constraint [A3_FK] FOREIGN KEY (b_id_1, b_id_2, b_id_3) " + - "\n REFERENCES B3 (b_id_1, b_id_2, b_id_3) ON DELETE NO ACTION ON UPDATE CASCADE)"; - ExecuteQuery(query); - - - var model = ExtractCatalog(); - Schema schema = model.DefaultSchema; - - // Validating. - ForeignKey fk1 = (ForeignKey) schema.Tables["A1"].TableConstraints[0]; - Assert.IsNotNull(fk1); - Assert.IsTrue(fk1.Columns[0].Name=="b_id"); - Assert.IsTrue(fk1.ReferencedColumns[0].Name=="b_id"); - Assert.IsTrue(fk1.ReferencedColumns.Count==1); - Assert.IsTrue(fk1.Columns.Count==1); - - ForeignKey fk2 = (ForeignKey) schema.Tables["A2"].TableConstraints[0]; - Assert.IsNotNull(fk1); - Assert.IsTrue(fk2.Name=="A2_FK"); - Assert.IsTrue(fk2.Columns[0].Name=="b_id_1"); - Assert.IsTrue(fk2.ReferencedColumns[0].Name=="b_id_1"); - Assert.IsTrue(fk2.Columns[1].Name=="b_id_2"); - Assert.IsTrue(fk2.ReferencedColumns[1].Name=="b_id_2"); - Assert.IsTrue(fk2.ReferencedColumns.Count==2); - Assert.IsTrue(fk2.Columns.Count==2); - Assert.IsTrue(fk2.OnDelete==ReferentialAction.Cascade); - Assert.IsTrue(fk2.OnUpdate==ReferentialAction.NoAction); + var createTableQuery = GetMSSqlTypesExtractionPrepareScript("mssqlDttTable"); + RegisterCleanupScript(GetMSSqlTypesExtractionCleanUpScrypt, "mssqlDttTable"); - ForeignKey fk3 = (ForeignKey) schema.Tables["A3"].TableConstraints[0]; - Assert.IsNotNull(fk3); - Assert.IsTrue(fk3.Name=="A3_FK"); - Assert.IsTrue(fk3.Columns[0].Name=="b_id_1"); - Assert.IsTrue(fk3.ReferencedColumns[0].Name=="b_id_1"); - Assert.IsTrue(fk3.Columns[1].Name=="b_id_2"); - Assert.IsTrue(fk3.ReferencedColumns[1].Name=="b_id_2"); - Assert.IsTrue(fk3.Columns[2].Name=="b_id_3"); - Assert.IsTrue(fk3.ReferencedColumns[2].Name=="b_id_3"); - Assert.IsTrue(fk3.ReferencedColumns.Count==3); - Assert.IsTrue(fk3.Columns.Count==3); - Assert.IsTrue(fk3.OnDelete==ReferentialAction.NoAction); - Assert.IsTrue(fk3.OnUpdate==ReferentialAction.Cascade); - } - } - - public class MSSQLExtractor_TestExtractingUniqueConstraints : MSSQLExtractorTestBase - { - public override string CleanUpScript - { - get { return "drop table A"; } - } - - [Test] - public void Main() - { - ExecuteQuery( - " Create table A (" + - "\n col_11 int, col_12 int, col_13 int," + - "\n col_21 int, col_22 int, col_23 int," + - "\n CONSTRAINT A_UNIQUE_1 UNIQUE(col_11,col_12,col_13)," + - "\n CONSTRAINT A_UNIQUE_2 UNIQUE(col_21,col_22,col_23))"); - - var model = ExtractCatalog(); - Schema schema = model.DefaultSchema; - - // Validating. - UniqueConstraint A_UNIQUE_1 = (UniqueConstraint) schema.Tables["A"].TableConstraints["A_UNIQUE_1"]; - Assert.IsNotNull(A_UNIQUE_1); - Assert.IsTrue(A_UNIQUE_1.Columns[0].Name=="col_11"); - Assert.IsTrue(A_UNIQUE_1.Columns[1].Name=="col_12"); - Assert.IsTrue(A_UNIQUE_1.Columns[2].Name=="col_13"); - Assert.IsTrue(A_UNIQUE_1.Columns.Count==3); - - UniqueConstraint A_UNIQUE_2 = (UniqueConstraint) schema.Tables["A"].TableConstraints["A_UNIQUE_2"]; - Assert.IsNotNull(A_UNIQUE_2); - Assert.IsTrue(A_UNIQUE_2.Columns[0].Name=="col_21"); - Assert.IsTrue(A_UNIQUE_2.Columns[1].Name=="col_22"); - Assert.IsTrue(A_UNIQUE_2.Columns[2].Name=="col_23"); - Assert.IsTrue(A_UNIQUE_2.Columns.Count==3); - } - } - - public class MSSQLExtractor_TestIndexesExtracted : MSSQLExtractorTestBase - { - public override string CleanUpScript - { - get { return "drop table table1"; } - } - - [Test] - public virtual void Main() - { - ExecuteQuery( - " create table table1 (" + - "\n column1 int, " + - "\n column2 int) " + - "\n create index table1_index1_desc_asc on table1 (column1 desc, column2 asc)" + - "\n create unique index table1_index1_u_asc_desc on table1 (column1 asc, column2 desc)" + - "\n create unique index table1_index_with_included_columns on table1 (column1 asc)" + - "\n include (column2)"); - - var model = ExtractCatalog(); - Schema schema = model.DefaultSchema; - - Assert.IsTrue(schema.Tables["table1"]!=null); - Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index1_desc_asc"]); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns.Count==2); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Name=="column1"); - Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); - - Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"]); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns.Count==2); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[0].Ascending); - Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[1].Ascending); - - Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index_with_included_columns"]); - Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].Columns.Count, - "Key columns"); - Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].NonkeyColumns.Count, - "Included columns"); - } - } - - public class MSSQLExtractor2005_TestSchemaExtraction : MSSQLExtractor_TestSchemaExtraction - { - public override string CleanUpScript - { - get - { - return - "\n drop table schema1.table1" + - "\n drop table schema2.table2" + - "\n drop table schema3.table3" + - "\n drop table schema3.table31" + - "\n drop schema schema1" + - "\n drop schema schema2" + - "\n drop schema schema3"; - ; - } - } - - [Test] - public override void Main() - { - ExecuteQuery(" create schema schema1"); - ExecuteQuery(" create schema schema2"); - ExecuteQuery(" create schema schema3"); - string createTablesSql = - "\n create table schema1.table1(test int, test2 int, test3 int)" + - "\n create table schema2.table2(test int, test2 int, test3 int)" + - "\n create table schema3.table3(test int, test2 int, test3 int)" + - "\n create table schema3.table31(test int, test2 int, test3 int)"; - ExecuteQuery(createTablesSql); - - var model = ExtractCatalog(); - - // Validating. - Assert.IsNotNull(model.Schemas["schema1"]); - Assert.IsNotNull(model.Schemas["schema1"].Tables["table1"]); - Assert.IsNotNull(model.Schemas["schema1"].Tables["table1"].TableColumns["test"]); - Assert.IsNotNull(model.Schemas["schema1"].Tables["table1"].TableColumns["test2"]); - Assert.IsNotNull(model.Schemas["schema1"].Tables["table1"].TableColumns["test3"]); - Assert.IsTrue(model.Schemas["schema1"].Tables.Count==1); - - Assert.IsNotNull(model.Schemas["schema2"]); - Assert.IsNotNull(model.Schemas["schema2"].Tables["table2"]); - Assert.IsNotNull(model.Schemas["schema2"].Tables["table2"].TableColumns["test"]); - Assert.IsNotNull(model.Schemas["schema2"].Tables["table2"].TableColumns["test2"]); - Assert.IsNotNull(model.Schemas["schema2"].Tables["table2"].TableColumns["test3"]); - Assert.IsTrue(model.Schemas["schema2"].Tables.Count==1); + ExecuteQuery(createTableQuery); - Assert.IsNotNull(model.Schemas["schema3"]); - Assert.IsNotNull(model.Schemas["schema3"].Tables["table3"]); - Assert.IsNotNull(model.Schemas["schema3"].Tables["table3"].TableColumns["test"]); - Assert.IsNotNull(model.Schemas["schema3"].Tables["table3"].TableColumns["test2"]); - Assert.IsNotNull(model.Schemas["schema3"].Tables["table3"].TableColumns["test3"]); - Assert.IsTrue(model.Schemas["schema3"].Tables.Count==2); + var table = ExtractDefaultSchema().Tables["mssqlDttTable"]; + + var comparer = StringComparer.InvariantCultureIgnoreCase; + + Assert.IsTrue(table.TableColumns["int_l4"].DataType.Type == SqlType.Int32); + Assert.IsTrue(table.TableColumns["binary_l50"].DataType.Length == 50); + Assert.IsTrue(table.TableColumns["binary_l50"].DataType.Type == SqlType.Binary); + Assert.IsTrue(table.TableColumns["bit_l1"].DataType.Type == SqlType.Boolean); + Assert.IsTrue(table.TableColumns["char_10"].DataType.Length == 5); + Assert.IsTrue(table.TableColumns["char_10"].DataType.Type == SqlType.Char); + Assert.IsTrue(table.TableColumns["datetime_l8"].DataType.Type == SqlType.DateTime); + Assert.IsTrue(table.TableColumns["decimal_p18_s0"].DataType.Type == SqlType.Decimal); + Assert.IsTrue(table.TableColumns["decimal_p18_s0"].DataType.Precision == 18); + Assert.IsTrue(table.TableColumns["decimal_p18_s0"].DataType.Scale == 0); + Assert.IsTrue(table.TableColumns["decimal_p12_s11_l9"].DataType.Type == SqlType.Decimal); + Assert.IsTrue(table.TableColumns["decimal_p12_s11_l9"].DataType.Precision == 12); + Assert.IsTrue(table.TableColumns["decimal_p12_s11_l9"].DataType.Scale == 11); + Assert.IsTrue(table.TableColumns["float_p53"].DataType.Type == SqlType.Double); + Assert.IsTrue(table.TableColumns["float_p53"].DataType.Precision == null); + Assert.IsTrue(table.TableColumns["float_p53"].DataType.Scale == null); + Assert.IsTrue(table.TableColumns["image_16"].DataType.Type == SqlType.VarBinaryMax); + Assert.IsTrue(comparer.Compare(table.TableColumns["money_p19_s4_l8"].DataType.TypeName, "money") == 0); + Assert.IsTrue(table.TableColumns["money_p19_s4_l8"].DataType.Precision == 19); + Assert.IsTrue(table.TableColumns["money_p19_s4_l8"].DataType.Scale == 4); + Assert.IsTrue(table.TableColumns["nchar_l100"].DataType.Type == SqlType.Char); + Assert.IsTrue(table.TableColumns["nchar_l100"].DataType.Length == 100); + Assert.IsTrue(table.TableColumns["ntext"].DataType.Type == SqlType.VarCharMax); + Assert.IsTrue(table.TableColumns["numeric_p5_s5"].DataType.Type == SqlType.Decimal); + Assert.IsTrue(table.TableColumns["numeric_p5_s5"].DataType.Precision == 5); + Assert.IsTrue(table.TableColumns["nvarchar_l50"].DataType.Type == SqlType.VarChar); + Assert.IsTrue(table.TableColumns["nvarchar_l50"].DataType.Length == 50); + Assert.IsTrue(table.TableColumns["real_p24_s0_l4"].DataType.Type == SqlType.Float); + Assert.IsTrue(table.TableColumns["real_p24_s0_l4"].DataType.Precision == null); + Assert.IsTrue(table.TableColumns["real_p24_s0_l4"].DataType.Scale == null); + Assert.IsTrue(table.TableColumns["smalldatetime_l4"].DataType.Type == SqlType.DateTime); + Assert.IsTrue(table.TableColumns["smallint_l2"].DataType.Type == SqlType.Int16); + Assert.IsTrue(comparer.Compare(table.TableColumns["small_money_p10_s4_l4"].DataType.TypeName, "smallmoney") == 0); + Assert.IsTrue(comparer.Compare(table.TableColumns["sql_variant_"].DataType.TypeName, "sql_variant") == 0); + Assert.IsTrue(table.TableColumns["text_16"].DataType.Type == SqlType.VarCharMax); + Assert.IsTrue(comparer.Compare(table.TableColumns["timestamp_l8"].DataType.TypeName, "timestamp") == 0); + Assert.IsTrue(table.TableColumns["tinyint_1_p3_s0_l1"].DataType.Type == SqlType.UInt8); + Assert.IsTrue(table.TableColumns["uniqueidentifier_l16"].DataType.Type == SqlType.Guid); + Assert.IsTrue(table.TableColumns["varbinary_l150"].DataType.Type == SqlType.VarBinary); + Assert.IsTrue(table.TableColumns["varbinary_l150"].DataType.Length == 150); + Assert.IsTrue(table.TableColumns["varchar_l50"].DataType.Type == SqlType.VarChar); + Assert.IsTrue(table.TableColumns["varchar_l50"].DataType.Length == 25); } - } - public class MSSQLExtractor2005_TestColumnTypeExtraction : MSSQLExtractor_TestColumnTypeExtraction - { - /* - public override string CleanUpScript + protected virtual string GetMSSqlViewsExtractionTestPrepareStript() { - get { return base.CleanUpScript + "\n drop table dataTypesTestTable2"; } + return + " EXEC sp_addrole 'role1';" + + "\n CREATE TABLE role1.table1(column1 int, column2 int);" + + "\n CREATE VIEW role1.view1 as Select column1 From role1.table1;" + + "\n CREATE VIEW role1.view2 as Select column1, column2 From role1.table1;"; } - public void Main2() + protected virtual string GetMSSqlViewsExtractionTestCleanUpScript() { - ExecuteQuery( - "create table dataTypesTestTable2(" + - "\n xml_column xml," + - "\n varbinary_max varbinary(max)," + - "\n nvarchar_max nvarchar(max)," + - "\n varchar_max varchar(max))", ConnectionString); - Model model = ExtractCatalog(ConnectionString); - - Schema schema = model.DefaultServer.DefaultCatalog.DefaultSchema; - Assert.IsNotNull(schema.Tables["dataTypesTestTable2"]); - Assert.IsTrue(schema.Tables["dataTypesTestTable2"].TableColumns["varbinary_max"].DataType.DataType == SqlDataType.VarBinaryMax); - Assert.IsTrue(schema.Tables["dataTypesTestTable2"].TableColumns["nvarchar_max"].DataType.DataType == SqlDataType.VarCharMax); - Assert.IsTrue(schema.Tables["dataTypesTestTable2"].TableColumns["varchar_max"].DataType.DataType == SqlDataType.VarCharMax); - Assert.IsTrue(schema.Tables["dataTypesTestTable2"].TableColumns["xml_column"].DataType.DataType == SqlDataType.Xml); - } - */ - } - - public class MSSQLExtractor2005_TestExtractingViews : MSSQLExtractor_TestExtractingViews - { - } - - public class MSSQLExtractor2005_TestExtractingForeignKeys : MSSQLExtractor_TestExtractingForeignKeys - { - } - - public class MSSQLExtractor2005_TestExtractingForeignKeys2 : MSSQLExtractorTestBase - { - public override string CleanUpScript - { - get - { - return - "\n Drop Table A" + - "\n Drop Table B" + - "\n Drop Table B2"; - } + return "\n drop table role1.table1;" + + "\n drop view role1.view1;" + + "\n drop view role1.view2;" + + "\n exec sp_droprole role1"; } [Test] - public void Main() - { - ExecuteQuery( - " Create Table B (b_id int primary key)" + - "\n Create Table B2(b_id_1 int primary key)" + - "\n Create Table A(" + - "\n b_id int ," + - "\n b_id_1 int ," + - "\n Constraint [A_FK_1] Foreign key(b_id) references B(b_id) ON DELETE SET NULL ," + - "\n Constraint [A_FK_2] Foreign key(b_id_1) references B2(b_id_1) ON DELETE SET DEFAULT" + - "\n )"); - var model = ExtractCatalog(); - Schema schema = model.DefaultSchema; - Assert.IsTrue(((ForeignKey) schema.Tables["A"].TableConstraints["A_FK_1"]).OnDelete==ReferentialAction.SetNull); - Assert.IsTrue(((ForeignKey) schema.Tables["A"].TableConstraints["A_FK_2"]).OnDelete==ReferentialAction.SetDefault); - } - } - - public class MSSQLExtractor2005_TestExtractingUniqueConstraints : MSSQLExtractor_TestExtractingUniqueConstraints - { - } - - public class MSSQLExtractor2005_TestIndexesExtracted : MSSQLExtractor_TestIndexesExtracted - { - } - - public class MSSQLExtractor2005_TestPartitionsExtracted : MSSQLExtractorTestBase - { - public override string CleanUpScript - { - get - { - return "IF DB_ID (N'MSSQL2005Extr_PartitionsTest') IS NOT NULL" + - "\n DROP DATABASE MSSQL2005Extr_PartitionsTest;"; - } - } - - public void Main() + public virtual void MSSqlViewsExtractionTest() { - ExecuteQuery("USE master;"); - string createTestDatabaseSql = @"-- Get the SQL Server data path - DECLARE @data_path nvarchar(256); - SET @data_path = (SELECT SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1) - FROM master.sys.master_files - WHERE database_id = 1 AND file_id = 1); - - -- execute the CREATE DATABASE statement - EXECUTE ('CREATE DATABASE MSSQL2005Extr_PartitionsTest - ON PRIMARY - ( NAME = SPri1_dat, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_P.mdf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 15% ), - ( NAME = SPri2_dat, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_S.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 15% ), - FILEGROUP MSSQL2005Extr_PartitionsTest_FG1 - ( NAME = MSSQL2005Extr_PartitionsTest_D11, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_D11.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 5 ), - ( NAME = MSSQL2005Extr_PartitionsTest_D12, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_D12.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 5 ), - FILEGROUP MSSQL2005Extr_PartitionsTest_FG2 - ( NAME = MSSQL2005Extr_PartitionsTest_D21, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_D21.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 5 ), - ( NAME = MSSQL2005Extr_PartitionsTest_D22, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_D22.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 5 ), - FILEGROUP MSSQL2005Extr_PartitionsTest_FG3 - ( NAME = MSSQL2005Extr_PartitionsTest_D31, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_D31.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 5 ), - ( NAME = MSSQL2005Extr_PartitionsTest_D32, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_D32.ndf'', - SIZE = 10, - MAXSIZE = 50, - FILEGROWTH = 5 ) - LOG ON - ( NAME = Sales_log, - FILENAME = '''+ @data_path + 'MSSQL2005Extr_PartitionsTest_Log.ldf'', - SIZE = 5MB, - MAXSIZE = 25MB, - FILEGROWTH = 5MB )' - );"; - ExecuteQuery(createTestDatabaseSql); - ExecuteQuery("use MSSQL2005Extr_PartitionsTest"); - - // Create partition function. - ExecuteQuery( - "CREATE PARTITION FUNCTION MSSQL2005Extr_PartitionsTest_PFA_LEFT_1_20_30_40 (int)" + - "\n AS RANGE LEFT FOR VALUES (500);"); - - // Create partition scheme. - ExecuteQuery( - "CREATE PARTITION SCHEME MSSQL2005Extr_PartitionsTest_PFA_Schema" + - "\n AS PARTITION MSSQL2005Extr_PartitionsTest_PFA_LEFT_1_20_30_40" + - "\n TO ( " + - "\n MSSQL2005Extr_PartitionsTest_FG1, " + - "\n MSSQL2005Extr_PartitionsTest_FG2);"); - - // Create partitioned tables - ExecuteQuery( - "CREATE TABLE MSSQL2005Extr_PartitionsTest_Table (col1 int, col2 char(10))" + - "\n ON MSSQL2005Extr_PartitionsTest_PFA_Schema (col1)"); - - ExecuteQuery( - "CREATE TABLE MSSQL2005Extr_PartitionsTest_Table2 (col1 int, col2 int)" + - "ON MSSQL2005Extr_PartitionsTest_PFA_Schema (col2) ;"); - - var model = ExtractCatalog(); - Schema schema = model.DefaultSchema; - - Assert.IsNotNull(schema.Tables["MSSQL2005Extr_PartitionsTest_Table"].PartitionDescriptor); + var createViewsScrypt = GetMSSqlViewsExtractionTestPrepareStript(); + RegisterCleanupScript(GetMSSqlViewsExtractionTestCleanUpScript); - Assert.IsTrue( - schema.Tables["MSSQL2005Extr_PartitionsTest_Table"] - .PartitionDescriptor - .PartitionSchema.Name=="MSSQL2005Extr_PartitionsTest_PFA_Schema"); + ExecuteQuery(createViewsScrypt); - Assert.IsTrue( - schema.Tables["MSSQL2005Extr_PartitionsTest_Table"] - .PartitionDescriptor - .PartitionSchema - .PartitionFunction - .BoundaryType==BoundaryType.Left); + var schema = ExtractCatalog().Schemas["role1"]; - AssertUtility.AssertArraysAreEqual( - schema.Tables["MSSQL2005Extr_PartitionsTest_Table"] - .PartitionDescriptor - .PartitionSchema - .PartitionFunction - .BoundaryValues, - new string[] {"0", "500"}); - - AssertUtility.AssertCollectionsAreEqual( - schema.Tables["MSSQL2005Extr_PartitionsTest_Table"] - .PartitionDescriptor - .PartitionSchema - .Filegroups, - new string[] {"MSSQL2005Extr_PartitionsTest_FG1", "MSSQL2005Extr_PartitionsTest_FG2"}); - - Assert.IsTrue(schema.Tables["MSSQL2005Extr_PartitionsTest_Table"] - .PartitionDescriptor - .PartitionMethod==PartitionMethod.Range); + Assert.IsNotNull(schema); + Assert.IsNotNull(schema.Views["view1"]); + Assert.IsNotNull(schema.Views["view2"]); + Assert.IsNotNull(schema.Views["view1"].ViewColumns["column1"]); + Assert.IsNotNull(schema.Views["view2"].ViewColumns["column1"]); + Assert.IsNotNull(schema.Views["view2"].ViewColumns["column2"]); } + #endregion } } \ No newline at end of file From ba11277a1461248a34fd50367b101512a6716204 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 16 Jan 2023 20:51:28 +0500 Subject: [PATCH 18/86] Firebird: Fixed FK actions extraction --- .../Sql.Drivers.Firebird/v2_5/Extractor.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Extractor.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Extractor.cs index 1ab70927fd..9a6512aba6 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Extractor.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Extractor.cs @@ -490,7 +490,7 @@ private static void ReadForeignKeyColumnData(DbDataReader reader, ref ForeignKey state.ReferencingTable = state.ReferencingSchema.Tables[reader.GetString(1).Trim()]; state.ForeignKey = state.ReferencingTable.CreateForeignKey(reader.GetString(2).Trim()); ReadConstraintProperties(state.ForeignKey, reader, 3, 4); - ReadCascadeAction(state.ForeignKey, reader, 5); + ReadCascadeAction(state.ForeignKey, reader, 5, 12); state.ReferencedTable = state.ReferencedSchema.Tables[reader.GetString(9).Trim()]; state.ForeignKey.ReferencedTable = state.ReferencedTable; } @@ -618,25 +618,25 @@ private static void ReadConstraintProperties(Constraint constraint, constraint.IsInitiallyDeferred = ReadStringOrNull(row, isInitiallyDeferredIndex) == "YES"; } - private static void ReadCascadeAction(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex) + private static void ReadCascadeAction(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex, int updateRuleIndex) { var deleteRule = ReadStringOrNull(row, deleteRuleIndex); - switch (deleteRule) { - case "CASCADE": - foreignKey.OnDelete = ReferentialAction.Cascade; - return; - case "SET NULL": - foreignKey.OnDelete = ReferentialAction.SetNull; - return; - case "NO ACTION": - foreignKey.OnDelete = ReferentialAction.NoAction; - return; - case "RESTRICT": // behaves like NO ACTION - foreignKey.OnDelete = ReferentialAction.NoAction; - return; - case "SET DEFAULT": - foreignKey.OnDelete = ReferentialAction.SetDefault; - return; + foreignKey.OnDelete = GetRefAction(deleteRule); + + var updateRule = ReadStringOrNull(row, updateRuleIndex); + foreignKey.OnUpdate = GetRefAction(updateRule); + + + static ReferentialAction GetRefAction(in string rawActionName) + { + return rawActionName switch { + "CASCADE" => ReferentialAction.Cascade, + "SET NULL" => ReferentialAction.SetNull, + "NO ACTION" => ReferentialAction.NoAction, + "RESTRICT" => ReferentialAction.NoAction, + "SET DEFAULT" => ReferentialAction.SetDefault, + _ => throw new ArgumentOutOfRangeException() + }; } } From 736ba39abc593917c691201af20d3f4fdea038d9 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 16 Jan 2023 20:54:56 +0500 Subject: [PATCH 19/86] ExtractorTest for Firebird --- .../ExtractorTestBase.cs | 92 +++++++++++++------ .../Firebird/ExtractorTest.cs | 92 +++++++++++++++---- 2 files changed, 138 insertions(+), 46 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index 3dec314c94..35bfe147cc 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -37,6 +37,8 @@ public abstract class ExtractorTestBase : SqlTest protected bool NonKeyColumnsSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.NonKeyColumns); protected bool PartialIndexesSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.Filtered); protected bool FulltextIndexesSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.FullText); + protected bool SortOrderSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.SortOrder); + protected override void TestFixtureSetUp() { @@ -169,18 +171,17 @@ public void ForeignKeyExtractionTest() // Validating. var fk1 = (ForeignKey) schema.Tables["A1"].TableConstraints[0]; Assert.IsNotNull(fk1); - Assert.IsTrue(fk1.Columns[0].Name == "b_id"); - Assert.IsTrue(fk1.ReferencedColumns[0].Name == "b_id"); + Assert.IsTrue(fk1.Columns[0].Name.Equals("b_id", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk1.ReferencedColumns[0].Name.Equals("b_id", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(fk1.ReferencedColumns.Count == 1); Assert.IsTrue(fk1.Columns.Count == 1); var fk2 = (ForeignKey) schema.Tables["A2"].TableConstraints[0]; Assert.IsNotNull(fk1); - Assert.IsTrue(fk2.Name == "A2_FK"); - Assert.IsTrue(fk2.Columns[0].Name == "b_id_1"); - Assert.IsTrue(fk2.ReferencedColumns[0].Name == "b_id_1"); - Assert.IsTrue(fk2.Columns[1].Name == "b_id_2"); - Assert.IsTrue(fk2.ReferencedColumns[1].Name == "b_id_2"); + Assert.IsTrue(fk2.Columns[0].Name.Equals("b_id_1", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk2.ReferencedColumns[0].Name.Equals("b_id_1", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk2.Columns[1].Name.Equals("b_id_2", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk2.ReferencedColumns[1].Name.Equals("b_id_2", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(fk2.ReferencedColumns.Count == 2); Assert.IsTrue(fk2.Columns.Count == 2); Assert.IsTrue(fk2.OnDelete == ReferentialAction.Cascade); @@ -188,13 +189,12 @@ public void ForeignKeyExtractionTest() var fk3 = (ForeignKey) schema.Tables["A3"].TableConstraints[0]; Assert.IsNotNull(fk3); - Assert.IsTrue(fk3.Name == "A3_FK"); - Assert.IsTrue(fk3.Columns[0].Name == "b_id_1"); - Assert.IsTrue(fk3.ReferencedColumns[0].Name == "b_id_1"); - Assert.IsTrue(fk3.Columns[1].Name == "b_id_2"); - Assert.IsTrue(fk3.ReferencedColumns[1].Name == "b_id_2"); - Assert.IsTrue(fk3.Columns[2].Name == "b_id_3"); - Assert.IsTrue(fk3.ReferencedColumns[2].Name == "b_id_3"); + Assert.IsTrue(fk3.Columns[0].Name.Equals("b_id_1", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk3.ReferencedColumns[0].Name.Equals("b_id_1", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk3.Columns[1].Name.Equals("b_id_2", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk3.ReferencedColumns[1].Name.Equals("b_id_2", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk3.Columns[2].Name.Equals("b_id_3", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(fk3.ReferencedColumns[2].Name.Equals("b_id_3", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(fk3.ReferencedColumns.Count == 3); Assert.IsTrue(fk3.Columns.Count == 3); Assert.IsTrue(fk3.OnDelete == ReferentialAction.NoAction); @@ -224,19 +224,37 @@ public void IndexExtractionTest() Assert.IsTrue(schema.Tables["table1"] != null); Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index1_desc_asc"]); Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns.Count == 2); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Name == "column1"); - Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Name.Equals("column1", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Name.Equals("column2", StringComparison.OrdinalIgnoreCase)); + if (SortOrderSupported) { + Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); + } + else { + Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); + Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); + } + Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"]); Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns.Count == 2); - Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[0].Ascending); - Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[1].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[0].Name.Equals("column1", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[1].Name.Equals("column2", StringComparison.OrdinalIgnoreCase)); + + if (SortOrderSupported) { + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[0].Ascending); + Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[1].Ascending); + } + else { + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[0].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_u_asc_desc"].Columns[1].Ascending); + } - Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index_with_included_columns"]); - Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].Columns.Count, - "Key columns"); if (NonKeyColumnsSupported) { + Assert.IsNotNull(schema.Tables["table1"].Indexes["table1_index_with_included_columns"]); + Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].Columns.Count, + "Key columns"); + Assert.AreEqual(1, schema.Tables["table1"].Indexes["table1_index_with_included_columns"].NonkeyColumns.Count, "Included columns"); } @@ -319,16 +337,16 @@ public void UniqueConstraintExtractionTest() // Validating. var uniqueConstraint = (UniqueConstraint) schema.Tables["uniqueConstraintTable"].TableConstraints["A_UNIQUE_1"]; Assert.IsNotNull(uniqueConstraint); - Assert.IsTrue(uniqueConstraint.Columns[0].Name == "col_11"); - Assert.IsTrue(uniqueConstraint.Columns[1].Name == "col_12"); - Assert.IsTrue(uniqueConstraint.Columns[2].Name == "col_13"); + Assert.IsTrue(uniqueConstraint.Columns[0].Name.Equals("col_11", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(uniqueConstraint.Columns[1].Name.Equals("col_12", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(uniqueConstraint.Columns[2].Name.Equals("col_13", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(uniqueConstraint.Columns.Count == 3); uniqueConstraint = (UniqueConstraint) schema.Tables["uniqueConstraintTable"].TableConstraints["A_UNIQUE_2"]; Assert.IsNotNull(uniqueConstraint); - Assert.IsTrue(uniqueConstraint.Columns[0].Name == "col_21"); - Assert.IsTrue(uniqueConstraint.Columns[1].Name == "col_22"); - Assert.IsTrue(uniqueConstraint.Columns[2].Name == "col_23"); + Assert.IsTrue(uniqueConstraint.Columns[0].Name.Equals("col_21", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(uniqueConstraint.Columns[1].Name.Equals("col_22", StringComparison.OrdinalIgnoreCase)); + Assert.IsTrue(uniqueConstraint.Columns[2].Name.Equals("col_23", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(uniqueConstraint.Columns.Count == 3); } @@ -434,7 +452,23 @@ protected void ExecuteQuery(string sqlQuery) { if (string.IsNullOrEmpty(sqlQuery)) return; - ExecuteNonQuery(sqlQuery); + if(Driver.ServerInfo.Query.Features.HasFlag(QueryFeatures.DdlBatches)) { + _ = ExecuteNonQuery(sqlQuery); + } + else { + ExecuteQueryLineByLine(sqlQuery); + } + } + + protected void ExecuteQueryLineByLine(string sqlQuery) + { + if (string.IsNullOrEmpty(sqlQuery)) + return; + foreach (var q in sqlQuery.Split(';')) { + if (string.IsNullOrEmpty(q)) + continue; + _ = ExecuteNonQuery(q); + } } protected void RegisterCleanupScript(Func func) => cleanups.Add(func()); diff --git a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs index cd15f3e060..0120d6ab79 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs @@ -1,39 +1,97 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Csaba Beer // Created: 2011.01.13 using NUnit.Framework; using System; +using System.Text; +using Xtensive.Sql; namespace Xtensive.Orm.Tests.Sql.Firebird { - public class ExtractorTest : SqlTest + public class ExtractorTest : ExtractorTestBase { - [Test] - public void BaseTest() + protected override bool CheckContstraintExtracted => false; + + protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.Firebird); + + protected override string GetTypesExtractionPrepareScript(string tableName) { - var schema = ExtractDefaultSchema(); + var dataTypes = Driver.ServerInfo.DataTypes; + var sb = new StringBuilder(); + _ = sb.Append($"CREATE TABLE {tableName} ("); + sb.AppendLine($"{TypeToColumnName[SqlType.Int16]} smallint,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int32]} integer,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int64]} bigint,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Decimal]} decimal({DecimalPrecision}, {DecimalScale}),"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Float]} float,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Double]} double precision,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} timestamp,"); +#if NET6_0_OR_GREATER //DO_DATEONLY + sb.AppendLine($"{TypeToColumnName[SqlType.Date]} date,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Time]} time,"); +#endif + + sb.AppendLine($"{TypeToColumnName[SqlType.Char]} char({CharLength}),"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarChar]} varchar({VarCharLength}),"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarCharMax]} blob sub_type 1,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.VarBinaryMax]} blob sub_type 0"); + sb.AppendLine(")"); + + return sb.ToString(); } + protected override string GetTypesExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; - public override void SetUp() + protected override string GetForeignKeyExtractionPrepareScript() { - base.SetUp(); - // hack because Visual Nunit doesn't use TestFixtureSetUp attribute, just SetUp attribute - RealTestFixtureSetUp(); + return "CREATE TABLE B1 (b_id integer primary key);" + + "CREATE TABLE A1 (b_id integer references B1(b_id));" + + "CREATE TABLE B2 (b_id_1 integer, b_id_2 integer, " + + " CONSTRAINT B2_PK PRIMARY KEY (b_id_1, b_id_2));" + + "CREATE TABLE A2 (b_id_1 integer, b_id_2 integer," + + " CONSTRAINT A2_FK FOREIGN KEY (b_id_1, b_id_2)" + + " REFERENCES B2 (b_id_1, b_id_2) ON DELETE CASCADE ON UPDATE NO ACTION);" + + "CREATE TABLE B3 (b_id_1 integer, b_id_2 integer, b_id_3 integer," + + " CONSTRAINT B3_PK PRIMARY KEY(b_id_1, b_id_2, b_id_3));" + + "CREATE TABLE A3 (A_col1 integer, b_id_3 integer, b_id_1 integer, b_id_2 integer," + + " CONSTRAINT A3_FK FOREIGN KEY (b_id_1, b_id_2, b_id_3)" + + " REFERENCES B3 (b_id_1, b_id_2, b_id_3) ON DELETE NO ACTION ON UPDATE CASCADE);"; } - public override void TearDown() + protected override string GetForeignKeyExtractionCleanUpScript() => + "drop table A1;" + + "\n drop table A2;" + + "\n drop table A3;" + + "\n drop table B1;" + + "\n drop table B2;" + + "\n drop table B3;"; + + + protected override string GetIndexExtractionPrepareScript(string tableName) { - base.TearDown(); - // hack because Visual Nunit doesn't use TestFixtureTearDown attribute, just TearDown attribute - RealTestFixtureTearDown(); + return + $"CREATE TABLE {tableName} (column1 integer, column2 integer);" + + $"\n CREATE DESC INDEX {tableName}_index1_desc_asc on {tableName} (column1, column2);" + + $"\n CREATE UNIQUE ASC INDEX {tableName}_index1_u_asc_desc on {tableName} (column1, column2);"; } - protected override void CheckRequirements() + protected override string GetIndexExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; + + + protected override string GetUniqueConstraintExtractionPrepareScript(string tableName) { - Require.ProviderIs(StorageProvider.Firebird); + return $"CREATE TABLE {tableName} (" + + "\n col_11 integer, col_12 integer, col_13 integer," + + "\n col_21 integer, col_22 integer, col_23 integer," + + "\n CONSTRAINT A_UNIQUE_1 UNIQUE(col_11,col_12,col_13), " + + "\n CONSTRAINT A_UNIQUE_2 UNIQUE(col_21,col_22,col_23));"; } + + protected override string GetUniqueConstraintExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; } } From 46295a493c1bd60ef8f35ab8b24123e67a348927 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 16 Jan 2023 20:57:24 +0500 Subject: [PATCH 20/86] MSSQLExtractorTest: Use line by line script --- Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs index 0ac25a2a3e..3d7bb6e489 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs @@ -238,10 +238,10 @@ protected virtual string GetMSSqlViewsExtractionTestCleanUpScript() [Test] public virtual void MSSqlViewsExtractionTest() { - var createViewsScrypt = GetMSSqlViewsExtractionTestPrepareStript(); + var createViewsQuery = GetMSSqlViewsExtractionTestPrepareStript(); RegisterCleanupScript(GetMSSqlViewsExtractionTestCleanUpScript); - ExecuteQuery(createViewsScrypt); + ExecuteQueryLineByLine(createViewsQuery); var schema = ExtractCatalog().Schemas["role1"]; From b641322b5073e1b5b5040cfd573f8ebee1288b02 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 17 Jan 2023 20:29:44 +0500 Subject: [PATCH 21/86] ExtractorTest for MySQL --- .../ExtractorTestBase.cs | 24 +- .../Firebird/ExtractorTest.cs | 2 +- .../MySQL/ExtractorTest.cs | 289 +++++++----------- 3 files changed, 127 insertions(+), 188 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index 35bfe147cc..546eefc39e 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -16,7 +16,7 @@ namespace Xtensive.Orm.Tests.Sql [TestFixture] public abstract class ExtractorTestBase : SqlTest { - protected const int CharLength = 3; + protected const int CharLength = 4; protected const int VarCharLength = 6; protected const int BinaryLength = 3; @@ -54,17 +54,9 @@ protected override void TestFixtureSetUp() protected override void TestFixtureTearDown() { if (!IgnoreTests) { - if (Driver.ServerInfo.Query.Features.HasFlag(QueryFeatures.DdlBatches)) { - var cleanupScript = BuildCleanUpScrcipt(); - ExecuteQuery(cleanupScript); + foreach (var query in cleanups) { + ExecuteQueryLineByLine(query); } - else { - foreach (var query in cleanups) { - ExecuteQuery(query); - } - } - - ExecuteQuery(CleanUpScript); } base.TestFixtureTearDown(); @@ -82,11 +74,11 @@ string BuildCleanUpScrcipt() [Test] public void SchemaExtractionTest() { - Require.AllFeaturesSupported(Providers.ProviderFeatures.Multischema); + Assert.That(ExtractDefaultSchema(), Is.Not.Null); var catalog = ExtractCatalog(); - - Assert.That(schemasToCheck.All(s => catalog.Schemas[s] != null), Is.True); + if (StorageProviderInfo.Instance.CheckAllFeaturesNotSupported(Providers.ProviderFeatures.Multischema)) + Assert.That(schemasToCheck.All(s => catalog.Schemas[s] != null), Is.True); } @@ -231,8 +223,8 @@ public void IndexExtractionTest() Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); } else { - Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); - Assert.IsTrue(!schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[0].Ascending); + Assert.IsTrue(schema.Tables["table1"].Indexes["table1_index1_desc_asc"].Columns[1].Ascending); } diff --git a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs index 0120d6ab79..9024f5258c 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs @@ -76,7 +76,7 @@ protected override string GetIndexExtractionPrepareScript(string tableName) { return $"CREATE TABLE {tableName} (column1 integer, column2 integer);" + - $"\n CREATE DESC INDEX {tableName}_index1_desc_asc on {tableName} (column1, column2);" + + $"\n CREATE ASC INDEX {tableName}_index1_desc_asc on {tableName} (column1, column2);" + $"\n CREATE UNIQUE ASC INDEX {tableName}_index1_u_asc_desc on {tableName} (column1, column2);"; } diff --git a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs index c7a850c971..a6c8cef1ce 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs @@ -1,180 +1,127 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2023 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 // Created: 2011.02.25 -using System; -using System.Data.Common; -using NUnit.Framework; +using System.Text; using Xtensive.Sql; -using Xtensive.Sql.Model; - + namespace Xtensive.Orm.Tests.Sql.MySQL { - [TestFixture] - public class ExtractorTest : SqlTest + public class ExtractorTest : ExtractorTestBase + { + protected override bool CheckContstraintExtracted => false; + + protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.MySql); + + #region Base test class members + protected override string GetTypesExtractionPrepareScript(string tableName) + { + var dataTypes = Driver.ServerInfo.DataTypes; + var sb = new StringBuilder(); + _ = sb.Append($"CREATE TABLE {tableName} ("); + sb.AppendLine($"{TypeToColumnName[SqlType.Boolean]} boolean NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int8]} tinyint NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Int16]} smallint NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int32]} int NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int64]} bigint NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Decimal]} decimal({DecimalPrecision}, {DecimalScale}) NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Float]} float NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Double]} double precision NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} datetime NULL,"); +#if NET6_0_OR_GREATER //DO_DATEONLY + sb.AppendLine($"{TypeToColumnName[SqlType.Date]} date NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Time]} time NULL,"); +#endif + + sb.AppendLine($"{TypeToColumnName[SqlType.Char]} char({CharLength}) NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarChar]} varchar({VarCharLength}) NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarCharMax]} longtext NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Binary]} binary({BinaryLength}) NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarBinary]} varbinary({VarBinaryLength}) NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.VarBinaryMax]} longblob NULL"); + + sb.AppendLine(");"); + + return sb.ToString(); + } + protected override string GetTypesExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; + + protected override string GetForeignKeyExtractionPrepareScript() + { + return "CREATE TABLE B1 (b_id int primary key);" + + "\n CREATE TABLE A1 (b_id int, CONSTRAINT A1_FK FOREIGN KEY (b_id) REFERENCES B1 (b_id));" + + "\n CREATE TABLE B2 (b_id_1 int, b_id_2 int, CONSTRAINT B2_PK PRIMARY KEY (b_id_1, b_id_2)); " + + "\n CREATE TABLE A2 (b_id_1 int, b_id_2 int," + + " CONSTRAINT A2_FK FOREIGN KEY (b_id_1, b_id_2)" + + " REFERENCES B2 (b_id_1, b_id_2) ON DELETE CASCADE ON UPDATE NO ACTION); " + + "\n CREATE TABLE B3 (b_id_1 int, b_id_2 int, b_id_3 int," + + " CONSTRAINT B3_PK PRIMARY KEY (b_id_1, b_id_2, b_id_3)); " + + "\n CREATE TABLE A3 (A_col1 int, b_id_3 int, b_id_1 int, b_id_2 int," + + " CONSTRAINT A3_FK FOREIGN KEY (b_id_1, b_id_2, b_id_3)" + + " REFERENCES B3 (b_id_1, b_id_2, b_id_3) ON DELETE NO ACTION ON UPDATE CASCADE); "; + } + protected override string GetForeignKeyExtractionCleanUpScript() => + "drop table if exists A1;" + + "\n drop table if exists A2;" + + "\n drop table if exists A3;" + + "\n drop table if exists B1;" + + "\n drop table if exists B2;" + + "\n drop table if exists B3;"; + + protected override string GetIndexExtractionPrepareScript(string tableName) + { + return + $"CREATE TABLE {tableName} (column1 int, column2 int);" + + $"\n CREATE INDEX {tableName}_index1_desc_asc on {tableName} (column1 desc, column2 asc);" + + $"\n CREATE UNIQUE INDEX {tableName}_index1_u_asc_desc on {tableName} (column1 asc, column2 desc);"; + } + protected override string GetIndexExtractionCleanUpScript(string tableName) => $"drop table if exists {tableName};"; + + protected override string GetPartialIndexExtractionPrepareScript(string tableName) + { + return + $"CREATE TABLE {tableName} (column1 int, column2 int);" + + $"\n CREATE INDEX {tableName}_index1_filtered on {tableName} (column1, column2) WHERE column1 > 10;"; + } + protected override string GetPartialIndexExtractionCleanUpScript(string tableName) => $"drop table if exists {tableName};"; + + protected override string GetFulltextIndexExtractionPrepareScript(string tableName) + { + return $"CREATE TABLE {tableName} (Id int NOT NULL," + + "\n Name nvarchar(100) NULL," + + "\n Comments nvarchar(1000) NULL," + + $"\n CONSTRAINT [PK_{tableName}] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]);" + + $"\n CREATE FULLTEXT INDEX ON {tableName}(Name LANGUAGE 1033, Comments LANGUAGE 1033)" + + $"\n KEY INDEX PK_{tableName} WITH CHANGE_TRACKING AUTO;"; + } + protected override string GetFulltextIndexExtractionCleanUpScript(string tableName) => $"drop table if exists {tableName};"; + + protected override string GetUniqueConstraintExtractionPrepareScript(string tableName) { - #region Test DDL - - const string DropBadSetTableQuery = @"drop table if exists dataTypesBadSetTable"; - private const string CreateBadSetTableQuery = - @"CREATE TABLE dataTypesBadSetTable ( - set_162 SET('a', 'b', 'c', 'd') - )"; - - const string DropBadEnumTableQuery = @"drop table if exists dataTypesBadEnumTable"; - private const string CreateBadEnumTableQuery = - @"CREATE TABLE dataTypesBadEnumTable ( - num_231 ENUM('small', 'medium', 'large') - )"; - - const string DropBadBitTableQuery = @"drop table if exists dataTypesBadBitTable"; - private const string CreateBadBitTableQuery = - @"CREATE TABLE dataTypesBadBitTable ( - bit_l1 bit NULL - )"; - - const string DropGoodTableQuery = @"drop table if exists dataTypesGoodTable"; - const string CreateGoodTableQuery = - @"CREATE TABLE dataTypesGoodTable ( - int_l4 int NULL , - binary_l50 binary (50) NULL , - char_10 char (10) COLLATE utf8_unicode_ci NULL , - datetime_l8 datetime NULL , - decimal_p18_s0 decimal(18, 0) NULL , - decimal_p12_s11_l9 decimal(12, 11) NULL , - float_p53 float NULL , - image_16 blob NULL , - image_17 tinyblob NULL , - image_18 mediumblob NULL , - image_19 longblob NULL , - money_p19_s4_l8 decimal(18,2) NULL , - nchar_l100 nchar (100) COLLATE utf8_unicode_ci NULL , - tiny_text_01 tinytext COLLATE utf8_unicode_ci NULL , - long_text_01 longtext COLLATE utf8_unicode_ci NULL , - medium_text_01 mediumtext COLLATE utf8_unicode_ci NULL , - numeric_p5_s5 numeric(5, 5) NULL , - nvarchar_l50 nvarchar (50) COLLATE utf8_unicode_ci NULL , - real_p24_s0_l4 real NULL , - smalldatetime_l4 datetime NULL , - tinyint_l2 tinyint NULL , - smallint_l2 smallint NULL , - int_l2 int NULL , - mediumint_148 mediumint null, - big_int_120 bigint null, - small_money_p10_s4_l4 decimal(18,2) NULL , - text_16 varchar (50) COLLATE utf8_unicode_ci NULL , - timestamp_l8 timestamp NULL , - tinyint_1_p3_s0_l1 tinyint NULL , - varbinary_l150 varbinary (150) NULL , - varchar_l50 varchar (50) COLLATE utf8_unicode_ci NULL - )"; - - #endregion - - protected override void CheckRequirements() - { - Require.ProviderIs(StorageProvider.MySql); - } - - private void DropBadTables() - { - this.ExecuteNonQuery(DropBadSetTableQuery); - this.ExecuteNonQuery(DropBadEnumTableQuery); - this.ExecuteNonQuery(DropBadBitTableQuery); - } - - [Test] - public void DefaultSchemaIsAvailable() - { - this.DropBadTables(); - - var schema = this.ExtractDefaultSchema(); - Assert.IsNotNull(schema); - } - - [Test] - public void TestCatalogExtraction() - { - var catalog = ExtractCatalog(); - Assert.GreaterOrEqual(catalog.Schemas.Count, 1); - } - - [Test] - public void ExtractObjectsFromDefaultSchema() - { - this.DropBadTables(); - - var schema = this.ExtractDefaultSchema(); - var catalog = this.ExtractSchema(schema.Name); - Assert.IsNotNull(catalog); - } - - [Test] - public void TestForSupportedDataTypes() - { - this.DropBadTables(); - - ExecuteNonQuery(DropGoodTableQuery); - ExecuteNonQuery(CreateGoodTableQuery); - - var schema = ExtractDefaultSchema(); - var catalog = schema.Catalog; - - Table table = catalog.DefaultSchema.Tables["dataTypesGoodTable"]; - Assert.IsTrue(table.TableColumns["int_l4"].DataType.Type == SqlType.Int32); - } - - [Test] - //[ExpectedException(typeof(NotSupportedException))] - [Ignore("")] - public void TestForUnsupportedSETDatatypes() - { - this.DropBadTables(); - - ExecuteNonQuery(DropBadSetTableQuery); - ExecuteNonQuery(CreateBadSetTableQuery); - - var schema = ExtractDefaultSchema(); - var catalog = schema.Catalog; - - Table table = catalog.DefaultSchema.Tables["dataTypesBadSetTable"]; - Assert.IsNotNull(table); - } - - [Test] - //[ExpectedException(typeof(NotSupportedException))] - [Ignore("")] - public void TestForUnsupportedENUMDatatypes() - { - this.DropBadTables(); - - ExecuteNonQuery(DropBadEnumTableQuery); - ExecuteNonQuery(CreateBadEnumTableQuery); - - var schema = ExtractDefaultSchema(); - var catalog = schema.Catalog; - - Table table = catalog.DefaultSchema.Tables["dataTypesBadEnumTable"]; - Assert.IsNotNull(table); - } - - [Test] - //[ExpectedException(typeof(NotSupportedException))] - [Ignore("")] - public void TestForUnsupportedBITDatatypes() - { - this.DropBadTables(); - - ExecuteNonQuery(DropBadBitTableQuery); - ExecuteNonQuery(CreateBadBitTableQuery); - - var schema = ExtractDefaultSchema(); - var catalog = schema.Catalog; - - Table table = catalog.DefaultSchema.Tables["dataTypesBadBitTable"]; - Assert.IsNotNull(table); - } + return $"CREATE TABLE {tableName} (" + + "\n col_11 int, col_12 int, col_13 int," + + "\n col_21 int, col_22 int, col_23 int," + + "\n CONSTRAINT A_UNIQUE_1 UNIQUE(col_11,col_12,col_13), " + + "\n CONSTRAINT A_UNIQUE_2 UNIQUE(col_21,col_22,col_23));"; } + protected override string GetUniqueConstraintExtractionCleanUpScript(string tableName) => $"drop table if exists {tableName}"; + + // not supported yet + //protected override string GetCheckConstraintExtractionPrepareScript(string tableName) + //{ + // return $"CREATE TABLE {tableName} (" + + // "\n col_11 int, col_12 int, col_13 int," + + // "\n col_21 int, col_22 int, col_23 int," + + // "\n CONSTRAINT A_CHECK_1 CHECK(col_11 > 0 OR col_12 > 10 OR col_13 > 20), " + + // "\n CONSTRAINT A_CHECK_2 CHECK(col_21 <0 AND col_22 < 10 AND col_23 < 20));"; + //} + //protected override string GetCheckConstraintExtractionCleanUpScript(string tableName) => $"drop table {tableName}"; + #endregion + } } From dda5a0e8a8db7a31a0bb1eb62774ea4207045d96 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 18 Jan 2023 14:10:55 +0500 Subject: [PATCH 22/86] ExtractorTest for Sqlite --- .../ExtractorTestBase.cs | 10 +- .../Sqlite/ExtractorTest.cs | 116 ++++++++++++------ 2 files changed, 83 insertions(+), 43 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index 546eefc39e..a113cac56e 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -109,7 +109,7 @@ public void TypeExtractionTest() || sqlType == SqlType.Binary || sqlType == SqlType.VarBinary) { Assert.That(tableColumn.DataType.Length, Is.EqualTo(GetExpectedLength(sqlType))); } - if (sqlType == SqlType.Decimal) { + if (sqlType == SqlType.Decimal && StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.Sqlite)) { Assert.That(tableColumn.DataType.Precision, Is.EqualTo(DecimalPrecision)); Assert.That(tableColumn.DataType.Scale, Is.EqualTo(DecimalScale)); } @@ -257,7 +257,7 @@ public void IndexExtractionTest() protected virtual string GetPartialIndexExtractionCleanUpScript(string tableName) => null; [Test] - public void TestPartialIndexExtraction() + public void PartialIndexExtractionTest() { Require.AllFeaturesSupported(Providers.ProviderFeatures.PartialIndexes); @@ -308,8 +308,8 @@ public void FulltextIndexExtractionTest() } - protected abstract string GetUniqueConstraintExtractionPrepareScript(string tableName); - protected abstract string GetUniqueConstraintExtractionCleanUpScript(string tableName); + protected virtual string GetUniqueConstraintExtractionPrepareScript(string tableName) => ""; + protected virtual string GetUniqueConstraintExtractionCleanUpScript(string tableName) => ""; // Test expects storage variant of following structure // CREATE TABLE uniqueConstraintTable ( @@ -320,6 +320,8 @@ public void FulltextIndexExtractionTest() [Test] public void UniqueConstraintExtractionTest() { + Require.ProviderIsNot(StorageProvider.Sqlite); + var query = GetUniqueConstraintExtractionPrepareScript("uniqueConstraintTable"); RegisterCleanupScript(GetUniqueConstraintExtractionCleanUpScript, "uniqueConstraintTable"); ExecuteQuery(query); diff --git a/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs index 243788cbdd..d422b3fcd2 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs @@ -1,51 +1,89 @@ -using System; +// Copyright (C) 2011-2023 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 +// Created: 2011.03.16 + +using System; +using System.Text; using NUnit.Framework; -using Xtensive.Sql.Model; +using Xtensive.Sql; namespace Xtensive.Orm.Tests.Sql.Sqlite { - [TestFixture] - [Explicit] - public class ExtractorTest : SqlTest + public class ExtractorTest: ExtractorTestBase { - [Test] - public void BaseTest() + protected override bool CheckContstraintExtracted => false; + + protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.Sqlite); + + #region Base test class members + protected override string GetTypesExtractionPrepareScript(string tableName) + { + var dataTypes = Driver.ServerInfo.DataTypes; + var sb = new StringBuilder(); + _ = sb.Append($"CREATE TABLE {tableName} ("); + sb.AppendLine($"{TypeToColumnName[SqlType.Boolean]} [bit] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int16]} [smallint] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int32]} [integer] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Int64]} [bigint] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.UInt8]} [tinyint] NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Decimal]} [decimal]({DecimalPrecision}, {DecimalScale}) NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Float]} [real] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Double]} [float] NULL,"); + + + sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} [datetime] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.DateTimeOffset]} [datetimeoffset] NULL,"); +#if NET6_0_OR_GREATER //DO_DATEONLY + sb.AppendLine($"{TypeToColumnName[SqlType.Date]} [date] NULL,"); + sb.AppendLine($"{TypeToColumnName[SqlType.Time]} [time] NULL,"); +#endif + + sb.AppendLine($"{TypeToColumnName[SqlType.VarCharMax]} [nvarchar] NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.VarBinaryMax]} blob NULL,"); + + sb.AppendLine($"{TypeToColumnName[SqlType.Guid]} uniqueidentifier NULL"); + sb.AppendLine(");"); + + return sb.ToString(); + } + protected override string GetTypesExtractionCleanUpScript(string tableName) => $"drop table if exists {tableName};"; + + protected override string GetForeignKeyExtractionPrepareScript() { - var schema = ExtractDefaultSchema(); - foreach (var table in schema.Tables) { - Console.WriteLine("Table: " + table.Name); - Console.WriteLine("Columns"); - foreach (var column in table.TableColumns) - Console.WriteLine(string.Format(" {0}, {1}, {2}, {3}", column.Name, column.DataType, column.IsNullable, column.DefaultValue)); - Console.WriteLine("Indexes"); - foreach (var index in table.Indexes) { - Console.WriteLine(string.Format(" Name: {0}, Unique : {1}", index.Name, index.IsUnique)); - foreach (var column in index.Columns) - Console.WriteLine(" " + column.Name); - } - Console.WriteLine("Constraints"); - foreach (var constraint in table.TableConstraints) { - var uniqueConstraint = constraint as UniqueConstraint; - if (uniqueConstraint!=null) { - Console.WriteLine(string.Format(" {0}, Primary Key", constraint.Name)); - foreach (var column in uniqueConstraint.Columns) - Console.WriteLine(" " + column.Name); - continue; - } - var foreignKey = constraint as ForeignKey; - if (foreignKey!=null) { - Console.WriteLine(string.Format(" {0}, Foreign Key, {1}, On Update = {2}, On Delete = {3}", constraint.Name, foreignKey.ReferencedTable.Name, foreignKey.OnUpdate, foreignKey.OnDelete)); - foreach (var column in foreignKey.Columns) - Console.WriteLine(" " + column.Name); - continue; - } - } - } + return "CREATE TABLE B1 (b_id int primary key);" + + "CREATE TABLE A1 (b_id int, CONSTRAINT [A1_FK] FOREIGN KEY (b_id) REFERENCES B1(b_id));" + + "CREATE TABLE B2 (b_id_1 int, b_id_2 int, " + + " CONSTRAINT [B2_PK] PRIMARY KEY (b_id_1, b_id_2));" + + "CREATE TABLE A2 (b_id_1 int, b_id_2 int," + + " CONSTRAINT [A2_FK] FOREIGN KEY (b_id_1, b_id_2)" + + " REFERENCES B2 (b_id_1, b_id_2) ON DELETE CASCADE ON UPDATE NO ACTION);" + + "CREATE TABLE B3 (b_id_1 int, b_id_2 int, b_id_3 int," + + " CONSTRAINT [B3_PK] PRIMARY KEY (b_id_1, b_id_2, b_id_3));" + + "CREATE TABLE A3 (A_col1 int, b_id_3 int, b_id_1 int, b_id_2 int," + + " CONSTRAINT [A3_FK] FOREIGN KEY (b_id_1, b_id_2, b_id_3)" + + " REFERENCES B3 (b_id_1, b_id_2, b_id_3) ON DELETE NO ACTION ON UPDATE CASCADE);"; } + protected override string GetForeignKeyExtractionCleanUpScript() => + "drop table if exists A1;" + + "\n drop table if exists A2;" + + "\n drop table if exists A3;" + + "\n drop table if exists B1;" + + "\n drop table if exists B2;" + + "\n drop table if exists B3;"; - protected override void CheckRequirements() + protected override string GetIndexExtractionPrepareScript(string tableName) { - Require.ProviderIs(StorageProvider.Sqlite); + return + $"CREATE TABLE {tableName} (column1 int, column2 int);" + + $"\n CREATE INDEX {tableName}_index1_desc_asc on {tableName} (column1 desc, column2 asc);" + + $"\n CREATE UNIQUE INDEX {tableName}_index1_u_asc_desc on {tableName} (column1 asc, column2 desc);"; } + protected override string GetIndexExtractionCleanUpScript(string tableName) => $"drop table if exists {tableName};"; + #endregion } } \ No newline at end of file From 20e864101e9d0525b5ad3c026fd781cdc64859d6 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 20 Jan 2023 11:11:51 +0500 Subject: [PATCH 23/86] PgSql: extrator tests --- .../ExtractorTestBase.cs | 42 ++- .../PostgreSql/ExtractorTest.cs | 254 +++++++++++++----- 2 files changed, 233 insertions(+), 63 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index a113cac56e..68b8bf117d 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -30,8 +30,8 @@ public abstract class ExtractorTestBase : SqlTest protected readonly Dictionary TypeToColumnName = new(); - protected virtual string CleanUpScript => null; protected virtual bool CheckContstraintExtracted => true; + protected virtual bool SeqStartEqualsToMin => false; protected bool IgnoreTests { get; set; } = false; protected bool NonKeyColumnsSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.NonKeyColumns); @@ -92,7 +92,6 @@ public void TypeExtractionTest() { var createTableQuery = GetTypesExtractionPrepareScript("dataTypesTestTable"); RegisterCleanupScript(GetTypesExtractionCleanUpScript, "dataTypesTestTable"); - ExecuteQuery(createTableQuery); var testTable = ExtractDefaultSchema().Tables["dataTypesTestTable"]; @@ -376,6 +375,44 @@ public void CheckConstraintExtractionTest() Assert.IsNotNull(checkConstraint.Condition); } + + protected virtual string GetCheckSequenceExtractionPrepareScript() + { + return "CREATE SEQUENCE \"seq1\" START WITH 11 INCREMENT BY 100 MINVALUE 10 MAXVALUE 10000 NO CYCLE;" + + "CREATE SEQUENCE \"seq2\" START WITH 110 INCREMENT BY 10 MINVALUE 10 MAXVALUE 100000 CYCLE;"; + } + + protected virtual string GetCheckSequenceExtractionCleanupScript() => "DROP SEQUENCE \"seq1\"; DROP SEQUENCE \"seq2\""; + + [Test] + public void SequenceExtractionTest() + { + Require.AllFeaturesSupported(Providers.ProviderFeatures.Sequences); + + var query = GetCheckSequenceExtractionPrepareScript(); + RegisterCleanupScript(GetCheckSequenceExtractionCleanupScript); + ExecuteQuery(query); + + var schema = ExtractDefaultSchema(); + Assert.That(schema.Sequences["seq1"], Is.Not.Null); + var seq1Descriptor = schema.Sequences["seq1"].SequenceDescriptor; + Assert.That(seq1Descriptor.StartValue, Is.EqualTo(SeqStartEqualsToMin ? 10 : 11)); + Assert.That(seq1Descriptor.Increment, Is.EqualTo(100)); + Assert.That(seq1Descriptor.MinValue, Is.EqualTo(10)); + Assert.That(seq1Descriptor.MaxValue, Is.EqualTo(10000)); + + Assert.That(seq1Descriptor.IsCyclic, Is.False); + + Assert.That(schema.Sequences["seq2"], Is.Not.Null); + var seq2Descriptor = schema.Sequences["seq2"].SequenceDescriptor; + Assert.That(seq2Descriptor.StartValue, Is.EqualTo(SeqStartEqualsToMin ? 10 : 110)); + Assert.That(seq2Descriptor.Increment, Is.EqualTo(10)); + Assert.That(seq2Descriptor.MinValue, Is.EqualTo(10)); + Assert.That(seq2Descriptor.MaxValue, Is.EqualTo(100000)); + + Assert.That(seq2Descriptor.IsCyclic, Is.True); + } + private void PopulateTypeToColumnName() { TypeToColumnName[SqlType.Boolean] = "boolean_column"; @@ -392,6 +429,7 @@ private void PopulateTypeToColumnName() TypeToColumnName[SqlType.Float] = "float_column"; TypeToColumnName[SqlType.Double] = "double_column"; + TypeToColumnName[SqlType.Interval] = "interval_column"; TypeToColumnName[SqlType.DateTime] = "datetime_column"; TypeToColumnName[SqlType.DateTimeOffset] = "datetimeoffset_column"; #if NET6_0_OR_GREATER //DO_DATEONLY diff --git a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs index a00e1d2d5b..ef1a7ce21c 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs @@ -6,50 +6,215 @@ using System; using System.Diagnostics; +using System.Text; using NUnit.Framework; +using Xtensive.Sql; +using Xtensive.Sql.Dml; +using Xtensive.Sql.Drivers.PostgreSql.v8_0; using Xtensive.Sql.Model; namespace Xtensive.Orm.Tests.Sql.PostgreSql { - [TestFixture, Explicit] - public class ExtractorTest : SqlTest + public class ExtractorTest: ExtractorTestBase { - protected override void CheckRequirements() + protected override bool CheckContstraintExtracted => true; + protected override bool SeqStartEqualsToMin => true; + + protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.PostgreSql); + + protected override void PopulateCustomTypeToColumnName() + { + TypeToColumnName[CustomSqlType.Point] = "pgpoint_column"; + TypeToColumnName[CustomSqlType.LSeg] = "pglsegar_column"; + TypeToColumnName[CustomSqlType.Box] = "pgbox_column"; + TypeToColumnName[CustomSqlType.Path] = "pgpath_column"; + TypeToColumnName[CustomSqlType.Polygon] = "pgpolygon_column"; + TypeToColumnName[CustomSqlType.Circle] = "pgcirgcle_column"; + } + + #region Base test class members + protected override string GetTypesExtractionPrepareScript(string tableName) + { + var dataTypes = Driver.ServerInfo.DataTypes; + var sb = new StringBuilder(); + _ = sb.Append($"CREATE TABLE \"{tableName}\" ("); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Boolean]}\" boolean NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Int16]}\" smallint NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Int32]}\" integer NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Int64]}\" bigint NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Decimal]}\" decimal({DecimalPrecision}, {DecimalScale}) NULL,"); + + sb.AppendLine($"\"{TypeToColumnName[SqlType.Float]}\" real NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Double]}\" double precision NULL,"); + + sb.AppendLine($"\"{TypeToColumnName[SqlType.Interval]}\" interval NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTime]}\" timestamp NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTimeOffset]}\" timestamptz NULL,"); +#if NET6_0_OR_GREATER //DO_DATEONLY + sb.AppendLine($"\"{TypeToColumnName[SqlType.Date]}\" date NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Time]}\" time NULL,"); +#endif + + sb.AppendLine($"\"{TypeToColumnName[SqlType.Char]}\" char ({CharLength}) NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.VarChar]}\" varchar({VarCharLength}) NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.VarCharMax]}\" text NULL,"); + + sb.AppendLine($"\"{TypeToColumnName[SqlType.VarBinaryMax]}\" bytea NULL,"); + + sb.AppendLine($"\"{TypeToColumnName[CustomSqlType.Point]}\" point NULL,"); + sb.AppendLine($"\"{TypeToColumnName[CustomSqlType.LSeg]}\" lseg NULL,"); + sb.AppendLine($"\"{TypeToColumnName[CustomSqlType.Box]}\" box NULL,"); + sb.AppendLine($"\"{TypeToColumnName[CustomSqlType.Path]}\" path NULL,"); + sb.AppendLine($"\"{TypeToColumnName[CustomSqlType.Polygon]}\" polygon NULL,"); + sb.AppendLine($"\"{TypeToColumnName[CustomSqlType.Circle]}\" circle NULL"); + sb.AppendLine(");"); + + return sb.ToString(); + } + protected override string GetTypesExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; + + protected override string GetForeignKeyExtractionPrepareScript() + { + return "CREATE TABLE \"B1\" (\"b_id\" int primary key);" + + "CREATE TABLE \"A1\" (\"b_id\" int, CONSTRAINT \"A1_FK\" FOREIGN KEY (\"b_id\") REFERENCES \"B1\" (\"b_id\"));" + + "CREATE TABLE \"B2\" (\"b_id_1\" int, \"b_id_2\" int, " + + " CONSTRAINT \"B2_PK\" PRIMARY KEY (\"b_id_1\", \"b_id_2\"));" + + "CREATE TABLE \"A2\" (\"b_id_1\" int, \"b_id_2\" int," + + " CONSTRAINT \"A2_FK\" FOREIGN KEY (\"b_id_1\", \"b_id_2\")" + + " REFERENCES \"B2\" (\"b_id_1\", \"b_id_2\") ON DELETE CASCADE ON UPDATE NO ACTION);" + + "CREATE TABLE \"B3\" (\"b_id_1\" int, \"b_id_2\" int, \"b_id_3\" int," + + " CONSTRAINT \"B3_PK\" PRIMARY KEY (\"b_id_1\", \"b_id_2\", \"b_id_3\"));" + + "CREATE TABLE \"A3\" (\"A_col1\" int, \"b_id_3\" int, \"b_id_1\" int, \"b_id_2\" int," + + " CONSTRAINT \"A3_FK\" FOREIGN KEY (\"b_id_1\", \"b_id_2\", \"b_id_3\")" + + " REFERENCES \"B3\" (\"b_id_1\", \"b_id_2\", \"b_id_3\") ON DELETE NO ACTION ON UPDATE CASCADE);"; + } + protected override string GetForeignKeyExtractionCleanUpScript() => + "drop table \"A1\"" + + "\n drop table \"A2\"" + + "\n drop table \"A3\"" + + "\n drop table \"B1\"" + + "\n drop table \"B2\"" + + "\n drop table \"B3\""; + + protected override string GetIndexExtractionPrepareScript(string tableName) + { + return + $"CREATE TABLE \"{tableName}\" (\"column1\" int, \"column2\" int);" + + $"\n CREATE INDEX \"{tableName}_index1_desc_asc\" on \"{tableName}\" (\"column1\" desc, \"column2\" asc);" + + $"\n CREATE UNIQUE INDEX \"{tableName}_index1_u_asc_desc\" on \"{tableName}\" (\"column1\" asc, \"column2\" desc);"; + } + protected override string GetIndexExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; + + protected override string GetPartialIndexExtractionPrepareScript(string tableName) + { + return + $"CREATE TABLE \"{tableName}\" (\"column1\" int, \"column2\" int);" + + $"\n CREATE INDEX \"{tableName}_index1_filtered\" on \"{tableName}\" (\"column1\", \"column2\") WHERE \"column1\" > 10;"; + } + protected override string GetPartialIndexExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; + + protected override string GetFulltextIndexExtractionPrepareScript(string tableName) + { + return $"CREATE TABLE {tableName} (Id int NOT NULL," + + "\n Name nvarchar(100) NULL," + + "\n Comments nvarchar(1000) NULL," + + $"\n CONSTRAINT [PK_{tableName}] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]);" + + $"\n CREATE FULLTEXT INDEX ON {tableName}(Name LANGUAGE 1033, Comments LANGUAGE 1033)" + + $"\n KEY INDEX PK_{tableName} WITH CHANGE_TRACKING AUTO;"; + } + protected override string GetFulltextIndexExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; + + protected override string GetUniqueConstraintExtractionPrepareScript(string tableName) + { + return $"CREATE TABLE \"{tableName}\" (" + + "\n \"col_11\" int, \"col_12\" int, \"col_13\" int," + + "\n \"col_21\" int, \"col_22\" int, \"col_23\" int," + + "\n CONSTRAINT A_UNIQUE_1 UNIQUE(\"col_11\", \"col_12\", \"col_13\"), " + + "\n CONSTRAINT A_UNIQUE_2 UNIQUE(\"col_21\", \"col_22\", \"col_23\"));"; + } + protected override string GetUniqueConstraintExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; + + protected override string GetCheckConstraintExtractionPrepareScript(string tableName) { - Require.ProviderIs(StorageProvider.PostgreSql); + return $"CREATE TABLE \"{tableName}\" (" + + "\n \"col_11\" int, \"col_12\" int, \"col_13\" int," + + "\n \"col_21\" int, \"col_22\" int, \"col_23\" int," + + "\n CONSTRAINT \"A_CHECK_1\" CHECK(\"col_11\" > 0 OR \"col_12\" > 10 OR \"col_13\" > 20), " + + "\n CONSTRAINT \"A_CHECK_2\" CHECK(\"col_21\" < 0 AND \"col_22\" < 10 AND \"col_23\" < 20));"; + } + protected override string GetCheckConstraintExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; + #endregion + + #region Provider-related test cases + protected virtual string GetExtractDateTimeOffsetFieldsPrepareScript(string tableName) + { + return $"CREATE TABLE \"{tableName}\" (" + + "\"ID\" bigint PRIMARY KEY NOT NULL," + + "\"DateTimeOffset0\" timestamp(0) with time zone DEFAULT '0001-01-01 00:00:00+00:00'::timestamp(0) with time zone NOT NULL," + + "\"DateTimeOffset1\" timestamp(1) with time zone DEFAULT '0001-01-01 00:00:00.0+00:00'::timestamp(1) with time zone NOT NULL," + + "\"DateTimeOffset2\" timestamp(2) with time zone DEFAULT '0001-01-01 00:00:00.00+00:00'::timestamp(2) with time zone NOT NULL," + + "\"DateTimeOffset3\" timestamp(3) with time zone DEFAULT '0001-01-01 00:00:00.000+00:00'::timestamp(3) with time zone NOT NULL" + + ");"; + } + + protected virtual string GetExtractDateTimeOffsetFieldsCleanupScript(string tableName) + { + return $"DROP TABLE IF EXISTS \"{tableName}\""; } [Test] - public void FullTextIndexExtractorTest() + public void ExtractDateTimeOffsetFields() + { + var createTableQuery = GetExtractDateTimeOffsetFieldsPrepareScript("InteractionLog"); + RegisterCleanupScript(GetExtractDateTimeOffsetFieldsCleanupScript, "InteractionLog"); + + ExecuteQueryLineByLine(createTableQuery); + + var defaultSchema = ExtractDefaultSchema(); + + var testTable = defaultSchema.Tables["InteractionLog"]; + var tableColumn = testTable.TableColumns["DateTimeOffset0"]; + Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); + Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00+00:00'::timestamp(0) with time zone"))); + + tableColumn = testTable.TableColumns["DateTimeOffset1"]; + Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); + Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00.0+00:00'::timestamp(1) with time zone"))); + + tableColumn = testTable.TableColumns["DateTimeOffset2"]; + Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); + Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00.00+00:00'::timestamp(2) with time zone"))); + + tableColumn = testTable.TableColumns["DateTimeOffset3"]; + Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); + Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00.000+00:00'::timestamp(3) with time zone"))); + } + + + protected virtual string GetExpressionIndexExtractorPrepareScript(string tableName) { - var schema = Driver.ExtractDefaultSchema(Connection); + return $"CREATE TABLE \" {tableName} \"(col1 text, col2 text);" + + $"CREATE INDEX \"{tableName}_indx\" ON \"" + tableName + "\"(col1,col2,(col1||col2));"; + } + + protected virtual string GetExpressionIndexExtractorCleanupScript(string tableName) + { + return $"DROP TABLE IF EXISTS \"{tableName}\""; } [Test] public void ExpressionIndexExtractorTest() { - string guid = Guid.NewGuid().ToString(); - string tableName = "tbl" + guid; - string indexName = "ix" + guid; - string tableCreation = "CREATE TABLE \"" + tableName + "\"(col1 text, col2 text)"; - string indexCreation = "CREATE INDEX \"" + indexName + "\" ON \"" + tableName + "\"(col1,col2,(col1||col2))"; - - Schema schema = null; - try { - Connection.BeginTransaction(); - - using (var cmd = Connection.CreateCommand(tableCreation + ";" + indexCreation)) { - cmd.ExecuteNonQuery(); - } - schema = Driver.ExtractDefaultSchema(Connection); - } - finally { - Connection.Rollback(); - } - - var table = schema.Tables[tableName]; + var createTableQuery = GetExpressionIndexExtractorPrepareScript("tableWithIndx"); + RegisterCleanupScript(GetExpressionIndexExtractorCleanupScript, "tableWithIndx"); + + ExecuteQueryLineByLine(createTableQuery); + + var schema = ExtractDefaultSchema(); + + var table = schema.Tables["tableWithIndx"]; Assert.AreEqual(1, table.Indexes.Count); - var index = table.Indexes[indexName]; + var index = table.Indexes["tableWithIndx_indx"]; Assert.AreEqual(3, index.Columns.Count); Assert.AreSame(table.Columns[0], index.Columns[0].Column); Assert.AreSame(table.Columns[1], index.Columns[1].Column); @@ -58,39 +223,6 @@ public void ExpressionIndexExtractorTest() Assert.IsNull(index.Columns[2].Column); Assert.IsNotNull(index.Columns[2].Expression); } - - [Test] - public void ExtractDateTimeOffsetFields() - { - var dropTableScript = "DROP TABLE IF EXISTS \"InteractionLog\""; - var createTableScript = "CREATE TABLE \"InteractionLog\" (" + - "\"ID\" bigint PRIMARY KEY NOT NULL," + - "\"PacketId\" character varying(10485760)," + - "\"SenderId\" character varying(10485760)," + - "\"ReceiverId\" character varying(10485760)," + - "\"IsTest\" boolean DEFAULT false NOT NULL," + - "\"ResultCode\" integer," + - "\"ErrorMessage\" character varying(10485760)," + - "\"ErrorDescription\" character varying(10485760)," + - "\"Description\" character varying(10485760)," + - "\"Type\" integer DEFAULT 0 NOT NULL," + - "\"FormatId\" character varying(10485760)," + - "\"DateTimeOffset0\" timestamp(0) with time zone DEFAULT '0001-01-01 00:00:00+00:00'::timestamp(0) with time zone NOT NULL," + - "\"DateTimeOffset1\" timestamp(1) with time zone DEFAULT '0001-01-01 00:00:00.0+00:00'::timestamp(1) with time zone NOT NULL," + - "\"DateTimeOffset2\" timestamp(2) with time zone DEFAULT '0001-01-01 00:00:00.00+00:00'::timestamp(2) with time zone NOT NULL," + - "\"DateTimeOffset3\" timestamp(3) with time zone DEFAULT '0001-01-01 00:00:00.000+00:00'::timestamp(3) with time zone NOT NULL," + - "\"VersionID\" integer DEFAULT 0 NOT NULL" + - ");"; - - using (var command = Connection.CreateCommand()) { - command.CommandText = dropTableScript; - command.ExecuteNonQuery(); - - command.CommandText = createTableScript; - command.ExecuteNonQuery(); - } - - var catalog = Driver.ExtractCatalog(Connection); - } + #endregion } } \ No newline at end of file From e88dec1b85699c5dc7bbf73cad02b85ba0f3aa16 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 15:11:55 +0500 Subject: [PATCH 24/86] Oracle: Extractor test --- .../ExtractorTestBase.cs | 38 +++-- .../Oracle/ExtractorTest.cs | 139 +++++++++++++----- 2 files changed, 131 insertions(+), 46 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index 68b8bf117d..361aeab0bd 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Microsoft.VisualBasic; using NUnit.Framework; using Xtensive.Sql; using Xtensive.Sql.Info; @@ -55,7 +56,7 @@ protected override void TestFixtureTearDown() { if (!IgnoreTests) { foreach (var query in cleanups) { - ExecuteQueryLineByLine(query); + ExecuteQueryLineByLine(query, true); } } @@ -175,8 +176,14 @@ public void ForeignKeyExtractionTest() Assert.IsTrue(fk2.ReferencedColumns[1].Name.Equals("b_id_2", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(fk2.ReferencedColumns.Count == 2); Assert.IsTrue(fk2.Columns.Count == 2); - Assert.IsTrue(fk2.OnDelete == ReferentialAction.Cascade); - Assert.IsTrue(fk2.OnUpdate == ReferentialAction.NoAction); + if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.Oracle)) { + Assert.IsTrue(fk2.OnDelete == ReferentialAction.SetNull); + } + else { + Assert.IsTrue(fk2.OnDelete == ReferentialAction.Cascade); + Assert.IsTrue(fk2.OnUpdate == ReferentialAction.NoAction); + } + var fk3 = (ForeignKey) schema.Tables["A3"].TableConstraints[0]; Assert.IsNotNull(fk3); @@ -188,8 +195,13 @@ public void ForeignKeyExtractionTest() Assert.IsTrue(fk3.ReferencedColumns[2].Name.Equals("b_id_3", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(fk3.ReferencedColumns.Count == 3); Assert.IsTrue(fk3.Columns.Count == 3); - Assert.IsTrue(fk3.OnDelete == ReferentialAction.NoAction); - Assert.IsTrue(fk3.OnUpdate == ReferentialAction.Cascade); + if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.Oracle)) { + Assert.IsTrue(fk3.OnDelete == ReferentialAction.Cascade); + } + else { + Assert.IsTrue(fk3.OnDelete == ReferentialAction.NoAction); + Assert.IsTrue(fk3.OnUpdate == ReferentialAction.Cascade); + } } @@ -396,7 +408,8 @@ public void SequenceExtractionTest() var schema = ExtractDefaultSchema(); Assert.That(schema.Sequences["seq1"], Is.Not.Null); var seq1Descriptor = schema.Sequences["seq1"].SequenceDescriptor; - Assert.That(seq1Descriptor.StartValue, Is.EqualTo(SeqStartEqualsToMin ? 10 : 11)); + if (StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.Oracle)) + Assert.That(seq1Descriptor.StartValue, Is.EqualTo(SeqStartEqualsToMin ? 10 : 11)); Assert.That(seq1Descriptor.Increment, Is.EqualTo(100)); Assert.That(seq1Descriptor.MinValue, Is.EqualTo(10)); Assert.That(seq1Descriptor.MaxValue, Is.EqualTo(10000)); @@ -405,7 +418,8 @@ public void SequenceExtractionTest() Assert.That(schema.Sequences["seq2"], Is.Not.Null); var seq2Descriptor = schema.Sequences["seq2"].SequenceDescriptor; - Assert.That(seq2Descriptor.StartValue, Is.EqualTo(SeqStartEqualsToMin ? 10 : 110)); + if (StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.Oracle)) + Assert.That(seq2Descriptor.StartValue, Is.EqualTo(SeqStartEqualsToMin ? 10 : 110)); Assert.That(seq2Descriptor.Increment, Is.EqualTo(10)); Assert.That(seq2Descriptor.MinValue, Is.EqualTo(10)); Assert.That(seq2Descriptor.MaxValue, Is.EqualTo(100000)); @@ -492,14 +506,20 @@ protected void ExecuteQuery(string sqlQuery) } } - protected void ExecuteQueryLineByLine(string sqlQuery) + protected void ExecuteQueryLineByLine(string sqlQuery, bool ignoreExceptions = false) { if (string.IsNullOrEmpty(sqlQuery)) return; foreach (var q in sqlQuery.Split(';')) { if (string.IsNullOrEmpty(q)) continue; - _ = ExecuteNonQuery(q); + try { + _ = ExecuteNonQuery(q); + } + catch { + if (!ignoreExceptions) + throw; + } } } diff --git a/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs index 7fce21bdd4..241c3abc9e 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs @@ -1,60 +1,125 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2023 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.07.29 using System; +using System.Text; using NUnit.Framework; using Xtensive.Sql; using Xtensive.Sql.Model; namespace Xtensive.Orm.Tests.Sql.Oracle { - [TestFixture, Explicit] - public class ExtractorTest : SqlTest + public class ExtractorTest : ExtractorTestBase { - protected override void CheckRequirements() + protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.Oracle); + + #region Base test class members + protected override string GetTypesExtractionPrepareScript(string tableName) { - Require.ProviderIs(StorageProvider.Oracle); + var dataTypes = Driver.ServerInfo.DataTypes; + var sb = new StringBuilder(); + _ = sb.Append($"CREATE TABLE \"{tableName}\" ("); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Decimal]}\" number({DecimalPrecision}, {DecimalScale}) NULL,"); + + if (Driver.CoreServerInfo.ServerVersion.Major >= 10) { + sb.AppendLine($"\"{TypeToColumnName[SqlType.Float]}\" binary_float NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Double]}\" binary_double NULL,"); + } + else { + sb.AppendLine($"\"{TypeToColumnName[SqlType.Float]}\" real NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Double]}\" float NULL,"); + } + sb.AppendLine($"\"{TypeToColumnName[SqlType.Interval]}\" interval day to second NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTime]}\" timestamp NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTimeOffset]}\" TIMESTAMP WITH TIME ZONE NULL,"); +#if NET6_0_OR_GREATER //DO_DATEONLY + sb.AppendLine($"\"{TypeToColumnName[SqlType.Date]}\" date NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.Time]}\" interval day(0) to second NULL,"); +#endif + + sb.AppendLine($"\"{TypeToColumnName[SqlType.Char]}\" nchar ({CharLength}) NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.VarChar]}\" nvarchar2({VarCharLength}) NULL,"); + sb.AppendLine($"\"{TypeToColumnName[SqlType.VarCharMax]}\" nclob NULL,"); + + sb.AppendLine($"\"{TypeToColumnName[SqlType.VarBinaryMax]}\" blob NULL"); + sb.AppendLine(")"); + + return sb.ToString(); } + protected override string GetTypesExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; - [Test] - public void BaseTest() + protected override string GetForeignKeyExtractionPrepareScript() { - var schema = ExtractDefaultSchema(); + return "CREATE TABLE \"B1\" (\"b_id\" int primary key);" + + "CREATE TABLE \"B2\" (\"b_id_1\" int, \"b_id_2\" int, CONSTRAINT \"B2_PK\" PRIMARY KEY (\"b_id_1\", \"b_id_2\"));" + + "CREATE TABLE \"B3\" (\"b_id_1\" int, \"b_id_2\" int, \"b_id_3\" int," + + " CONSTRAINT \"B3_PK\" PRIMARY KEY (\"b_id_1\", \"b_id_2\", \"b_id_3\"));" + + "CREATE TABLE \"A1\" (\"b_id\" int, CONSTRAINT \"A1_FK\" FOREIGN KEY (\"b_id\") REFERENCES \"B1\" (\"b_id\"));" + + "CREATE TABLE \"A2\" (\"b_id_1\" int, \"b_id_2\" int," + + " CONSTRAINT \"A2_FK\" FOREIGN KEY (\"b_id_1\", \"b_id_2\")" + + " REFERENCES \"B2\" (\"b_id_1\", \"b_id_2\") ON DELETE SET NULL);" + + "CREATE TABLE \"A3\" (\"A_col1\" int, \"b_id_3\" int, \"b_id_1\" int, \"b_id_2\" int," + + " CONSTRAINT \"A3_FK\" FOREIGN KEY (\"b_id_1\", \"b_id_2\", \"b_id_3\")" + + " REFERENCES \"B3\" (\"b_id_1\", \"b_id_2\", \"b_id_3\") ON DELETE CASCADE);"; } + protected override string GetForeignKeyExtractionCleanUpScript() => + "drop table \"A1\";" + + "\n drop table \"A2\";" + + "\n drop table \"A3\";" + + "\n drop table \"B1\";" + + "\n drop table \"B2\";" + + "\n drop table \"B3\";"; - [Test] - public void TimeStampBasedTypes() + protected override string GetIndexExtractionPrepareScript(string tableName) { - var dropTable = "DROP TABLE TableWithTimeStamps PURGE"; - try { - var dropTableCommand = Connection.CreateCommand(dropTable); - dropTableCommand.ExecuteNonQuery(); - } - catch { + return + $"CREATE TABLE \"{tableName}\" (\"column1\" int, \"column2\" int);" + + $"\n CREATE INDEX \"{tableName}_index1_desc_asc\" on \"{tableName}\" (\"column1\" desc, \"column2\" asc);" + + $"\n CREATE UNIQUE INDEX \"{tableName}_index1_u_asc_desc\" on \"{tableName}\" (\"column1\" asc, \"column2\" desc);"; + } + protected override string GetIndexExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; - } + protected override string GetFulltextIndexExtractionPrepareScript(string tableName) + { + return $"CREATE TABLE {tableName} (Id int NOT NULL," + + "\n Name nvarchar(100) NULL," + + "\n Comments nvarchar(1000) NULL," + + $"\n CONSTRAINT [PK_{tableName}] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]);" + + $"\n CREATE FULLTEXT INDEX ON {tableName}(Name LANGUAGE 1033, Comments LANGUAGE 1033)" + + $"\n KEY INDEX PK_{tableName} WITH CHANGE_TRACKING AUTO;"; + } + protected override string GetFulltextIndexExtractionCleanUpScript(string tableName) => $"drop table {tableName};"; + + protected override string GetUniqueConstraintExtractionPrepareScript(string tableName) + { + return $"CREATE TABLE \"{tableName}\" (" + + "\n \"col_11\" int, \"col_12\" int, \"col_13\" int," + + "\n \"col_21\" int, \"col_22\" int, \"col_23\" int," + + "\n CONSTRAINT \"A_UNIQUE_1\" UNIQUE(\"col_11\", \"col_12\", \"col_13\"), " + + "\n CONSTRAINT \"A_UNIQUE_2\" UNIQUE(\"col_21\", \"col_22\", \"col_23\"));"; + } + protected override string GetUniqueConstraintExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\""; - var createTable = "CREATE TABLE TableWithTimeStamps" + - "( Id NUMBER (6)," + - ", DateTime TIMESTAMP" + - ", DateTimeOffset TIMESTAMP WITH TIME ZONE)"; - var command = Connection.CreateCommand(createTable); - command.ExecuteNonQuery(); - var schema = ExtractDefaultSchema(); - var table = schema.Tables["TableWithTimeStamps"]; - Assert.That(table, Is.Not.Null); - Assert.That(table.Columns.Count, Is.EqualTo(3)); - - var dateTimeColumn = table.TableColumns["DateTime"]; - Assert.That(dateTimeColumn, Is.Not.Null); - Assert.That(dateTimeColumn.DataType.Type, Is.EqualTo(SqlType.DateTime)); - - var dateTimeOffsetColumn = table.TableColumns["DateTimeOffset"]; - Assert.That(dateTimeOffsetColumn, Is.Not.Null); - Assert.That(dateTimeColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); + protected override string GetCheckConstraintExtractionPrepareScript(string tableName) + { + return $"CREATE TABLE \"{tableName}\" (" + + "\n \"col_11\" int, \"col_12\" int, \"col_13\" int," + + "\n \"col_21\" int, \"col_22\" int, \"col_23\" int," + + "\n CONSTRAINT \"A_CHECK_1\" CHECK(\"col_11\" > 0 OR \"col_12\" > 10 OR \"col_13\" > 20), " + + "\n CONSTRAINT \"A_CHECK_2\" CHECK(\"col_21\" <0 AND \"col_22\" < 10 AND \"col_23\" < 20));"; } + protected override string GetCheckConstraintExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\""; + + protected override string GetCheckSequenceExtractionPrepareScript() + { + return "CREATE SEQUENCE \"seq1\" START WITH 11 INCREMENT BY 100 MINVALUE 10 MAXVALUE 10000 NOCYCLE;" + + "CREATE SEQUENCE \"seq2\" START WITH 110 INCREMENT BY 10 MINVALUE 10 MAXVALUE 100000 CYCLE;"; + } + + protected override string GetCheckSequenceExtractionCleanupScript() => "DROP SEQUENCE \"seq1\"; DROP SEQUENCE \"seq2\""; + #endregion } } \ No newline at end of file From 6ae309bcfc4050f949aeddc10edcc35e68f14bee Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 15:13:19 +0500 Subject: [PATCH 25/86] Fix typo in class name --- Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs index 3d7bb6e489..8a2ac63af2 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs @@ -9,7 +9,7 @@ namespace Xtensive.Orm.Tests.Sql.SqlServer { - public class MSSQLExtratorTest : ExtractorTestBase + public class MSSQLExtractorTest : ExtractorTestBase { protected override bool CheckContstraintExtracted => false; From 5d1f69bc327d29be069218eaee4b8ddd7c1e65eb Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 19:14:39 +0500 Subject: [PATCH 26/86] PgSql: Full-text Index extraction text --- Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs | 4 +++- Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index 361aeab0bd..4fae7afc2c 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -310,11 +310,13 @@ public void FulltextIndexExtractionTest() var ftIndex = (FullTextIndex) schema.Tables["fullTextTestTable"].Indexes.FirstOrDefault(i => i.IsFullText); Assert.IsNotNull(ftIndex); Assert.That(ftIndex.Columns.Count, Is.EqualTo(2)); + Assert.That( + ftIndex.Columns.All(c => c.Languages.Count == 1 && c.Languages[0].Name.Equals("English", StringComparison.OrdinalIgnoreCase)), + Is.True); if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.SqlServer)) { Assert.That(ftIndex.FullTextCatalog, Is.Null); Assert.That(ftIndex.ChangeTrackingMode, Is.EqualTo(ChangeTrackingMode.Auto)); Assert.That(ftIndex.UnderlyingUniqueIndex, Is.EqualTo("PK_fullTextTestTable")); - Assert.That(ftIndex.Columns.All(c => c.Languages.Count == 1 && c.Languages[0].Name == "English"), Is.True); } } diff --git a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs index ef1a7ce21c..e662f81b24 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs @@ -115,12 +115,9 @@ protected override string GetPartialIndexExtractionPrepareScript(string tableNam protected override string GetFulltextIndexExtractionPrepareScript(string tableName) { - return $"CREATE TABLE {tableName} (Id int NOT NULL," + - "\n Name nvarchar(100) NULL," + - "\n Comments nvarchar(1000) NULL," + - $"\n CONSTRAINT [PK_{tableName}] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]);" + - $"\n CREATE FULLTEXT INDEX ON {tableName}(Name LANGUAGE 1033, Comments LANGUAGE 1033)" + - $"\n KEY INDEX PK_{tableName} WITH CHANGE_TRACKING AUTO;"; + return $"CREATE TABLE \"{tableName}\" (\"Id\" integer NOT NULL, \"Name\" varchar(100), \"Comments\" varchar(1000)," + + $" CONSTRAINT \"PK_{tableName}\" PRIMARY KEY (\"Id\"));" + + $"CREATE INDEX \"FT_{tableName}\" ON \"{tableName}\" USING gin ((to_tsvector('English'::regconfig, (\"Name\")::text) || to_tsvector('English'::regconfig, (\"Comments\")::text)))"; } protected override string GetFulltextIndexExtractionCleanUpScript(string tableName) => $"drop table \"{tableName}\";"; From b72f821417cda9697f49eda2580188705052421d Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 19:22:10 +0500 Subject: [PATCH 27/86] MySQL extraction imrovements - add extraction of date and time equivalents - extract both ON UPDATE and ON DELETE actions of Foreign Keys - ordinal comparer applied on string comparisons --- .../v5_0/Extractor.Queries.cs | 1 + .../Sql.Drivers.MySql/v5_0/Extractor.cs | 93 ++++++++++++------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.Queries.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.Queries.cs index 93568e894e..9e3c7e1906 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.Queries.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.Queries.cs @@ -120,6 +120,7 @@ protected string GetExtractForeignKeysQuery() r.table_name, r.constraint_name, r.delete_rule, + r.update_rule, c.column_name, c.ordinal_position, c.referenced_table_schema, 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 4a4a2aa5ae..db5b369da3 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 @@ -528,26 +528,27 @@ private static void ReadIndexColumnData(DbDataReader reader, ref IndexColumnRead // 1 table_name, // 2 constraint_name, // 3 delete_rule, - // 4 column_name, - // 5 ordinal_position, - // 6 referenced_table_schema, - // 7 referenced_table_name, - // 8 referenced_column_name + // 4 update_rule + // 5 column_name, + // 6 ordinal_position, + // 7 referenced_table_schema, + // 8 referenced_table_name, + // 9 referenced_column_name private static void ReadForeignKeyColumnData(DbDataReader reader, ref ForeignKeyReaderState state) { - var columnIndex = ReadInt(reader, 5); + var columnIndex = ReadInt(reader, 6); if (columnIndex <= state.LastColumnIndex) { var referencingSchema = state.Catalog.Schemas[reader.GetString(0)]; state.ReferencingTable = referencingSchema.Tables[reader.GetString(1)]; state.ForeignKey = state.ReferencingTable.CreateForeignKey(reader.GetString(2)); - ReadCascadeAction(state.ForeignKey, reader, 3); + ReadForeignKeyActions(state.ForeignKey, reader, 3, 4); var referencedSchema = state.Catalog.Schemas[reader.GetString(0)]; //Schema same as current - state.ReferencedTable = referencedSchema.Tables[reader.GetString(7)]; + state.ReferencedTable = referencedSchema.Tables[reader.GetString(8)]; state.ForeignKey.ReferencedTable = state.ReferencedTable; } - var referencingColumn = state.ReferencingTable.TableColumns[reader.GetString(4)]; - var referencedColumn = state.ReferencedTable.TableColumns[reader.GetString(8)]; + var referencingColumn = state.ReferencingTable.TableColumns[reader.GetString(5)]; + var referencedColumn = state.ReferencedTable.TableColumns[reader.GetString(9)]; state.ForeignKey.Columns.Add(referencingColumn); state.ForeignKey.ReferencedColumns.Add(referencedColumn); state.LastColumnIndex = columnIndex; @@ -617,69 +618,85 @@ private SqlValueType CreateValueType(IDataRecord row, return new SqlValueType(SqlType.Double); } - if (columnName == "TINYINT(1)") { + if (columnName.Equals("TINYINT(1)", StringComparison.Ordinal)) { return new SqlValueType(SqlType.Boolean); } - if (typeName.StartsWith("TINYINT")) { + if (typeName.StartsWith("TINYINT", StringComparison.Ordinal)) { // ignoring "day precision" and "second precision" // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Int8); } - if (typeName.StartsWith("SMALLINT")) { + if (typeName.StartsWith("SMALLINT", StringComparison.Ordinal)) { // ignoring "day precision" and "second precision" // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Int16); } - if (typeName.StartsWith("MEDIUMINT")) { + if (typeName.StartsWith("MEDIUMINT", StringComparison.Ordinal)) { // There is not 34bit Int in SqlType // ignoring "day precision" and "second precision" // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Int32); } - if (typeName.StartsWith("INT")) { + if (typeName.StartsWith("INT", StringComparison.Ordinal)) { // ignoring "day precision" and "second precision" // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Int32); } - if (typeName.StartsWith("BIGINT")) { + if (typeName.StartsWith("BIGINT", StringComparison.Ordinal)) { // ignoring "day precision" and "second precision" // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Int64); } - - if (typeName.StartsWith("TIME")) { +#if NET6_0_OR_GREATER //DO_DATEONLY + if (typeName.Equals("TIME", StringComparison.Ordinal)) { + + return new SqlValueType(SqlType.Time); + } + else if (typeName.StartsWith("TIME", StringComparison.Ordinal)) { // "timestamp precision" is saved as "scale", ignoring too return new SqlValueType(SqlType.DateTime); } - if (typeName.StartsWith("YEAR")) { +#else + if (typeName.StartsWith("TIME", StringComparison.Ordinal)) { + // "timestamp precision" is saved as "scale", ignoring too + return new SqlValueType(SqlType.DateTime); + } +#endif + if (typeName.StartsWith("YEAR", StringComparison.Ordinal)) { // "timestamp precision" is saved as "scale", ignoring too return new SqlValueType(SqlType.Decimal, 4, 0); } - if (typeName=="LONGTEXT") { + if (typeName.Equals("LONGTEXT", StringComparison.Ordinal)) { return new SqlValueType(SqlType.VarCharMax); } - if (typeName.Contains("TEXT")) { + if (typeName.Contains("TEXT", StringComparison.Ordinal)) { var length = ReadInt(row, charLengthIndex); return new SqlValueType(SqlType.VarCharMax, length); } - if (typeName.Contains("BLOB")) { + if (typeName.Contains("BLOB", StringComparison.Ordinal)) { return new SqlValueType(SqlType.VarBinaryMax); } - if (typeName == "VARBINARY") { + if (typeName.Equals("BINARY", StringComparison.Ordinal)) { + var length = ReadInt(row, charLengthIndex); + return new SqlValueType(SqlType.Binary, length); + } + if (typeName.Equals("VARBINARY", StringComparison.Ordinal)) { var length = ReadInt(row, charLengthIndex); + return new SqlValueType(SqlType.VarBinary, length); } - if (typeName == "VARCHAR" || typeName == "CHAR") { + if (typeName.Equals("VARCHAR", StringComparison.Ordinal) + || typeName.Equals("CHAR", StringComparison.Ordinal)) { var length = Convert.ToInt32(row[charLengthIndex]); var sqlType = typeName.Length==4 ? SqlType.Char : SqlType.VarChar; return new SqlValueType(sqlType, length); @@ -732,19 +749,25 @@ private static int ReadInt(IDataRecord row, int index) private static string ReadStringOrNull(IDataRecord row, int index) => row.IsDBNull(index) ? null : row.GetString(index); - private static void ReadCascadeAction(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex) + private static void ReadForeignKeyActions(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex, int updateRuleIndex) { var deleteRule = row.GetString(deleteRuleIndex); - switch (deleteRule) { - case "CASCADE": - foreignKey.OnDelete = ReferentialAction.Cascade; - return; - case "SET NULL": - foreignKey.OnDelete = ReferentialAction.SetNull; - return; - case "NO ACTION": - foreignKey.OnDelete = ReferentialAction.NoAction; - return; + foreignKey.OnDelete = GetEnumAction(deleteRule); + + var updateRule = row.GetString(updateRuleIndex); + foreignKey.OnUpdate = GetEnumAction(updateRule); + + + static ReferentialAction GetEnumAction(in string rawActionName) + { + return rawActionName switch { + "CASCADE" => ReferentialAction.Cascade, + "SET NULL" => ReferentialAction.SetNull, + "NO ACTION" => ReferentialAction.NoAction, + "RESTRICT" => ReferentialAction.NoAction, + "SET DEFAULT" => ReferentialAction.SetDefault, + _ => throw new ArgumentOutOfRangeException() + }; } } From 75071174f29219c760e9b10d07aff6e1d13f2831 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 19:25:43 +0500 Subject: [PATCH 28/86] Oracle schema extraction imrpovements - add support for time and date equivalent types - ordinal string comparison --- .../Sql.Drivers.Oracle/v09/Extractor.cs | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Extractor.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Extractor.cs index e456e72f30..3f2d77d819 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Extractor.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Extractor.cs @@ -101,8 +101,12 @@ public ExtractionContext(Catalog catalog, Dictionary replacement } } - private const int DefaultPrecision = 38; - private const int DefaultScale = 0; + private const int DefaultDecimalPrecision = 38; + private const int DefaultDecimalScale = 0; +#if NET6_0_OR_GREATER + private const int DefaultDayPrecision = 2; + private const int DefaultFSecondsPrecision = 6; +#endif private readonly object accessGuard = new object(); @@ -494,7 +498,7 @@ private static void ReadForeignKeyColumnData(DbDataReader reader, ref ForeignKey state.ReferencingTable = referencingSchema.Tables[reader.GetString(1)]; state.ForeignKey = state.ReferencingTable.CreateForeignKey(reader.GetString(2)); ReadConstraintProperties(state.ForeignKey, reader, 3, 4); - ReadCascadeAction(state.ForeignKey, reader, 5); + ReadForeignKeyDeleteAction(state.ForeignKey, reader, 5); var referencedSchema = state.Catalog.Schemas[reader.GetString(8)]; state.ReferencedTable = referencedSchema.Tables[reader.GetString(9)]; state.ForeignKey.ReferencedTable = state.ReferencedTable; @@ -563,25 +567,37 @@ private SqlValueType CreateValueType(IDataRecord row, int typeNameIndex, int precisionIndex, int scaleIndex, int charLengthIndex) { var typeName = row.GetString(typeNameIndex); - if (typeName == "NUMBER") { - var precision = row.IsDBNull(precisionIndex) ? DefaultPrecision : ReadInt(row, precisionIndex); - var scale = row.IsDBNull(scaleIndex) ? DefaultScale : ReadInt(row, scaleIndex); + if (string.Equals(typeName, "NUMBER", StringComparison.OrdinalIgnoreCase)) { + var precision = row.IsDBNull(precisionIndex) ? DefaultDecimalPrecision : ReadInt(row, precisionIndex); + var scale = row.IsDBNull(scaleIndex) ? DefaultDecimalScale : ReadInt(row, scaleIndex); return new SqlValueType(SqlType.Decimal, precision, scale); } - if (typeName.StartsWith("INTERVAL DAY")) { +#if NET6_0_OR_GREATER + if (typeName.StartsWith("INTERVAL DAY", StringComparison.OrdinalIgnoreCase)) { + var dayPrecision = row.IsDBNull(precisionIndex) ? DefaultDayPrecision : ReadInt(row, precisionIndex); + var fSecondsPrecision = row.IsDBNull(scaleIndex) ? DefaultFSecondsPrecision : ReadInt(row, scaleIndex); + + return (dayPrecision == 0) + ? new SqlValueType(SqlType.Time) + : new SqlValueType(SqlType.Interval); + } +#else + if (typeName.StartsWith("INTERVAL DAY", StringComparison.OrdinalIgnoreCase)) { // ignoring "day precision" and "second precision" // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Interval); } - if (typeName.StartsWith("TIMESTAMP")) { +#endif + if (typeName.StartsWith("TIMESTAMP", StringComparison.OrdinalIgnoreCase)) { // "timestamp precision" is saved as "scale", ignoring too - if (typeName.Contains("WITH TIME ZONE")) { + if (typeName.Contains("WITH TIME ZONE", StringComparison.OrdinalIgnoreCase)) { return new SqlValueType(SqlType.DateTimeOffset); } return new SqlValueType(SqlType.DateTime); } - if (typeName == "NVARCHAR2" || typeName == "NCHAR") { + if (string.Equals(typeName, "NVARCHAR2", StringComparison.OrdinalIgnoreCase) + || string.Equals(typeName, "NCHAR", StringComparison.OrdinalIgnoreCase)) { var length = ReadInt(row, charLengthIndex); var sqlType = typeName.Length == 5 ? SqlType.Char : SqlType.VarChar; return new SqlValueType(sqlType, length); @@ -665,7 +681,7 @@ private static void ReadConstraintProperties(Constraint constraint, constraint.IsInitiallyDeferred = row.GetString(isInitiallyDeferredIndex) == "DEFERRED"; } - private static void ReadCascadeAction(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex) + private static void ReadForeignKeyDeleteAction(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex) { var deleteRule = row.GetString(deleteRuleIndex); switch (deleteRule) { From 17d02f8249199306fc5d54deda611d2047c1945d Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 19:27:04 +0500 Subject: [PATCH 29/86] Sqlite : add time and date as data types --- .../Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs index 304c4abb37..523e03f644 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs @@ -229,6 +229,11 @@ public override DataTypeCollection GetDataTypesInfo() types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index, ValueRange.DateTimeOffset, "datetimeoffset"); +#if NET6_0_OR_GREATER + types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, ValueRange.DateOnly, "date"); + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, ValueRange.TimeOnly, "time"); +#endif + types.VarCharMax = DataTypeInfo.Regular(SqlType.VarCharMax, common | index, "varchar", "nvarchar", "nchar", "char", "text", "xml"); types.VarBinaryMax = DataTypeInfo.Regular(SqlType.VarBinaryMax, common, "blob"); From 03e5c63dc163474fa10d90730f07f632c7075ba4 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 21 Jan 2023 19:31:09 +0500 Subject: [PATCH 30/86] Sqlite schema extractor improvements - both foreign key actions are extracted - ordinal string comparison applied --- .../Sql.Drivers.Sqlite/v3/Extractor.cs | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Extractor.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Extractor.cs index 47f6591a38..3d7b0c1921 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Extractor.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Extractor.cs @@ -416,7 +416,7 @@ private static void ReadForeignKeyColumnData(DbDataReader reader, Table table, r state.ForeignKey = state.ReferencingTable.CreateForeignKey(foreignKeyName); - ReadCascadeAction(state.ForeignKey, reader, 6); + ReadForeignKeyActions(state.ForeignKey, reader, 6, 5); var referencedSchema = table.Schema; //Schema same as current var referencedTable = referencedSchema.Tables[ReadStringOrNull(reader, 2)]; state.ReferencedTable = referencedTable; @@ -444,25 +444,29 @@ private SqlValueType ParseValueType(string typeDefinition) // (rules are taken from sqlite docs) // (1) If the declared type contains the string "INT" then it is assigned INTEGER affinity. - if (typeName.Contains("int")) { + if (typeName.Contains("int", StringComparison.OrdinalIgnoreCase)) { return new SqlValueType(SqlType.Int64); } // (2) If the declared type of the column contains any of the strings "CHAR", "CLOB", or "TEXT" // then that column has TEXT affinity. - if (typeName.Contains("char") || typeName.Contains("clob") || typeName.Contains("text")) { + if (typeName.Contains("char", StringComparison.OrdinalIgnoreCase) + || typeName.Contains("clob", StringComparison.OrdinalIgnoreCase) + || typeName.Contains("text", StringComparison.OrdinalIgnoreCase)) { return new SqlValueType(SqlType.VarCharMax); } // (3) If the declared type for a column contains the string "BLOB" // or if no type is specified then the column has affinity NONE. - if (typeName.Contains("blob") || typeName==string.Empty) { + if (typeName.Contains("blob", StringComparison.OrdinalIgnoreCase) || typeName==string.Empty) { return new SqlValueType(SqlType.VarBinaryMax); } // (4) If the declared type for a column contains any of the strings // "REAL", "FLOA", or "DOUB" then the column has REAL affinity. - if (typeName.Contains("real") || typeName.Contains("floa") || typeName.Contains("doub")) { + if (typeName.Contains("real", StringComparison.OrdinalIgnoreCase) + || typeName.Contains("floa", StringComparison.OrdinalIgnoreCase) + || typeName.Contains("doub", StringComparison.OrdinalIgnoreCase)) { return new SqlValueType(SqlType.Double); } @@ -491,19 +495,25 @@ private static string ReadStringOrNull(IDataRecord row, int index) => private static int? ReadNullableInt(IDataRecord reader, string column) => Convert.IsDBNull(reader[column]) ? null : (int?) Convert.ToInt32(reader[column]); - private static void ReadCascadeAction(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex) + private static void ReadForeignKeyActions(ForeignKey foreignKey, IDataRecord row, int deleteRuleIndex, int updateRuleIndex) { var deleteRule = row.GetString(deleteRuleIndex); - switch (deleteRule) { - case "CASCADE": - foreignKey.OnDelete = ReferentialAction.Cascade; - return; - case "SET NULL": - foreignKey.OnDelete = ReferentialAction.SetNull; - return; - case "NO ACTION": - foreignKey.OnDelete = ReferentialAction.NoAction; - return; + foreignKey.OnDelete = GetEnumAction(deleteRule); + + var updateRule = row.GetString(updateRuleIndex); + foreignKey.OnUpdate = GetEnumAction(updateRule); + + + static ReferentialAction GetEnumAction(in string rawActionName) + { + return rawActionName switch { + "CASCADE" => ReferentialAction.Cascade, + "SET NULL" => ReferentialAction.SetNull, + "NO ACTION" => ReferentialAction.NoAction, + "RESTRICT" => ReferentialAction.NoAction, + "SET DEFAULT" => ReferentialAction.SetDefault, + _ => throw new ArgumentOutOfRangeException() + }; } } From 051336fa8cb613759d8b251f0d28c5f5d01f2590 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 23 Jan 2023 12:21:55 +0500 Subject: [PATCH 31/86] Fixed wrong name of type members' compiler --- .../Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index e812b348cb..8b7fe0736d 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -13,7 +13,7 @@ namespace Xtensive.Orm.Providers.Expressions.MemberCompilers #if NET6_0_OR_GREATER [CompilerContainer(typeof(SqlExpression))] - internal static class DateTimeCompilers + internal static class DateOnlyCompilers { #region Extractors From ada49d53974740cc0051b42f6c8e647c555efb67 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 23 Jan 2023 13:54:20 +0500 Subject: [PATCH 32/86] Raw tests for DateOnly/TimeOnly fields/values within Linq --- .../TestHelper.cs | 35 +++ .../DateTimeAndDateTimeOffset/BaseTest.cs | 27 ++- .../DateOnly/ComparisonTestDateOnly.cs | 50 +++++ .../DateOnly/DateOnlyToIsoStringTest.cs | 26 +++ .../DateOnly/DistinctTest.cs | 38 ++++ .../DateOnly/GroupByTest.cs | 40 ++++ .../DateOnly/JoinTest.cs | 70 ++++++ .../DateOnly/MinMaxTest.cs | 45 ++++ .../DateOnly/OperationsTest.cs | 52 +++++ .../DateOnly/OrderByTest.cs | 74 +++++++ .../DateOnly/PartsExtractionTest.cs | 85 ++++++++ .../DateOnly/WhereTest.cs | 54 +++++ .../DateTime/ComparisonTest.cs | 37 ++-- .../DateTime/DateTimeToIsoTest.cs | 12 +- .../DateTime/DistinctTest.cs | 36 ++-- .../DateTime/GroupByTest.cs | 28 ++- .../DateTime/JoinTest.cs | 33 ++- .../DateTime/MinMaxTest.cs | 20 +- .../DateTime/OperationsTest.cs | 31 --- .../DateTime/OrderByTest.cs | 47 ++-- .../DateTime/PartsExtractionTest.cs | 198 ++++++++--------- .../DateTime/WhereTest.cs | 43 ++-- .../DateTimeBaseTest.cs | 202 +++++++++++++----- .../Linq/DateTimeAndDateTimeOffset/Model.cs | 109 +++++++--- .../TimeOnly/ComparisonTestTimeOnly.cs | 77 +++++++ .../TimeOnly/DistinctTest.cs | 42 ++++ .../TimeOnly/GroupByTest.cs | 46 ++++ .../TimeOnly/JoinTest.cs | 81 +++++++ .../TimeOnly/MinMaxTest.cs | 51 +++++ .../TimeOnly/OperationsTest.cs | 94 ++++++++ .../TimeOnly/OrderByTest.cs | 83 +++++++ .../TimeOnly/PartsExtractionTest.cs | 90 ++++++++ .../TimeOnly/TimeOnlyToIsoString.cs | 26 +++ .../TimeOnly/WhereTest.cs | 64 ++++++ 34 files changed, 1677 insertions(+), 369 deletions(-) create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs diff --git a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs index c4bc8884bf..220d402fb2 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs @@ -82,5 +82,40 @@ public static DateTime FixDateTimeForProvider(this DateTime origin, StorageProvi var newTicks = ticks - (ticks % divider.Value); return new DateTime(newTicks); } + +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + /// Cuts down resolution of value if needed. + /// + /// The value to fix. + /// Type of provider. + /// New value with less resolution if requires it or untouched if the provider doesn't + public static TimeOnly FixTimeOnlyForProvider(this TimeOnly origin, StorageProviderInfo providerInfo) + { + long? divider; + var provider = providerInfo.Provider; + switch (provider) { + case StorageProvider.MySql: + divider = providerInfo.Info.StorageVersion < StorageProviderVersion.MySql56 ? 10000000 : 10; + break; + case StorageProvider.Firebird: + divider = 1000; + break; + case StorageProvider.PostgreSql: + divider = 10; + break; + default: + divider = null; + break; + } + + if (!divider.HasValue) { + return origin; + } + var ticks = origin.Ticks; + var newTicks = ticks - (ticks % divider.Value); + return new TimeOnly(newTicks); + } +#endif } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/BaseTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/BaseTest.cs index c041fcbd64..dbb8671b46 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/BaseTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/BaseTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2016 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2016-2023 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: 2016.09.15 @@ -36,6 +36,14 @@ protected void ExecuteInsideSession(Action action) } } + protected void ExecuteInsideSession(Action action) + { + using (var session = Domain.OpenSession()) + using (var transaction = session.OpenTransaction()) { + action(session); + } + } + protected override void PopulateData() { PopulateNonPersistentData(); @@ -64,10 +72,23 @@ protected void RunTest(Expression> filter, int rightCount = 1) Assert.AreEqual(rightCount, count); } + protected void RunTest(Session session, Expression> filter, int rightCount = 1) + where T : Entity + { + var count = session.Query.All().Count(filter); + Assert.AreEqual(rightCount, count); + } + protected void RunWrongTest(Expression> filter) where T : Entity { RunTest(filter, 0); } + + protected void RunWrongTest(Session session, Expression> filter) + where T : Entity + { + RunTest(session, filter, 0); + } } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs new file mode 100644 index 0000000000..a8f26954f1 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs @@ -0,0 +1,50 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + + public class ComparisonTestDateOnly : DateTimeBaseTest + { + [Test] + public void EqualsTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly == FirstDateOnly); + RunTest(s, c => c.NullableDateOnly == NullableDateOnly); + + RunWrongTest(s, c => c.DateOnly == WrongDateOnly); + RunWrongTest(s, c => c.NullableDateOnly == WrongDateOnly); + RunWrongTest(s, c => c.NullableDateOnly == null); + }); + } + + [Test] + public void NotEqualTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly != FirstDateOnly.AddYears(1)); + RunTest(s, c => c.NullableDateOnly != NullableDateOnly.AddYears(1)); + }); + } + + [Test] + public void CompareTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly > FirstDateOnly.AddDays(-1)); + RunTest(s, c => c.DateOnly < FirstDateOnly.AddDays(1)); + + RunWrongTest(s, c => c.DateOnly > FirstDateOnly); + RunWrongTest(s, c => c.DateOnly < FirstDateOnly); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs new file mode 100644 index 0000000000..3f1d8a31a6 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class DateOnlyToIsoStringTest : DateTimeBaseTest + { + [Test] + public void ToIsoStringTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.ToString("s") == FirstDateOnly.ToString("s")); + RunWrongTest(s, c => c.DateOnly.ToString("s") == FirstDateOnly.AddDays(1).ToString("s")); + }); + } + } +} +#endif diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs new file mode 100644 index 0000000000..1648faad35 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs @@ -0,0 +1,38 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class DistinctTest : DateTimeBaseTest + { + [Test] + public void DistinctByDateOnlyTest() => + ExecuteInsideSession(static (s) => DistinctPrivate(s, c => c.DateOnly)); + + [Test] + public void DistinctByNullableDateOnlyTest() => + ExecuteInsideSession(static (s) => DistinctPrivate(s, c => c.NullableDateOnly)); + + private static void DistinctPrivate(Session session, Expression> selectExpression) + where T : Entity + { + var compiledSelectExpression = selectExpression.Compile(); + var distinctLocal = session.Query.All().ToArray().Select(compiledSelectExpression).Distinct().OrderBy(c => c); + var distinctByServer = session.Query.All().Select(selectExpression).Distinct().OrderBy(c => c); + Assert.IsTrue(distinctLocal.SequenceEqual(distinctByServer)); + + distinctByServer = session.Query.All().Select(selectExpression).Distinct().OrderByDescending(c => c); + Assert.IsFalse(distinctLocal.SequenceEqual(distinctByServer)); + } + } +} +#endif diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs new file mode 100644 index 0000000000..b0ab715b8b --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class GroupByTest : DateTimeBaseTest + { + [Test] + public void DateOnlyGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.DateOnly, c => c.Id)); + + [Test] + public void NullableDateOnlyGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.NullableDateOnly, c => c.Id)); + + private void GroupByPrivate(Session session, Expression> groupByExpression, Expression> orderByExpression) + where T : Entity + { + var compiledGroupByExpression = groupByExpression.Compile(); + var compiledOrderByExpression = orderByExpression.Compile(); + var groupByLocal = session.Query.All().ToArray().GroupBy(compiledGroupByExpression).ToArray(); + var groupByServer = session.Query.All().GroupBy(groupByExpression); + foreach (var group in groupByServer) { + Assert.Contains(group, groupByLocal); + var localGroup = groupByLocal.Single(c => c.Key.Equals(group.Key)); + Assert.IsTrue(group.OrderBy(compiledOrderByExpression).SequenceEqual(localGroup.OrderBy(compiledOrderByExpression))); + } + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs new file mode 100644 index 0000000000..4696440319 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs @@ -0,0 +1,70 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class JoinTest : DateTimeBaseTest + { + [Test] + public void DateOnlyJoinTest() + { + ExecuteInsideSession((s) => JoinPrivate, DateOnly, long>(s, + left => left.DateOnly, + right => right.DateOnly, + (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateOnly, RightDateTime = right.DateOnly }, + c => c.LeftId, + c => c.RightId)); + } + + [Test] + public void NullableDateOnlyJoinTest() + { + ExecuteInsideSession((s) => JoinPrivate, DateOnly?, long>(s, + left => left.NullableDateOnly, + right => right.NullableDateOnly, + (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.NullableDateOnly, RightDateTime = right.NullableDateOnly }, + c => c.LeftId, + c => c.RightId)); + } + + private static void JoinPrivate(Session session, + Expression> leftJoinExpression, Expression> rightJoinExpression, + Expression> joinResultExpression, Expression> orderByExpression, Expression> thenByExpression) + where T1 : Entity + where T2 : Entity + { + var compiledLeftJoinExpression = leftJoinExpression.Compile(); + var compiledRightJoinExpression = rightJoinExpression.Compile(); + var compiledJoinResultExpression = joinResultExpression.Compile(); + var compiledOrderByExpression = orderByExpression.Compile(); + var compiledThenByExpression = thenByExpression.Compile(); + var joinLocal = session.Query.All().ToArray() + .Join(session.Query.All().ToArray(), compiledLeftJoinExpression, compiledRightJoinExpression, compiledJoinResultExpression) + .OrderBy(compiledOrderByExpression) + .ThenBy(compiledThenByExpression); + + var joinServer = session.Query.All() + .Join(session.Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) + .OrderBy(orderByExpression) + .ThenBy(thenByExpression); + + Assert.IsTrue(joinLocal.SequenceEqual(joinServer)); + + joinServer = session.Query.All() + .Join(session.Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) + .OrderByDescending(orderByExpression) + .ThenBy(thenByExpression); + Assert.IsFalse(joinLocal.SequenceEqual(joinServer)); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs new file mode 100644 index 0000000000..9ce9613ac9 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class MinMaxTest : DateTimeBaseTest + { + [Test] + public void DateOnlyMinMaxTest() + { + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.DateOnly)); + } + + [Test] + public void NullableDateOnlyMinMaxTest() + { + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.NullableDateOnly)); + } + + private static void MinMaxPrivate(Session session, Expression> selectExpression) + where T : Entity + { + var compiledSelectExpression = selectExpression.Compile(); + var minLocal = session.Query.All().ToArray().Min(compiledSelectExpression); + var maxLocal = session.Query.All().ToArray().Max(compiledSelectExpression); + var minServer = session.Query.All().Min(selectExpression); + var maxServer = session.Query.All().Max(selectExpression); + + Assert.AreEqual(minLocal, minServer); + Assert.AreEqual(maxLocal, maxServer); + Assert.AreNotEqual(minLocal, maxServer); + Assert.AreNotEqual(maxLocal, minServer); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs new file mode 100644 index 0000000000..58f3f52145 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs @@ -0,0 +1,52 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class OperationsTest : DateTimeBaseTest + { + [Test] + public void AddYearsTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(1)); + RunTest(s, c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(33)); + + RunWrongTest(s, c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(2)); + RunWrongTest(s, c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(44)); + }); + } + + [Test] + public void AddMonthsTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(1)); + RunTest(s, c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(33)); + + RunWrongTest(s, c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(2)); + RunWrongTest(s, c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(44)); + }); + } + + [Test] + public void AddDaysTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(1)); + RunTest(s, c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(33)); + + RunWrongTest(s, c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(2)); + RunWrongTest(s, c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(44)); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs new file mode 100644 index 0000000000..b567a9c6bb --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs @@ -0,0 +1,74 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class OrderByTest : DateTimeBaseTest + { + [Test] + public void DateOnlyOrderByTest() + { + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.DateOnly, c => c.Id); + OrderByPrivate(s, c => c.DateOnly, c => c); + }); + } + + [Test] + public void NullableDateOnlyOrderByTest() + { + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.NullableDateOnly, c => c.Id); + OrderByPrivate(s, c => c.NullableDateOnly, c => c); + }); + } + + private static void OrderByPrivate(Session session, Expression> orderByExpression, Expression> thenByExpression) + where T : Entity + { + var compiledOrderByExpression = orderByExpression.Compile(); + var compiledThenByExpression = thenByExpression.Compile(); + var notOrderedLocal = session.Query.All().ToArray(); + var orderedLocal = notOrderedLocal.OrderBy(compiledOrderByExpression).ThenBy(compiledThenByExpression); + var orderedLocalDescending = notOrderedLocal.OrderByDescending(compiledOrderByExpression).ThenBy(compiledThenByExpression); + var orderedByServer = session.Query.All().OrderBy(orderByExpression).ThenBy(thenByExpression); + var orderedByServerDescending = session.Query.All().OrderByDescending(orderByExpression).ThenBy(thenByExpression); + + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedLocal)); + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocalDescending.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocal.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocalDescending.SequenceEqual(orderedByServer)); + } + + protected static void OrderByPrivate(Session session, Expression> selectorExpression, Expression> orderByExpression) + where T1 : Entity + { + var compiledOrderByExpression = orderByExpression.Compile(); + + var notOrderedLocal = session.Query.All().Select(selectorExpression).ToArray(); + var orderedLocal = notOrderedLocal.OrderBy(compiledOrderByExpression); + var orderedLocalDescending = notOrderedLocal.OrderByDescending(compiledOrderByExpression); + var orderedByServer = session.Query.All().Select(selectorExpression).OrderBy(orderByExpression); + var orderedByServerDescending = session.Query.All().Select(selectorExpression).OrderByDescending(orderByExpression); + + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedLocal)); + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocalDescending.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocal.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocalDescending.SequenceEqual(orderedByServer)); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs new file mode 100644 index 0000000000..2a16963d09 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs @@ -0,0 +1,85 @@ +// Copyright (C) 2016-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: Alex Groznov +// Created: 2016.08.01 + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class PartsExtractionTest : DateTimeBaseTest + { + [Test] + public void ExtractYearTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.Year == FirstDateOnly.Year); + RunTest(s, c => c.NullableDateOnly.Value.Year == NullableDateOnly.Year); + + RunWrongTest(s, c => c.DateOnly.Year == WrongDateOnly.Year); + RunWrongTest(s, c => c.NullableDateOnly.Value.Year == WrongDateOnly.Year); + }); + } + + [Test] + public void ExtractMonthTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.Month == FirstDateOnly.Month); + RunTest(s, c => c.NullableDateOnly.Value.Month == NullableDateOnly.Month); + + RunWrongTest(s, c => c.DateOnly.Month == WrongDateOnly.Month); + RunWrongTest(s, c => c.NullableDateOnly.Value.Month == WrongDateOnly.Month); + + RunTest(s, c => c.DateOnly.Month == FirstDateOnly.Month); + RunTest(s, c => c.NullableDateOnly.Value.Month == NullableDateOnly.Month); + + RunWrongTest(s, c => c.DateOnly.Month == WrongDateOnly.Month); + RunWrongTest(s, c => c.NullableDateOnly.Value.Month == WrongDateOnly.Month); + + }); + } + + [Test] + public void ExtractDayTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.Day == FirstDateOnly.Day); + RunTest(s, c => c.NullableDateOnly.Value.Day == NullableDateOnly.Day); + + RunWrongTest(s, c => c.DateOnly.Day == WrongDateOnly.Day); + RunWrongTest(s, c => c.NullableDateOnly.Value.Day == WrongDateOnly.Day); + }); + } + + [Test] + public void ExtractDayOfYearTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.DayOfYear == FirstDateOnly.DayOfYear); + RunTest(s, c => c.NullableDateOnly.Value.DayOfYear == NullableDateOnly.DayOfYear); + + RunWrongTest(s, c => c.DateOnly.DayOfYear == WrongDateOnly.DayOfYear); + RunWrongTest(s, c => c.NullableDateOnly.Value.DayOfYear == WrongDateOnly.DayOfYear); + }); + } + + [Test] + public void ExtractDayOfWeekTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateOnly.DayOfWeek == FirstDateOnly.DayOfWeek); + RunTest(s, c => c.NullableDateOnly.Value.DayOfWeek == NullableDateOnly.DayOfWeek); + + RunWrongTest(s, c => c.DateOnly.DayOfWeek == WrongDateOnly.DayOfWeek); + RunWrongTest(s, c => c.NullableDateOnly.Value.DayOfWeek == WrongDateOnly.DayOfWeek); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs new file mode 100644 index 0000000000..96570cfc42 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs @@ -0,0 +1,54 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; +using Xtensive.Orm.Tests.ObjectModel.Interfaces.Alphabet; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class WhereTest : DateTimeBaseTest + { + [Test] + public void DateOnlyWhereTest() + { + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.DateOnly == FirstDateOnly, c => c.Id); + WherePrivate(s, c => c.DateOnly.Day == FirstDateOnly.Day, c => c.Id); + WherePrivate(s, c => c.DateOnly.Month == FirstDateOnly.Month, c => c.Id); + }); + } + + [Test] + public void NullableDateOnlyWhereTest() + { + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.NullableDateOnly == FirstDateOnly, c => c.Id); + WherePrivate(s, c => c.NullableDateOnly == null, c => c.Id); + WherePrivate(s, c => c.NullableDateOnly.HasValue && c.NullableDateOnly.Value.Day == FirstDateOnly.Day, c => c.Id); + WherePrivate(s, c => c.NullableDateOnly.HasValue && c.NullableDateOnly.Value.Month == FirstDateOnly.Month, c => c.Id); + }); + } + + private static void WherePrivate(Session session, Expression> whereExpression, Expression> orderByExpression) + where T : Entity + { + var compiledWhereExpression = whereExpression.Compile(); + var compiledOrderByExpression = orderByExpression.Compile(); + + var whereLocal = session.Query.All().ToArray().Where(compiledWhereExpression).OrderBy(compiledOrderByExpression); + var whereByServer = session.Query.All().Where(whereExpression).OrderBy(orderByExpression).ToList(); + Assert.IsTrue(whereLocal.SequenceEqual(whereByServer)); + + whereByServer = session.Query.All().Where(whereExpression).OrderByDescending(orderByExpression).ToList(); + Assert.IsFalse(whereLocal.SequenceEqual(whereByServer)); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs index 0f6440071b..73d7923676 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs @@ -1,10 +1,9 @@ -// Copyright (C) 2016-2022 Xtensive LLC. +// Copyright (C) 2016-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alex Groznov // Created: 2016.08.01 -using System; using NUnit.Framework; using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; @@ -15,7 +14,7 @@ public class ComparisonTest : DateTimeBaseTest [Test] public void EqualsTest() { - ExecuteInsideSession(() => { + ExecuteInsideSession((s) => { RunTest(c => c.DateTime==FirstDateTime); RunTest(c => c.MillisecondDateTime==FirstMillisecondDateTime); RunTest(c => c.NullableDateTime==NullableDateTime); @@ -24,23 +23,16 @@ public void EqualsTest() RunWrongTest(c => c.MillisecondDateTime==WrongMillisecondDateTime); RunWrongTest(c => c.NullableDateTime==WrongDateTime); RunWrongTest(c => c.NullableDateTime==null); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly == FirstDateOnly); - RunTest(c => c.NullableDateOnly == NullableDateOnly); -#endif }); } [Test] public void NotEqualTest() { - ExecuteInsideSession(() => { + ExecuteInsideSession((s) => { RunTest(c=>c.DateTime!=FirstDateTime.AddYears(1)); RunTest(c => c.MillisecondDateTime!=FirstMillisecondDateTime.AddYears(1)); RunTest(c=>c.NullableDateTime!=NullableDateTime.AddYears(1)); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly != FirstDateOnly.AddYears(1)); -#endif }); } @@ -48,21 +40,18 @@ public void NotEqualTest() public void CompareTest() { Require.ProviderIsNot(StorageProvider.MySql); - ExecuteInsideSession(() => { - RunTest(c => c.DateTime > FirstDateTime.Date); - RunTest(c => c.DateTime > FirstDateTime.AddSeconds(-1)); - RunTest(c => c.MillisecondDateTime > FirstMillisecondDateTime.AddMilliseconds(-1)); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime > FirstDateTime.Date); + RunTest(s, c => c.DateTime > FirstDateTime.AddSeconds(-1)); + RunTest(s, c => c.MillisecondDateTime > FirstMillisecondDateTime.AddMilliseconds(-1)); - RunTest(c => c.DateTime < FirstDateTime.Date.AddDays(1)); - RunTest(c => c.DateTime < FirstDateTime.AddSeconds(1)); - RunTest(c => c.MillisecondDateTime < FirstMillisecondDateTime.AddMilliseconds(1)); + RunTest(s, c => c.DateTime < FirstDateTime.Date.AddDays(1)); + RunTest(s, c => c.DateTime < FirstDateTime.AddSeconds(1)); + RunTest(s, c => c.MillisecondDateTime < FirstMillisecondDateTime.AddMilliseconds(1)); - RunWrongTest(c => c.DateTime > FirstDateTime); - RunWrongTest(c => c.MillisecondDateTime > FirstMillisecondDateTime); - RunWrongTest(c => c.MillisecondDateTime < FirstMillisecondDateTime.Date); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly > FirstDateOnly.AddDays(-1)); -#endif + RunWrongTest(s, c => c.DateTime > FirstDateTime); + RunWrongTest(s, c => c.MillisecondDateTime > FirstMillisecondDateTime); + RunWrongTest(s, c => c.MillisecondDateTime < FirstMillisecondDateTime.Date); }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs index 4e9139e9b7..48370ebfa1 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2016 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Groznov // Created: 2016.08.01 @@ -14,9 +14,9 @@ public class DateTimeToIsoTest : DateTimeBaseTest [Test] public void ToIsoStringTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.ToString("s")==FirstDateTime.ToString("s")); - RunWrongTest(c => c.DateTime.ToString("s")==FirstDateTime.AddMinutes(1).ToString("s")); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.ToString("s")==FirstDateTime.ToString("s")); + RunWrongTest(s, c => c.DateTime.ToString("s")==FirstDateTime.AddMinutes(1).ToString("s")); }); } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs index 40ed6eeb60..b4e4e6607e 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DistinctTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2016 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Groznov // Created: 2016.08.01 @@ -15,36 +15,26 @@ namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateTimes public class DistinctTest : DateTimeBaseTest { [Test] - public void DistinctByDateTimeTest() - { - ExecuteInsideSession(() => DistinctPrivate(c => c.DateTime)); -#if NET6_0_OR_GREATER //DO_DATEONLY - ExecuteInsideSession(() => DistinctPrivate(c => c.DateOnly)); - ExecuteInsideSession(() => DistinctPrivate(c => c.TimeOnly)); -#endif - } + public void DistinctByDateTimeTest() => + ExecuteInsideSession((s) => DistinctPrivate(s,c => c.DateTime)); [Test] - public void DistinctByDateTimeWithMillisecondsTest() - { - ExecuteInsideSession(() => DistinctPrivate(c => c.DateTime)); - } + public void DistinctByDateTimeWithMillisecondsTest() => + ExecuteInsideSession((s) => DistinctPrivate(s, c => c.DateTime)); [Test] - public void DistinctByNullableDateTimeTest() - { - ExecuteInsideSession(() => DistinctPrivate(c => c.DateTime)); - } + public void DistinctByNullableDateTimeTest() => + ExecuteInsideSession((s) => DistinctPrivate(s, c => c.DateTime)); - private void DistinctPrivate(Expression> selectExpression) + private static void DistinctPrivate(Session session, Expression> selectExpression) where T : Entity { var compiledSelectExpression = selectExpression.Compile(); - var distinctLocal = Query.All().ToArray().Select(compiledSelectExpression).Distinct().OrderBy(c => c); - var distinctByServer = Query.All().Select(selectExpression).Distinct().OrderBy(c => c); + var distinctLocal = session.Query.All().ToArray().Select(compiledSelectExpression).Distinct().OrderBy(c => c); + var distinctByServer = session.Query.All().Select(selectExpression).Distinct().OrderBy(c => c); Assert.IsTrue(distinctLocal.SequenceEqual(distinctByServer)); - distinctByServer = Query.All().Select(selectExpression).Distinct().OrderByDescending(c => c); + distinctByServer = session.Query.All().Select(selectExpression).Distinct().OrderByDescending(c => c); Assert.IsFalse(distinctLocal.SequenceEqual(distinctByServer)); } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/GroupByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/GroupByTest.cs index ccae27229b..b55afd4ba1 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/GroupByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/GroupByTest.cs @@ -1,3 +1,7 @@ +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + using System; using System.Linq; using System.Linq.Expressions; @@ -9,30 +13,24 @@ namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateTimes public class GroupByTest : DateTimeBaseTest { [Test] - public void DateTimeGroupByTest() - { - ExecuteInsideSession(() => GroupByPrivate(c => c.DateTime, c => c.Id)); - } + public void DateTimeGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.DateTime, c => c.Id)); [Test] - public void MillisecondDateTimeGroupByTest() - { - ExecuteInsideSession(() => GroupByPrivate(c => c.DateTime, c => c.Id)); - } + public void MillisecondDateTimeGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.DateTime, c => c.Id)); [Test] - public void NullableDateTimeGroupByTest() - { - ExecuteInsideSession(() => GroupByPrivate(c => c.DateTime, c => c.Id)); - } + public void NullableDateTimeGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.DateTime, c => c.Id)); - private void GroupByPrivate(Expression> groupByExpression, Expression> orderByExpression) + private static void GroupByPrivate(Session session, Expression> groupByExpression, Expression> orderByExpression) where T : Entity { var compiledGroupByExpression = groupByExpression.Compile(); var compiledOrderByExpression = orderByExpression.Compile(); - var groupByLocal = Query.All().ToArray().GroupBy(compiledGroupByExpression).ToArray(); - var groupByServer = Query.All().GroupBy(groupByExpression); + var groupByLocal = session.Query.All().ToArray().GroupBy(compiledGroupByExpression).ToArray(); + var groupByServer = session.Query.All().GroupBy(groupByExpression); foreach (var group in groupByServer) { Assert.Contains(group, groupByLocal); var localGroup = groupByLocal.Single(c => c.Key.Equals(group.Key)); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs index 8fecd9427d..0c0b34bfac 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/JoinTest.cs @@ -1,3 +1,7 @@ +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + using System; using System.Linq; using System.Linq.Expressions; @@ -11,26 +15,18 @@ public class JoinTest : DateTimeBaseTest [Test] public void DateTimeJoinTest() { - ExecuteInsideSession(() => JoinPrivate, DateTime, long>( + ExecuteInsideSession((s) => JoinPrivate, DateTime, long>(s, left => left.DateTime, right => right.DateTime, (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateTime, RightDateTime = right.DateTime }, c => c.LeftId, c => c.RightId)); -#if NET6_0_OR_GREATER //DO_DATEONLY - ExecuteInsideSession(() => JoinPrivate, DateOnly, long>( - left => left.DateOnly, - right => right.DateOnly, - (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateOnly, RightDateTime = right.DateOnly }, - c => c.LeftId, - c => c.RightId)); -#endif } [Test] public void MillisecondDateTimeJoinTest() { - ExecuteInsideSession(() => JoinPrivate, DateTime, long>( + ExecuteInsideSession((s) => JoinPrivate, DateTime, long>(s, left => left.DateTime, right => right.DateTime, (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateTime, RightDateTime = right.DateTime }, @@ -41,7 +37,7 @@ public void MillisecondDateTimeJoinTest() [Test] public void NullableDateTimeJoinTest() { - ExecuteInsideSession(() => JoinPrivate, DateTime?, long>( + ExecuteInsideSession((s) => JoinPrivate, DateTime?, long>(s, left => left.DateTime, right => right.DateTime, (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateTime, RightDateTime = right.DateTime }, @@ -49,7 +45,8 @@ public void NullableDateTimeJoinTest() c => c.RightId)); } - private void JoinPrivate(Expression> leftJoinExpression, Expression> rightJoinExpression, + private static void JoinPrivate(Session session, + Expression> leftJoinExpression, Expression> rightJoinExpression, Expression> joinResultExpression, Expression> orderByExpression, Expression> thenByExpression) where T1 : Entity where T2 : Entity @@ -59,20 +56,20 @@ private void JoinPrivate(Expression> leftJoi var compiledJoinResultExpression = joinResultExpression.Compile(); var compiledOrderByExpression = orderByExpression.Compile(); var compiledThenByExpression = thenByExpression.Compile(); - var joinLocal = Query.All().ToArray() - .Join(Query.All().ToArray(), compiledLeftJoinExpression, compiledRightJoinExpression, compiledJoinResultExpression) + var joinLocal = session.Query.All().ToArray() + .Join(session.Query.All().ToArray(), compiledLeftJoinExpression, compiledRightJoinExpression, compiledJoinResultExpression) .OrderBy(compiledOrderByExpression) .ThenBy(compiledThenByExpression); - var joinServer = Query.All() - .Join(Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) + var joinServer = session.Query.All() + .Join(session.Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) .OrderBy(orderByExpression) .ThenBy(thenByExpression); Assert.IsTrue(joinLocal.SequenceEqual(joinServer)); - joinServer = Query.All() - .Join(Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) + joinServer = session.Query.All() + .Join(session.Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) .OrderByDescending(orderByExpression) .ThenBy(thenByExpression); Assert.IsFalse(joinLocal.SequenceEqual(joinServer)); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/MinMaxTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/MinMaxTest.cs index 78cc986822..f00d2194b2 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/MinMaxTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/MinMaxTest.cs @@ -1,3 +1,7 @@ +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + using System; using System.Linq; using System.Linq.Expressions; @@ -11,29 +15,29 @@ public class MinMaxTest : DateTimeBaseTest [Test] public void DateTimeMinMaxTest() { - ExecuteInsideSession(() => MinMaxPrivate(c => c.DateTime)); + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.DateTime)); } [Test] public void MillisecondDateTimeMinMaxTest() { - ExecuteInsideSession(() => MinMaxPrivate(c => c.DateTime)); + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.DateTime)); } [Test] public void NullableDateTimeMinMaxTest() { - ExecuteInsideSession(() => MinMaxPrivate(c => c.DateTime)); + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.DateTime)); } - private void MinMaxPrivate(Expression> selectExpression) + private static void MinMaxPrivate(Session session, Expression> selectExpression) where T : Entity { var compiledSelectExpression = selectExpression.Compile(); - var minLocal = Query.All().ToArray().Min(compiledSelectExpression); - var maxLocal = Query.All().ToArray().Max(compiledSelectExpression); - var minServer = Query.All().Min(selectExpression); - var maxServer = Query.All().Max(selectExpression); + var minLocal = session.Query.All().ToArray().Min(compiledSelectExpression); + var maxLocal = session.Query.All().ToArray().Max(compiledSelectExpression); + var minServer = session.Query.All().Min(selectExpression); + var maxServer = session.Query.All().Max(selectExpression); Assert.AreEqual(minLocal, minServer); Assert.AreEqual(maxLocal, maxServer); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs index 230300116f..b0b2bb46d7 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs @@ -22,13 +22,6 @@ public void AddYearsTest() RunWrongTest(c => c.DateTime.AddYears(1) == FirstDateTime.AddYears(2)); RunWrongTest(c => c.MillisecondDateTime.AddYears(-1) == FirstMillisecondDateTime.AddYears(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddYears(33) == NullableDateTime.AddYears(44)); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(1)); - RunTest(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(33)); - - RunWrongTest(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(2)); - RunWrongTest(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(44)); -#endif }); } @@ -43,13 +36,6 @@ public void AddMonthsTest() RunWrongTest(c => c.DateTime.AddMonths(1) == FirstDateTime.AddMonths(2)); RunWrongTest(c => c.MillisecondDateTime.AddMonths(-1) == FirstMillisecondDateTime.AddMonths(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddMonths(33) == NullableDateTime.AddMonths(44)); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(1)); - RunTest(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(33)); - - RunWrongTest(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(2)); - RunWrongTest(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(44)); -#endif }); } @@ -64,13 +50,6 @@ public void AddDaysTest() RunWrongTest(c => c.DateTime.AddDays(1) == FirstDateTime.AddDays(2)); RunWrongTest(c => c.MillisecondDateTime.AddDays(-1) == FirstMillisecondDateTime.AddDays(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddDays(33) == NullableDateTime.AddDays(44)); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(1)); - RunTest(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(33)); - - RunWrongTest(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(2)); - RunWrongTest(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(44)); -#endif }); } @@ -99,11 +78,6 @@ public void AddMinutesTest() RunWrongTest(c => c.DateTime.AddMinutes(1) == FirstDateTime.AddMinutes(2)); RunWrongTest(c => c.MillisecondDateTime.AddMinutes(-1) == FirstMillisecondDateTime.AddMinutes(-2)); RunWrongTest(c => c.NullableDateTime.Value.AddMinutes(33) == NullableDateTime.AddMinutes(44)); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(1)); - - RunWrongTest(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(2)); -#endif }); } @@ -214,11 +188,6 @@ public void MinusDateTimeTest() RunWrongTest(c => c.DateTime - SecondDateTime == FirstDateTime - WrongDateTime); RunWrongTest(c => c.MillisecondDateTime - SecondDateTime == FirstMillisecondDateTime - WrongDateTime); RunWrongTest(c => c.NullableDateTime - SecondDateTime == NullableDateTime - WrongDateTime); - -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - SecondTimeOnly); - RunWrongTest(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - WrongTimeOnly); -#endif }); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs index 5c317c0795..ece14ffa9c 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OrderByTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2016 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Groznov // Created: 2016.08.01 @@ -17,45 +17,41 @@ public class OrderByTest : DateTimeBaseTest [Test] public void DateTimeOrderByTest() { - ExecuteInsideSession(() => { - OrderByPrivate(c => c.DateTime, c => c.Id); - OrderByPrivate(c => c.DateTime, c => c); -#if NET6_0_OR_GREATER //DO_DATEONLY - OrderByPrivate(c => c.DateOnly, c => c.Id); - OrderByPrivate(c => c.DateOnly, c => c); - OrderByPrivate(c => c.TimeOnly, c => c.Id ); -#endif + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.DateTime, c => c.Id); + OrderByPrivate(s, c => c.DateTime, c => c); }); } [Test] public void MillisecondDateTimeOrderByTest() { - ExecuteInsideSession(() => { - OrderByPrivate(c => c.DateTime, c => c.Id); - OrderByPrivate(c => c.DateTime, c => c); + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.DateTime, c => c.Id); + OrderByPrivate(s, c => c.DateTime, c => c); }); } [Test] public void NullableDateTimeOrderByTest() { - ExecuteInsideSession(() => { - OrderByPrivate(c => c.DateTime, c => c.Id); - OrderByPrivate(c => c.DateTime, c => c); + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.DateTime, c => c.Id); + OrderByPrivate(s, c => c.DateTime, c => c); }); } - private void OrderByPrivate(Expression> orderByExpression, Expression> thenByExpression) + private static void OrderByPrivate(Session session, + Expression> orderByExpression, Expression> thenByExpression) where T : Entity { var compiledOrderByExpression = orderByExpression.Compile(); var compiledThenByExpression = thenByExpression.Compile(); - var notOrderedLocal = Query.All().ToArray(); + var notOrderedLocal = session.Query.All().ToArray(); var orderedLocal = notOrderedLocal.OrderBy(compiledOrderByExpression).ThenBy(compiledThenByExpression); var orderedLocalDescending = notOrderedLocal.OrderByDescending(compiledOrderByExpression).ThenBy(compiledThenByExpression); - var orderedByServer = Query.All().OrderBy(orderByExpression).ThenBy(thenByExpression); - var orderedByServerDescending = Query.All().OrderByDescending(orderByExpression).ThenBy(thenByExpression); + var orderedByServer = session.Query.All().OrderBy(orderByExpression).ThenBy(thenByExpression); + var orderedByServerDescending = session.Query.All().OrderByDescending(orderByExpression).ThenBy(thenByExpression); Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedLocal)); Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedByServer)); @@ -65,16 +61,17 @@ private void OrderByPrivate(Expression> orderByExpress Assert.IsFalse(orderedLocalDescending.SequenceEqual(orderedByServer)); } - protected void OrderByPrivate(Expression> selectorExpression, Expression> orderByExpression) + protected static void OrderByPrivate(Session session, + Expression> selectorExpression, Expression> orderByExpression) where T1 : Entity { var compiledOrderByExpression = orderByExpression.Compile(); - var notOrderedLocal = Query.All().Select(selectorExpression).ToArray(); + var notOrderedLocal = session.Query.All().Select(selectorExpression).ToArray(); var orderedLocal = notOrderedLocal.OrderBy(compiledOrderByExpression); var orderedLocalDescending = notOrderedLocal.OrderByDescending(compiledOrderByExpression); - var orderedByServer = Query.All().Select(selectorExpression).OrderBy(orderByExpression); - var orderedByServerDescending = Query.All().Select(selectorExpression).OrderByDescending(orderByExpression); + var orderedByServer = session.Query.All().Select(selectorExpression).OrderBy(orderByExpression); + var orderedByServerDescending = session.Query.All().Select(selectorExpression).OrderByDescending(orderByExpression); Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedLocal)); Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedByServer)); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs index f66db476e7..93966431c0 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2022 Xtensive LLC. +// Copyright (C) 2016-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alex Groznov @@ -15,96 +15,84 @@ public class PartsExtractionTest : DateTimeBaseTest [Test] public void ExtractYearTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Year == FirstDateTime.Year); - RunTest(c => c.MillisecondDateTime.Year == FirstMillisecondDateTime.Year); - RunTest(c => c.NullableDateTime.Value.Year == NullableDateTime.Year); - - RunWrongTest(c => c.DateTime.Year == WrongDateTime.Year); - RunWrongTest(c => c.MillisecondDateTime.Year == WrongMillisecondDateTime.Year); - RunWrongTest(c => c.NullableDateTime.Value.Year == WrongDateTime.Year); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Year == FirstDateTime.Year); + RunTest(s, c => c.MillisecondDateTime.Year == FirstMillisecondDateTime.Year); + RunTest(s, c => c.NullableDateTime.Value.Year == NullableDateTime.Year); + + RunWrongTest(s, c => c.DateTime.Year == WrongDateTime.Year); + RunWrongTest(s, c => c.MillisecondDateTime.Year == WrongMillisecondDateTime.Year); + RunWrongTest(s, c => c.NullableDateTime.Value.Year == WrongDateTime.Year); }); } [Test] public void ExtractMonthTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Month == FirstDateTime.Month); - RunTest(c => c.MillisecondDateTime.Month == FirstMillisecondDateTime.Month); - RunTest(c => c.NullableDateTime.Value.Month == NullableDateTime.Month); - - RunWrongTest(c => c.DateTime.Month == WrongDateTime.Month); - RunWrongTest(c => c.MillisecondDateTime.Month == WrongMillisecondDateTime.Month); - RunWrongTest(c => c.NullableDateTime.Value.Month == WrongDateTime.Month); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.DateOnly.Month == FirstDateOnly.Month); - RunTest(c => c.NullableDateOnly.Value.Month == NullableDateOnly.Month); - - RunWrongTest(c => c.DateOnly.Month == WrongDateOnly.Month); - RunWrongTest(c => c.NullableDateOnly.Value.Month == WrongDateOnly.Month); -#endif + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Month == FirstDateTime.Month); + RunTest(s, c => c.MillisecondDateTime.Month == FirstMillisecondDateTime.Month); + RunTest(s, c => c.NullableDateTime.Value.Month == NullableDateTime.Month); + + RunWrongTest(s, c => c.DateTime.Month == WrongDateTime.Month); + RunWrongTest(s, c => c.MillisecondDateTime.Month == WrongMillisecondDateTime.Month); + RunWrongTest(s, c => c.NullableDateTime.Value.Month == WrongDateTime.Month); }); } [Test] public void ExtractDayTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Day == FirstDateTime.Day); - RunTest(c => c.MillisecondDateTime.Day == FirstMillisecondDateTime.Day); - RunTest(c => c.NullableDateTime.Value.Day == NullableDateTime.Day); - - RunWrongTest(c => c.DateTime.Day == WrongDateTime.Day); - RunWrongTest(c => c.MillisecondDateTime.Day == WrongMillisecondDateTime.Day); - RunWrongTest(c => c.NullableDateTime.Value.Day == WrongDateTime.Day); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Day == FirstDateTime.Day); + RunTest(s, c => c.MillisecondDateTime.Day == FirstMillisecondDateTime.Day); + RunTest(s, c => c.NullableDateTime.Value.Day == NullableDateTime.Day); + + RunWrongTest(s, c => c.DateTime.Day == WrongDateTime.Day); + RunWrongTest(s, c => c.MillisecondDateTime.Day == WrongMillisecondDateTime.Day); + RunWrongTest(s, c => c.NullableDateTime.Value.Day == WrongDateTime.Day); }); } [Test] public void ExtractHourTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Hour == FirstDateTime.Hour); - RunTest(c => c.MillisecondDateTime.Hour == FirstMillisecondDateTime.Hour); - RunTest(c => c.NullableDateTime.Value.Hour == NullableDateTime.Hour); - - RunWrongTest(c => c.DateTime.Hour == WrongDateTime.Hour); - RunWrongTest(c => c.MillisecondDateTime.Hour == WrongMillisecondDateTime.Hour); - RunWrongTest(c => c.NullableDateTime.Value.Hour == WrongDateTime.Hour); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Hour == FirstDateTime.Hour); + RunTest(s, c => c.MillisecondDateTime.Hour == FirstMillisecondDateTime.Hour); + RunTest(s, c => c.NullableDateTime.Value.Hour == NullableDateTime.Hour); + + RunWrongTest(s, c => c.DateTime.Hour == WrongDateTime.Hour); + RunWrongTest(s, c => c.MillisecondDateTime.Hour == WrongMillisecondDateTime.Hour); + RunWrongTest(s, c => c.NullableDateTime.Value.Hour == WrongDateTime.Hour); }); } [Test] public void ExtractMinuteTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Minute == FirstDateTime.Minute); - RunTest(c => c.MillisecondDateTime.Minute == FirstMillisecondDateTime.Minute); - RunTest(c => c.NullableDateTime.Value.Minute == NullableDateTime.Minute); - - RunWrongTest(c => c.DateTime.Minute == WrongDateTime.Minute); - RunWrongTest(c => c.MillisecondDateTime.Minute == WrongMillisecondDateTime.Minute); - RunWrongTest(c => c.NullableDateTime.Value.Minute == WrongDateTime.Minute); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Minute == FirstDateTime.Minute); + RunTest(s, c => c.MillisecondDateTime.Minute == FirstMillisecondDateTime.Minute); + RunTest(s, c => c.NullableDateTime.Value.Minute == NullableDateTime.Minute); + + RunWrongTest(s, c => c.DateTime.Minute == WrongDateTime.Minute); + RunWrongTest(s, c => c.MillisecondDateTime.Minute == WrongMillisecondDateTime.Minute); + RunWrongTest(s, c => c.NullableDateTime.Value.Minute == WrongDateTime.Minute); }); } [Test] public void ExtractSecondTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Second == FirstDateTime.Second); - RunTest(c => c.MillisecondDateTime.Second == FirstMillisecondDateTime.Second); - RunTest(c => c.NullableDateTime.Value.Second == NullableDateTime.Second); - - RunWrongTest(c => c.DateTime.Second == WrongDateTime.Second); - RunWrongTest(c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Second); - RunWrongTest(c => c.NullableDateTime.Value.Second == WrongDateTime.Second); -#if NET6_0_OR_GREATER //DO_DATEONLY - RunTest(c => c.TimeOnly.Second == FirstTimeOnly.Second); - - RunWrongTest(c => c.TimeOnly.Second == WrongTimeOnly.Second); -#endif + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Second == FirstDateTime.Second); + RunTest(s, c => c.MillisecondDateTime.Second == FirstMillisecondDateTime.Second); + RunTest(s, c => c.NullableDateTime.Value.Second == NullableDateTime.Second); + + RunWrongTest(s, c => c.DateTime.Second == WrongDateTime.Second); + RunWrongTest(s, c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Second); + RunWrongTest(s, c => c.NullableDateTime.Value.Second == WrongDateTime.Second); }); } @@ -112,9 +100,9 @@ public void ExtractSecondTest() public void ExtractMillisecondTest() { Require.ProviderIsNot(StorageProvider.MySql); - ExecuteInsideSession(() => { - RunTest(c => c.MillisecondDateTime.Millisecond == FirstMillisecondDateTime.Millisecond); - RunWrongTest(c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Millisecond); + ExecuteInsideSession((s) => { + RunTest(s, c => c.MillisecondDateTime.Millisecond == FirstMillisecondDateTime.Millisecond); + RunWrongTest(s, c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Millisecond); }); } @@ -122,24 +110,24 @@ public void ExtractMillisecondTest() public void MysqlExtractMillisecondTest() { Require.ProviderIs(StorageProvider.MySql); - ExecuteInsideSession(() => { + ExecuteInsideSession((s) => { var firstMillisecondDateTime = FirstMillisecondDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); - RunTest(c => c.MillisecondDateTime.Millisecond == firstMillisecondDateTime.Millisecond); - RunWrongTest(c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Millisecond); + RunTest(s, c => c.MillisecondDateTime.Millisecond == firstMillisecondDateTime.Millisecond); + RunWrongTest(s, c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Millisecond); }); } [Test] public void ExtractDateTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.Date == FirstDateTime.Date); - RunTest(c => c.MillisecondDateTime.Date == FirstMillisecondDateTime.Date); - RunTest(c => c.NullableDateTime.Value.Date == NullableDateTime.Date); - - RunWrongTest(c => c.DateTime.Date == WrongDateTime.Date); - RunWrongTest(c => c.MillisecondDateTime.Date == WrongMillisecondDateTime.Date); - RunWrongTest(c => c.NullableDateTime.Value.Date == WrongDateTime.Date); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.Date == FirstDateTime.Date); + RunTest(s, c => c.MillisecondDateTime.Date == FirstMillisecondDateTime.Date); + RunTest(s, c => c.NullableDateTime.Value.Date == NullableDateTime.Date); + + RunWrongTest(s, c => c.DateTime.Date == WrongDateTime.Date); + RunWrongTest(s, c => c.MillisecondDateTime.Date == WrongMillisecondDateTime.Date); + RunWrongTest(s, c => c.NullableDateTime.Value.Date == WrongDateTime.Date); }); } @@ -152,52 +140,52 @@ public void ExtractDateTest() public void ExtractDateFromMicrosecondsTest(string testValueString) { Require.ProviderIs(StorageProvider.SqlServer); - ExecuteInsideSession(() => { + ExecuteInsideSession((s) => { var testDateTime = DateTime.Parse(testValueString); - _ = new SingleDateTimeEntity() { MillisecondDateTime = testDateTime }; - RunTest(c => c.MillisecondDateTime.Date == testDateTime.Date); + _ = new SingleDateTimeEntity(s) { MillisecondDateTime = testDateTime }; + RunTest(s, c => c.MillisecondDateTime.Date == testDateTime.Date); }); } [Test] public void ExtractTimeOfDayTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.TimeOfDay == FirstDateTime.TimeOfDay); - RunTest(c => c.MillisecondDateTime.TimeOfDay == FirstMillisecondDateTime.TimeOfDay); - RunTest(c => c.NullableDateTime.Value.TimeOfDay == NullableDateTime.TimeOfDay); - - RunWrongTest(c => c.DateTime.TimeOfDay == WrongDateTime.TimeOfDay); - RunWrongTest(c => c.MillisecondDateTime.TimeOfDay == WrongMillisecondDateTime.TimeOfDay); - RunWrongTest(c => c.NullableDateTime.Value.TimeOfDay == WrongDateTime.TimeOfDay); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.TimeOfDay == FirstDateTime.TimeOfDay); + RunTest(s, c => c.MillisecondDateTime.TimeOfDay == FirstMillisecondDateTime.TimeOfDay); + RunTest(s, c => c.NullableDateTime.Value.TimeOfDay == NullableDateTime.TimeOfDay); + + RunWrongTest(s, c => c.DateTime.TimeOfDay == WrongDateTime.TimeOfDay); + RunWrongTest(s, c => c.MillisecondDateTime.TimeOfDay == WrongMillisecondDateTime.TimeOfDay); + RunWrongTest(s, c => c.NullableDateTime.Value.TimeOfDay == WrongDateTime.TimeOfDay); }); } [Test] public void ExtractDayOfYearTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.DayOfYear == FirstDateTime.DayOfYear); - RunTest(c => c.MillisecondDateTime.DayOfYear == FirstMillisecondDateTime.DayOfYear); - RunTest(c => c.NullableDateTime.Value.DayOfYear == NullableDateTime.DayOfYear); - - RunWrongTest(c => c.DateTime.DayOfYear == WrongDateTime.DayOfYear); - RunWrongTest(c => c.MillisecondDateTime.DayOfYear == WrongMillisecondDateTime.DayOfYear); - RunWrongTest(c => c.NullableDateTime.Value.DayOfYear == WrongDateTime.DayOfYear); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.DayOfYear == FirstDateTime.DayOfYear); + RunTest(s, c => c.MillisecondDateTime.DayOfYear == FirstMillisecondDateTime.DayOfYear); + RunTest(s, c => c.NullableDateTime.Value.DayOfYear == NullableDateTime.DayOfYear); + + RunWrongTest(s, c => c.DateTime.DayOfYear == WrongDateTime.DayOfYear); + RunWrongTest(s, c => c.MillisecondDateTime.DayOfYear == WrongMillisecondDateTime.DayOfYear); + RunWrongTest(s, c => c.NullableDateTime.Value.DayOfYear == WrongDateTime.DayOfYear); }); } [Test] public void ExtractDayOfWeekTest() { - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.DayOfWeek == FirstDateTime.DayOfWeek); - RunTest(c => c.MillisecondDateTime.DayOfWeek == FirstMillisecondDateTime.DayOfWeek); - RunTest(c => c.NullableDateTime.Value.DayOfWeek == NullableDateTime.DayOfWeek); - - RunWrongTest(c => c.DateTime.DayOfWeek == WrongDateTime.DayOfWeek); - RunWrongTest(c => c.MillisecondDateTime.DayOfWeek == WrongMillisecondDateTime.DayOfWeek); - RunWrongTest(c => c.NullableDateTime.Value.DayOfWeek == WrongDateTime.DayOfWeek); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.DayOfWeek == FirstDateTime.DayOfWeek); + RunTest(s, c => c.MillisecondDateTime.DayOfWeek == FirstMillisecondDateTime.DayOfWeek); + RunTest(s, c => c.NullableDateTime.Value.DayOfWeek == NullableDateTime.DayOfWeek); + + RunWrongTest(s, c => c.DateTime.DayOfWeek == WrongDateTime.DayOfWeek); + RunWrongTest(s, c => c.MillisecondDateTime.DayOfWeek == WrongMillisecondDateTime.DayOfWeek); + RunWrongTest(s, c => c.NullableDateTime.Value.DayOfWeek == WrongDateTime.DayOfWeek); }); } @@ -206,9 +194,9 @@ public void ExtractTimeOfDayTicksTest() { Require.ProviderIsNot(StorageProvider.PostgreSql | StorageProvider.Oracle); - ExecuteInsideSession(() => { - RunTest(c => c.DateTime.TimeOfDay.Ticks == FirstDateTime.TimeOfDay.Ticks); - RunWrongTest(c => c.DateTime.TimeOfDay.Ticks < FirstDateTime.TimeOfDay.Ticks); + ExecuteInsideSession((s) => { + RunTest(s, c => c.DateTime.TimeOfDay.Ticks == FirstDateTime.TimeOfDay.Ticks); + RunWrongTest(s, c => c.DateTime.TimeOfDay.Ticks < FirstDateTime.TimeOfDay.Ticks); }); } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs index 949f7a5a05..596b0cb879 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/WhereTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2021 Xtensive LLC. +// Copyright (C) 2016-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alex Groznov @@ -17,17 +17,10 @@ public class WhereTest : DateTimeBaseTest [Test] public void DateTimeWhereTest() { - ExecuteInsideSession(() => { - WherePrivate(c => c.DateTime == FirstDateTime, c => c.Id); - WherePrivate(c => c.DateTime.Hour == FirstDateTime.Hour, c => c.Id); - WherePrivate(c => c.DateTime.Second == FirstDateTime.Second, c => c.Id); - -#if NET6_0_OR_GREATER //DO_DATEONLY - WherePrivate(c => c.DateOnly == FirstDateOnly, c => c.Id); - WherePrivate(c => c.TimeOnly == FirstTimeOnly, c => c.Id); - WherePrivate(c => c.TimeOnly.Hour == FirstTimeOnly.Hour, c => c.Id); - WherePrivate(c => c.TimeOnly.Second == FirstTimeOnly.Second, c => c.Id); -#endif + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.DateTime == FirstDateTime, c => c.Id); + WherePrivate(s, c => c.DateTime.Hour == FirstDateTime.Hour, c => c.Id); + WherePrivate(s, c => c.DateTime.Second == FirstDateTime.Second, c => c.Id); }); } @@ -35,35 +28,35 @@ public void DateTimeWhereTest() public void MillisecondDateTimeWhereTest() { Require.ProviderIsNot(StorageProvider.MySql); - ExecuteInsideSession(() => { - WherePrivate(c => c.DateTime == FirstMillisecondDateTime, c => c.Id); - WherePrivate(c => c.DateTime.Hour == FirstMillisecondDateTime.Hour, c => c.Id); - WherePrivate(c => c.DateTime.Millisecond == FirstMillisecondDateTime.Millisecond, c => c.Id); + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.DateTime == FirstMillisecondDateTime, c => c.Id); + WherePrivate(s, c => c.DateTime.Hour == FirstMillisecondDateTime.Hour, c => c.Id); + WherePrivate(s, c => c.DateTime.Millisecond == FirstMillisecondDateTime.Millisecond, c => c.Id); }); } [Test] public void NullableDateTimeWhereTest() { - ExecuteInsideSession(() => { - WherePrivate(c => c.DateTime == FirstDateTime, c => c.Id); - WherePrivate(c => c.DateTime == null, c => c.Id); - WherePrivate(c => c.DateTime.HasValue && c.DateTime.Value.Hour == FirstDateTime.Hour, c => c.Id); - WherePrivate(c => c.DateTime.HasValue && c.DateTime.Value.Second == FirstDateTime.Second, c => c.Id); + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.DateTime == FirstDateTime, c => c.Id); + WherePrivate(s, c => c.DateTime == null, c => c.Id); + WherePrivate(s, c => c.DateTime.HasValue && c.DateTime.Value.Hour == FirstDateTime.Hour, c => c.Id); + WherePrivate(s, c => c.DateTime.HasValue && c.DateTime.Value.Second == FirstDateTime.Second, c => c.Id); }); } - private void WherePrivate(Expression> whereExpression, Expression> orderByExpression) + private static void WherePrivate(Session session, Expression> whereExpression, Expression> orderByExpression) where T : Entity { var compiledWhereExpression = whereExpression.Compile(); var compiledOrderByExpression = orderByExpression.Compile(); - var whereLocal = Query.All().ToArray().Where(compiledWhereExpression).OrderBy(compiledOrderByExpression); - var whereByServer = Query.All().Where(whereExpression).OrderBy(orderByExpression); + var whereLocal = session.Query.All().ToArray().Where(compiledWhereExpression).OrderBy(compiledOrderByExpression); + var whereByServer = session.Query.All().Where(whereExpression).OrderBy(orderByExpression); Assert.IsTrue(whereLocal.SequenceEqual(whereByServer)); - whereByServer = Query.All().Where(whereExpression).OrderByDescending(orderByExpression); + whereByServer = session.Query.All().Where(whereExpression).OrderByDescending(orderByExpression); Assert.IsFalse(whereLocal.SequenceEqual(whereByServer)); } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs index 8909b2b2f0..3ff12d5e19 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2016 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2016-2023 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: 2016.09.15 @@ -27,81 +27,181 @@ public abstract class DateTimeBaseTest : BaseTest #if NET6_0_OR_GREATER //DO_DATEONLY protected static readonly DateOnly FirstDateOnly = DateOnly.FromDateTime(FirstDateTime); + protected static readonly DateOnly SecondDateOnly = DateOnly.FromDateTime(SecondDateTime); protected static readonly DateOnly NullableDateOnly = DateOnly.FromDateTime(SecondDateTime); protected static readonly DateOnly WrongDateOnly = DateOnly.FromDateTime(WrongDateTime); protected static readonly TimeOnly FirstTimeOnly = TimeOnly.FromDateTime(FirstDateTime); protected static readonly TimeOnly SecondTimeOnly = TimeOnly.FromDateTime(SecondDateTime); + protected static readonly TimeOnly NullableTimeOnly = TimeOnly.FromDateTime(SecondDateTime); protected static readonly TimeOnly WrongTimeOnly = TimeOnly.FromDateTime(WrongDateTime); + + protected static readonly TimeOnly FirstMillisecondTimeOnly = TimeOnly.FromDateTime(FirstDateTime.AddMilliseconds(321)); + protected static readonly TimeOnly SecondMillisecondTimeOnly = TimeOnly.FromDateTime(SecondDateTime.AddMilliseconds(987)); + protected static readonly TimeOnly WrongMillisecondTimeOnly = TimeOnly.FromDateTime(WrongDateTime.AddMilliseconds(654)); #endif protected override void RegisterTypes(DomainConfiguration configuration) { - configuration.Types.Register(typeof (SingleDateTimeEntity)); - configuration.Types.Register(typeof (DateTimeEntity)); - configuration.Types.Register(typeof (MillisecondDateTimeEntity)); - configuration.Types.Register(typeof (NullableDateTimeEntity)); + configuration.Types.Register(typeof(SingleDateTimeEntity)); + configuration.Types.Register(typeof(DateTimeEntity)); + configuration.Types.Register(typeof(MillisecondDateTimeEntity)); + configuration.Types.Register(typeof(NullableDateTimeEntity)); +#if NET6_0_OR_GREATER + configuration.Types.Register(typeof(DateOnlyEntity)); + configuration.Types.Register(typeof(SingleDateOnlyEntity)); + configuration.Types.Register(typeof(TimeOnlyEntity)); + configuration.Types.Register(typeof(SingleTimeOnlyEntity)); +#endif } protected override void PopulateEntities(Session session) { - new SingleDateTimeEntity { + _ = new SingleDateTimeEntity(session) { DateTime = FirstDateTime, MillisecondDateTime = FirstMillisecondDateTime, -#if NET6_0_OR_GREATER //DO_DATEONLY - DateOnly = DateOnly.FromDateTime(FirstDateTime), - NullableDateOnly = DateOnly.FromDateTime(NullableDateTime), - TimeOnly = FirstTimeOnly, -#endif NullableDateTime = NullableDateTime }; - new DateTimeEntity(FirstDateTime); - new DateTimeEntity(FirstDateTime); - new DateTimeEntity(FirstDateTime.Date); - new DateTimeEntity(SecondDateTime); - new DateTimeEntity(SecondDateTime.Date); - new DateTimeEntity(new DateTime(FirstDateTime.Year, FirstDateTime.Month, FirstDateTime.Day, FirstDateTime.Hour, FirstDateTime.Minute, 0)); - new DateTimeEntity(new DateTime(FirstDateTime.Ticks, DateTimeKind.Local)); - new DateTimeEntity(FirstDateTime.Add(new TimeSpan(987, 23, 34, 45))); - new DateTimeEntity(FirstDateTime.AddYears(1)); - new DateTimeEntity(FirstDateTime.AddYears(-2)); - new DateTimeEntity(FirstDateTime.AddMonths(44)); - new DateTimeEntity(FirstDateTime.AddMonths(-55)); - new DateTimeEntity(SecondDateTime.AddHours(5)); - new DateTimeEntity(SecondDateTime.AddHours(-15)); - new DateTimeEntity(SecondDateTime.AddMinutes(59)); - new DateTimeEntity(SecondDateTime.AddMinutes(-49)); - new DateTimeEntity(SecondDateTime.AddSeconds(57)); - new DateTimeEntity(SecondDateTime.AddSeconds(-5)); +#if NET6_0_OR_GREATER + _ = new SingleDateOnlyEntity(session) { + DateOnly = FirstDateOnly, + NullableDateOnly = NullableDateOnly, + }; + + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly, NullableDateOnly = FirstDateOnly }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly, NullableDateOnly = FirstDateOnly }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly, NullableDateOnly = NullableDateOnly }; + _ = new DateOnlyEntity(session) { DateOnly = SecondDateOnly, NullableDateOnly = NullableDateOnly }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly, NullableDateOnly = null }; + _ = new DateOnlyEntity(session) { DateOnly = SecondDateOnly, NullableDateOnly = null }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly.AddYears(1), NullableDateOnly = FirstDateOnly.AddYears(1) }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly.AddYears(-2), NullableDateOnly = FirstDateOnly.AddYears(-2) }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly.AddMonths(44), NullableDateOnly = FirstDateOnly.AddMonths(44) }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly.AddMonths(-55), NullableDateOnly = FirstDateOnly.AddMonths(-55) }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly.AddDays(444), NullableDateOnly = FirstDateOnly.AddDays(444) }; + _ = new DateOnlyEntity(session) { DateOnly = FirstDateOnly.AddDays(-555), NullableDateOnly = FirstDateOnly.AddDays(-555) }; + + _ = new SingleTimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = NullableTimeOnly, + MillisecondTimeOnly = FirstMillisecondTimeOnly + }; + + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = FirstTimeOnly, + MillisecondTimeOnly = FirstTimeOnly + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = FirstTimeOnly, + MillisecondTimeOnly = FirstTimeOnly + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = NullableTimeOnly, + MillisecondTimeOnly = FirstMillisecondTimeOnly + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = NullableTimeOnly, + MillisecondTimeOnly = FirstMillisecondTimeOnly + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = null, + MillisecondTimeOnly = FirstMillisecondTimeOnly + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly, + NullableTimeOnly = null, + MillisecondTimeOnly = FirstMillisecondTimeOnly + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly.AddHours(5), + MillisecondTimeOnly = FirstMillisecondTimeOnly.AddHours(5), + NullableTimeOnly = NullableTimeOnly.AddHours(5) + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly.AddHours(-15), + MillisecondTimeOnly = FirstMillisecondTimeOnly.AddHours(-15), + NullableTimeOnly = NullableTimeOnly.AddHours(-15), + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly.AddMinutes(59), + MillisecondTimeOnly = FirstMillisecondTimeOnly.AddMinutes(59), + NullableTimeOnly = NullableTimeOnly.AddMinutes(59), + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly.AddMinutes(-49), + MillisecondTimeOnly = FirstMillisecondTimeOnly.AddMinutes(-49), + NullableTimeOnly = NullableTimeOnly.AddMinutes(-49), + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly.Add(new TimeSpan(0, 0, 57)), + MillisecondTimeOnly = FirstMillisecondTimeOnly.Add(new TimeSpan(0, 0, 57)), + NullableTimeOnly = NullableTimeOnly.Add(new TimeSpan(0, 0, 57)), + }; + _ = new TimeOnlyEntity(session) { + TimeOnly = FirstTimeOnly.Add(new TimeSpan(0, 0,-5)), + MillisecondTimeOnly = FirstMillisecondTimeOnly.Add(new TimeSpan(0, 0,-5)), + NullableTimeOnly = NullableTimeOnly.Add(new TimeSpan(0, 0,-5)), + }; + +#endif + + _ = new DateTimeEntity(session, FirstDateTime); + _ = new DateTimeEntity(session, FirstDateTime); + _ = new DateTimeEntity(session, FirstDateTime.Date); + _ = new DateTimeEntity(session, SecondDateTime); + _ = new DateTimeEntity(session, SecondDateTime.Date); + _ = new DateTimeEntity(session, new DateTime(FirstDateTime.Year, FirstDateTime.Month, FirstDateTime.Day, FirstDateTime.Hour, FirstDateTime.Minute, 0)); + _ = new DateTimeEntity(session, new DateTime(FirstDateTime.Ticks, DateTimeKind.Local)); + _ = new DateTimeEntity(session, FirstDateTime.Add(new TimeSpan(987, 23, 34, 45))); + _ = new DateTimeEntity(session, FirstDateTime.AddYears(1)); + _ = new DateTimeEntity(session, FirstDateTime.AddYears(-2)); + _ = new DateTimeEntity(session, FirstDateTime.AddMonths(44)); + _ = new DateTimeEntity(session, FirstDateTime.AddMonths(-55)); + _ = new DateTimeEntity(session, SecondDateTime.AddHours(5)); + _ = new DateTimeEntity(session, SecondDateTime.AddHours(-15)); + _ = new DateTimeEntity(session, SecondDateTime.AddMinutes(59)); + _ = new DateTimeEntity(session, SecondDateTime.AddMinutes(-49)); + _ = new DateTimeEntity(session, SecondDateTime.AddSeconds(57)); + _ = new DateTimeEntity(session, SecondDateTime.AddSeconds(-5)); var dateTime = FirstDateTime.AddYears(10); - for (var i = 0; i < 60; ++i) - new DateTimeEntity(dateTime.AddSeconds(i)); - - new MillisecondDateTimeEntity { DateTime = FirstMillisecondDateTime }; - new MillisecondDateTimeEntity { DateTime = FirstMillisecondDateTime }; - new MillisecondDateTimeEntity { DateTime = FirstMillisecondDateTime.Date }; - new MillisecondDateTimeEntity { DateTime = SecondMillisecondDateTime }; - new MillisecondDateTimeEntity { DateTime = SecondMillisecondDateTime.Date }; - new MillisecondDateTimeEntity { DateTime = new DateTime(FirstMillisecondDateTime.Year, FirstMillisecondDateTime.Month, FirstMillisecondDateTime.Day, FirstMillisecondDateTime.Hour, FirstMillisecondDateTime.Minute, 0) }; - new MillisecondDateTimeEntity { DateTime = new DateTime(FirstMillisecondDateTime.Year, FirstMillisecondDateTime.Month, FirstMillisecondDateTime.Day, FirstMillisecondDateTime.Hour, FirstMillisecondDateTime.Minute, FirstMillisecondDateTime.Second, 0) }; - new MillisecondDateTimeEntity { DateTime = new DateTime(FirstMillisecondDateTime.Ticks, DateTimeKind.Local) }; - new MillisecondDateTimeEntity { DateTime = FirstMillisecondDateTime.Add(new TimeSpan(987, 23, 34, 45)) }; + for (var i = 0; i < 60; ++i) { + _ = new DateTimeEntity(session, dateTime.AddSeconds(i)); + } + + _ = new MillisecondDateTimeEntity(session) { DateTime = FirstMillisecondDateTime }; + _ = new MillisecondDateTimeEntity(session) { DateTime = FirstMillisecondDateTime }; + _ = new MillisecondDateTimeEntity(session) { DateTime = FirstMillisecondDateTime.Date }; + _ = new MillisecondDateTimeEntity(session) { DateTime = SecondMillisecondDateTime }; + _ = new MillisecondDateTimeEntity(session) { DateTime = SecondMillisecondDateTime.Date }; + _ = new MillisecondDateTimeEntity(session) { DateTime = new DateTime(FirstMillisecondDateTime.Year, FirstMillisecondDateTime.Month, FirstMillisecondDateTime.Day, FirstMillisecondDateTime.Hour, FirstMillisecondDateTime.Minute, 0) }; + _ = new MillisecondDateTimeEntity(session) { DateTime = new DateTime(FirstMillisecondDateTime.Year, FirstMillisecondDateTime.Month, FirstMillisecondDateTime.Day, FirstMillisecondDateTime.Hour, FirstMillisecondDateTime.Minute, FirstMillisecondDateTime.Second, 0) }; + _ = new MillisecondDateTimeEntity(session) { DateTime = new DateTime(FirstMillisecondDateTime.Ticks, DateTimeKind.Local) }; + _ = new MillisecondDateTimeEntity(session) { DateTime = FirstMillisecondDateTime.Add(new TimeSpan(987, 23, 34, 45)) }; var index = 0; - foreach (var dateTimeEntity1 in Query.All()) - new MillisecondDateTimeEntity(dateTimeEntity1, ++index % 3==0 ? FirstMillisecondDateTime.Millisecond : SecondMillisecondDateTime.Millisecond); + foreach (var dateTimeEntity1 in session.Query.All()) { + var dtValue = dateTimeEntity1.DateTime.AddMilliseconds(++index % 3 == 0 ? FirstMillisecondDateTime.Millisecond : SecondMillisecondDateTime.Millisecond); + _ = new MillisecondDateTimeEntity(session, dtValue); + } dateTime = FirstMillisecondDateTime.AddYears(10); - for (var i = 0; i < 1000; ++i) - new MillisecondDateTimeEntity { DateTime = dateTime.AddMilliseconds(i) }; + for (var i = 0; i < 1000; ++i) { + _ = new MillisecondDateTimeEntity(session) { DateTime = dateTime.AddMilliseconds(i) }; + } - foreach (var dateTimeEntity in Query.All()) - new NullableDateTimeEntity(dateTimeEntity); + foreach (var dateTimeEntity in session.Query.All()) { + _ = new NullableDateTimeEntity(session) { DateTime = dateTimeEntity.DateTime }; + } - new NullableDateTimeEntity { DateTime = null }; - new NullableDateTimeEntity { DateTime = null }; + _ = new NullableDateTimeEntity(session) { DateTime = null }; + _ = new NullableDateTimeEntity(session) { DateTime = null }; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs index 692f31e742..4e40aba4fe 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2016 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2016-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Groznov // Created: 2016.08.01 @@ -23,16 +23,10 @@ public class SingleDateTimeEntity : Entity [Field] public DateTime? NullableDateTime { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY - [Field] - public DateOnly DateOnly { get; set; } - - [Field] - public DateOnly? NullableDateOnly { get; set; } - - [Field] - public TimeOnly TimeOnly { get; set; } -#endif + public SingleDateTimeEntity(Session session) + : base(session) + { + } } [HierarchyRoot] @@ -60,21 +54,10 @@ public class DateTimeEntity : Entity [Field] public DateTime DateTime { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY - [Field] - public DateOnly DateOnly { get; set; } - - [Field] - public TimeOnly TimeOnly { get; set; } -#endif - - public DateTimeEntity(DateTime dateTime) + public DateTimeEntity(Session session, DateTime dateTime) + : base(session) { DateTime = dateTime; -#if NET6_0_OR_GREATER //DO_DATEONLY - DateOnly = DateOnly.FromDateTime(dateTime); - TimeOnly = TimeOnly.FromDateTime(dateTime); -#endif } } @@ -87,13 +70,13 @@ public class MillisecondDateTimeEntity : Entity [Field] public DateTime DateTime { get; set; } - public MillisecondDateTimeEntity() + public MillisecondDateTimeEntity(Session session) { } - public MillisecondDateTimeEntity(DateTimeEntity dateTimeEntity, int milliseconds) + public MillisecondDateTimeEntity(Session session, DateTime dateTime) { - DateTime = dateTimeEntity.DateTime.AddMilliseconds(milliseconds); + DateTime = dateTime; } } @@ -106,14 +89,10 @@ public class NullableDateTimeEntity : Entity [Field] public DateTime? DateTime { get; set; } - public NullableDateTimeEntity() + public NullableDateTimeEntity(Session session) + : base(session) { } - - public NullableDateTimeEntity(DateTimeEntity dateTimeEntity) - { - DateTime = dateTimeEntity.DateTime; - } } [HierarchyRoot] @@ -172,4 +151,64 @@ public NullableDateTimeOffsetEntity(DateTimeOffsetEntity dateTimeOffsetEntity) DateTimeOffset = dateTimeOffsetEntity.DateTimeOffset; } } + + +#if NET6_0_OR_GREATER //DO_DATEONLY + [HierarchyRoot] + public class DateOnlyEntity : Entity + { + [Field, Key] + public long Id { get; private set; } + + [Field] + public DateOnly DateOnly { get; set; } + + [Field] + public DateOnly? NullableDateOnly { get; set; } + + public DateOnlyEntity(Session session) + : base(session) + { + } + } + + public class SingleDateOnlyEntity : DateOnlyEntity + { + public SingleDateOnlyEntity(Session session) + : base(session) + { + + } + } + + [HierarchyRoot] + public class TimeOnlyEntity : Entity + { + [Field, Key] + public long Id { get; private set; } + + [Field] + public TimeOnly TimeOnly { get; set; } + + [Field] + public TimeOnly MillisecondTimeOnly { get; set; } + + [Field] + public TimeOnly? NullableTimeOnly { get; set; } + + public TimeOnlyEntity(Session session) + :base(session) + { + } + } + + public class SingleTimeOnlyEntity : TimeOnlyEntity + { + public SingleTimeOnlyEntity(Session session) + : base(session) + { + } + } + +#endif } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs new file mode 100644 index 0000000000..3804c72216 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs @@ -0,0 +1,77 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; +using Xtensive.Orm.Tests.ObjectModel.Interfaces.Alphabet; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class ComparisonTestTimeOnly : DateTimeBaseTest + { + [Test] + public void EqualsTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly == FirstTimeOnly); + RunTest(s, c => c.MillisecondTimeOnly == FirstMillisecondTimeOnly); + RunTest(s, c => c.NullableTimeOnly == NullableTimeOnly); + + RunWrongTest(s, c => c.TimeOnly == WrongTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly == WrongMillisecondTimeOnly); + RunWrongTest(s, c => c.NullableTimeOnly == WrongTimeOnly); + RunWrongTest(s, c => c.NullableTimeOnly == null); + }); + } + + [Test] + public void NotEqualTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly != FirstTimeOnly.AddHours(1)); + RunTest(s, c => c.MillisecondTimeOnly != FirstMillisecondTimeOnly.AddHours(1)); + RunTest(s, c => c.NullableTimeOnly != NullableTimeOnly.AddHours(1)); + }); + } + + [Test] + public void CompareTest() + { + Require.ProviderIsNot(StorageProvider.MySql); + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly > FirstTimeOnly.AddHours(-1)); + RunTest(s, c => c.MillisecondTimeOnly > FirstMillisecondTimeOnly.Add(new TimeSpan(0,0,0,0, -1))); + + RunTest(s, c => c.TimeOnly < FirstTimeOnly.AddHours(1)); + RunTest(s, c => c.MillisecondTimeOnly < FirstMillisecondTimeOnly.Add(new TimeSpan(0, 0, 0, 0, 1))); + + RunWrongTest(s, c => c.DateTime > FirstDateTime); + RunWrongTest(s, c => c.MillisecondDateTime > FirstMillisecondDateTime); + RunWrongTest(s, c => c.MillisecondDateTime < FirstMillisecondDateTime.Date); + + RunWrongTest(s, c => c.TimeOnly > FirstTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly > FirstMillisecondTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly < FirstMillisecondTimeOnly.AddMinutes(-3)); + }); + } + + [Test] + public void CompareMysqTest() + { + Require.ProviderIs(StorageProvider.MySql); + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly > FirstTimeOnly.AddMinutes(-1)); + RunTest(s, c => c.TimeOnly < FirstTimeOnly.AddMinutes(1)); + + RunWrongTest(s, c => c.TimeOnly > FirstTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly > FirstMillisecondTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly < FirstMillisecondTimeOnly.AddMinutes(3)); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs new file mode 100644 index 0000000000..da16f8cedf --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs @@ -0,0 +1,42 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class DistinctTest : DateTimeBaseTest + { + [Test] + public void DistinctByTimeOnlyTest() => + ExecuteInsideSession((s) => DistinctPrivate(s, c => c.TimeOnly)); + + [Test] + public void DistinctByTimeOnlyWithMillisecondsTest() => + ExecuteInsideSession((s) => DistinctPrivate(s, c => c.MillisecondTimeOnly)); + + [Test] + public void DistinctByNullableTimeOnlyTest() => + ExecuteInsideSession((s) => DistinctPrivate(s, c => c.TimeOnly)); + + private static void DistinctPrivate(Session session, Expression> selectExpression) + where T : Entity + { + var compiledSelectExpression = selectExpression.Compile(); + var distinctLocal = session.Query.All().ToArray().Select(compiledSelectExpression).Distinct().OrderBy(c => c); + var distinctByServer = session.Query.All().Select(selectExpression).Distinct().OrderBy(c => c); + Assert.IsTrue(distinctLocal.SequenceEqual(distinctByServer)); + + distinctByServer = session.Query.All().Select(selectExpression).Distinct().OrderByDescending(c => c); + Assert.IsFalse(distinctLocal.SequenceEqual(distinctByServer)); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs new file mode 100644 index 0000000000..7743a2e6ca --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class GroupByTest : DateTimeBaseTest + { + [Test] + public void TimeOnlyGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.TimeOnly, c => c.Id)); + + [Test] + public void MillisecondTimeOnlyGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.MillisecondTimeOnly, c => c.Id)); + + [Test] + public void NullableTimeOnlyGroupByTest() => + ExecuteInsideSession((s) => GroupByPrivate(s, c => c.NullableTimeOnly, c => c.Id)); + + private static void GroupByPrivate(Session session, + Expression> groupByExpression, + Expression> orderByExpression) + where T : Entity + { + var compiledGroupByExpression = groupByExpression.Compile(); + var compiledOrderByExpression = orderByExpression.Compile(); + var groupByLocal = session.Query.All().ToArray().GroupBy(compiledGroupByExpression).ToArray(); + var groupByServer = session.Query.All().GroupBy(groupByExpression); + foreach (var group in groupByServer) { + Assert.Contains(group, groupByLocal); + var localGroup = groupByLocal.Single(c => c.Key.Equals(group.Key)); + Assert.IsTrue(group.OrderBy(compiledOrderByExpression).SequenceEqual(localGroup.OrderBy(compiledOrderByExpression))); + } + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs new file mode 100644 index 0000000000..bb6f1227c3 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs @@ -0,0 +1,81 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class JoinTest : DateTimeBaseTest + { + [Test] + public void TimeOnlyJoinTest() + { + ExecuteInsideSession((s) => JoinPrivate, TimeOnly, long>(s, + left => left.TimeOnly, + right => right.TimeOnly, + (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.TimeOnly, RightDateTime = right.TimeOnly }, + c => c.LeftId, + c => c.RightId)); + } + + [Test] + public void MillisecondTimeOnlyJoinTest() + { + ExecuteInsideSession((s) => JoinPrivate, TimeOnly, long>(s, + left => left.MillisecondTimeOnly, + right => right.MillisecondTimeOnly, + (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.MillisecondTimeOnly, RightDateTime = right.MillisecondTimeOnly }, + c => c.LeftId, + c => c.RightId)); + } + + [Test] + public void NullableTimeOnlyJoinTest() + { + ExecuteInsideSession((s) => JoinPrivate, TimeOnly?, long>(s, + left => left.NullableTimeOnly, + right => right.NullableTimeOnly, + (left, right) => new JoinResult { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.NullableTimeOnly, RightDateTime = right.NullableTimeOnly }, + c => c.LeftId, + c => c.RightId)); + } + + private static void JoinPrivate(Session session, + Expression> leftJoinExpression, Expression> rightJoinExpression, + Expression> joinResultExpression, Expression> orderByExpression, Expression> thenByExpression) + where T1 : Entity + where T2 : Entity + { + var compiledLeftJoinExpression = leftJoinExpression.Compile(); + var compiledRightJoinExpression = rightJoinExpression.Compile(); + var compiledJoinResultExpression = joinResultExpression.Compile(); + var compiledOrderByExpression = orderByExpression.Compile(); + var compiledThenByExpression = thenByExpression.Compile(); + var joinLocal = session.Query.All().ToArray() + .Join(session.Query.All().ToArray(), compiledLeftJoinExpression, compiledRightJoinExpression, compiledJoinResultExpression) + .OrderBy(compiledOrderByExpression) + .ThenBy(compiledThenByExpression); + + var joinServer = session.Query.All() + .Join(session.Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) + .OrderBy(orderByExpression) + .ThenBy(thenByExpression); + + Assert.IsTrue(joinLocal.SequenceEqual(joinServer)); + + joinServer = session.Query.All() + .Join(session.Query.All(), leftJoinExpression, rightJoinExpression, joinResultExpression) + .OrderByDescending(orderByExpression) + .ThenBy(thenByExpression); + Assert.IsFalse(joinLocal.SequenceEqual(joinServer)); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs new file mode 100644 index 0000000000..56efa319a4 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs @@ -0,0 +1,51 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class MinMaxTest : DateTimeBaseTest + { + [Test] + public void TimeOnlyMinMaxTest() + { + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.TimeOnly)); + } + + [Test] + public void MillisecondTimeOnlyMinMaxTest() + { + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.MillisecondTimeOnly)); + } + + [Test] + public void NullableTimeOnlyMinMaxTest() + { + ExecuteInsideSession((s) => MinMaxPrivate(s, c => c.NullableTimeOnly)); + } + + private static void MinMaxPrivate(Session session, Expression> selectExpression) + where T : Entity + { + var compiledSelectExpression = selectExpression.Compile(); + var minLocal = session.Query.All().ToArray().Min(compiledSelectExpression); + var maxLocal = session.Query.All().ToArray().Max(compiledSelectExpression); + var minServer = session.Query.All().Min(selectExpression); + var maxServer = session.Query.All().Max(selectExpression); + + Assert.AreEqual(minLocal, minServer); + Assert.AreEqual(maxLocal, maxServer); + Assert.AreNotEqual(minLocal, maxServer); + Assert.AreNotEqual(maxLocal, minServer); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs new file mode 100644 index 0000000000..48c8300d59 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs @@ -0,0 +1,94 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class OperationsTest : DateTimeBaseTest + { + [Test] + public void AddHoursTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.AddHours(1) == FirstTimeOnly.AddHours(1)); + RunTest(s, c => c.MillisecondTimeOnly.AddHours(-2) == FirstMillisecondTimeOnly.AddHours(-2)); + RunTest(s, c => c.NullableTimeOnly.Value.AddHours(33) == NullableTimeOnly.AddHours(33)); + + RunWrongTest(s, c => c.TimeOnly.AddHours(1) == FirstTimeOnly.AddHours(2)); + RunWrongTest(s, c => c.MillisecondTimeOnly.AddHours(-1) == FirstMillisecondTimeOnly.AddHours(-2)); + RunWrongTest(s, c => c.NullableTimeOnly.Value.AddHours(33) == NullableTimeOnly.AddHours(44)); + }); + } + + [Test] + public void AddMinutesTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(1)); + RunTest(s, c => c.MillisecondTimeOnly.AddMinutes(-2) == FirstMillisecondTimeOnly.AddMinutes(-2)); + RunTest(s, c => c.NullableTimeOnly.Value.AddMinutes(33) == NullableTimeOnly.AddMinutes(33)); + + RunWrongTest(s, c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(2)); + RunWrongTest(s, c => c.MillisecondTimeOnly.AddMinutes(-1) == FirstMillisecondTimeOnly.AddMinutes(-2)); + RunWrongTest(s, c => c.NullableTimeOnly.Value.AddMinutes(33) == NullableTimeOnly.AddMinutes(44)); + }); + } + + [Test] + public void AddTimeSpanTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.Add(FirstOffset) == FirstTimeOnly.Add(FirstOffset)); + RunTest(s, c => c.MillisecondTimeOnly.Add(SecondOffset) == FirstMillisecondTimeOnly.Add(SecondOffset)); + RunTest(s, c => c.NullableTimeOnly.Value.Add(FirstOffset) == NullableTimeOnly.Add(FirstOffset)); + + RunWrongTest(s, c => c.TimeOnly.Add(FirstOffset) == FirstTimeOnly.Add(WrongOffset)); + RunWrongTest(s, c => c.MillisecondTimeOnly.Add(SecondOffset) == FirstMillisecondTimeOnly.Add(WrongOffset)); + RunWrongTest(s, c => c.NullableTimeOnly.Value.Add(FirstOffset) == NullableTimeOnly.Add(WrongOffset)); + }); + } + + [Test] + public void MinusTimeOnlyTest() + { + Require.ProviderIsNot(StorageProvider.MySql); + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - SecondTimeOnly); + RunTest(s, c => c.MillisecondTimeOnly - SecondTimeOnly == FirstMillisecondTimeOnly - SecondTimeOnly); + RunTest(s, c => c.NullableTimeOnly - SecondTimeOnly == NullableTimeOnly - SecondTimeOnly); + + RunWrongTest(s, c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - WrongTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly - SecondTimeOnly == FirstMillisecondTimeOnly - WrongTimeOnly); + RunWrongTest(s, c => c.NullableTimeOnly - SecondTimeOnly == NullableTimeOnly - WrongTimeOnly); + + }); + } + + [Test] + public void MysqlMinisDateTimeTest() + { + Require.ProviderIs(StorageProvider.MySql); + ExecuteInsideSession((s) => { + var firstTimeOnly = FirstTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + var secondTimeOnly = SecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + var nullableTimeOnly = NullableTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + + RunTest(s, c => c.TimeOnly - secondTimeOnly == firstTimeOnly - secondTimeOnly); + RunTest(s, c => c.MillisecondTimeOnly - secondTimeOnly == firstMillisecondTimeOnly - secondTimeOnly); + RunTest(s, c => c.NullableTimeOnly - secondTimeOnly == NullableTimeOnly - secondTimeOnly); + + RunWrongTest(s, c => c.TimeOnly - secondTimeOnly == secondTimeOnly - WrongTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly - secondTimeOnly == firstMillisecondTimeOnly - WrongTimeOnly); + RunWrongTest(s, c => c.NullableTimeOnly - secondTimeOnly == nullableTimeOnly - WrongTimeOnly); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs new file mode 100644 index 0000000000..39b74d5697 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs @@ -0,0 +1,83 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class OrderByTest : DateTimeBaseTest + { + [Test] + public void TimeOnlyOrderByTest() + { + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.TimeOnly, c => c.Id); + OrderByPrivate(s, c => c.TimeOnly, c => c); + }); + } + + [Test] + public void MillisecondTimeOnlyOrderByTest() + { + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.MillisecondTimeOnly, c => c.Id); + OrderByPrivate(s, c => c.MillisecondTimeOnly, c => c); + }); + } + + [Test] + public void NullableTimeOnlyOrderByTest() + { + ExecuteInsideSession((s) => { + OrderByPrivate(s, c => c.NullableTimeOnly, c => c.Id); + OrderByPrivate(s, c => c.NullableTimeOnly, c => c); + }); + } + + private static void OrderByPrivate(Session session, Expression> orderByExpression, Expression> thenByExpression) + where T : Entity + { + var compiledOrderByExpression = orderByExpression.Compile(); + var compiledThenByExpression = thenByExpression.Compile(); + var notOrderedLocal = session.Query.All().ToArray(); + var orderedLocal = notOrderedLocal.OrderBy(compiledOrderByExpression).ThenBy(compiledThenByExpression); + var orderedLocalDescending = notOrderedLocal.OrderByDescending(compiledOrderByExpression).ThenBy(compiledThenByExpression); + var orderedByServer = session.Query.All().OrderBy(orderByExpression).ThenBy(thenByExpression); + var orderedByServerDescending = session.Query.All().OrderByDescending(orderByExpression).ThenBy(thenByExpression); + + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedLocal)); + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocalDescending.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocal.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocalDescending.SequenceEqual(orderedByServer)); + } + + protected static void OrderByPrivate(Session session, Expression> selectorExpression, Expression> orderByExpression) + where T1 : Entity + { + var compiledOrderByExpression = orderByExpression.Compile(); + + var notOrderedLocal = session.Query.All().Select(selectorExpression).ToArray(); + var orderedLocal = notOrderedLocal.OrderBy(compiledOrderByExpression); + var orderedLocalDescending = notOrderedLocal.OrderByDescending(compiledOrderByExpression); + var orderedByServer = session.Query.All().Select(selectorExpression).OrderBy(orderByExpression); + var orderedByServerDescending = session.Query.All().Select(selectorExpression).OrderByDescending(orderByExpression); + + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedLocal)); + Assert.IsFalse(notOrderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocal.SequenceEqual(orderedByServer)); + Assert.IsTrue(orderedLocalDescending.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocal.SequenceEqual(orderedByServerDescending)); + Assert.IsFalse(orderedLocalDescending.SequenceEqual(orderedByServer)); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs new file mode 100644 index 0000000000..1e58b63fe3 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs @@ -0,0 +1,90 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class PartsExtractionTest : DateTimeBaseTest + { + [Test] + public void ExtractHourTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.Hour == FirstTimeOnly.Hour); + RunTest(s, c => c.MillisecondTimeOnly.Hour == FirstMillisecondTimeOnly.Hour); + RunTest(s, c => c.NullableTimeOnly.Value.Hour == NullableTimeOnly.Hour); + + RunWrongTest(s, c => c.TimeOnly.Hour == WrongTimeOnly.Hour); + RunWrongTest(s, c => c.MillisecondTimeOnly.Hour == WrongMillisecondTimeOnly.Hour); + RunWrongTest(s, c => c.NullableTimeOnly.Value.Hour == WrongTimeOnly.Hour); + }); + } + + [Test] + public void ExtractMinuteTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.Minute == FirstTimeOnly.Minute); + RunTest(s, c => c.MillisecondTimeOnly.Minute == FirstMillisecondTimeOnly.Minute); + RunTest(s, c => c.NullableTimeOnly.Value.Minute == NullableTimeOnly.Minute); + + RunWrongTest(s, c => c.TimeOnly.Minute == WrongTimeOnly.Minute); + RunWrongTest(s, c => c.MillisecondTimeOnly.Minute == WrongMillisecondTimeOnly.Minute); + RunWrongTest(s, c => c.NullableTimeOnly.Value.Minute == WrongTimeOnly.Minute); + }); + } + + [Test] + public void ExtractSecondTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.Second == FirstTimeOnly.Second); + RunTest(s, c => c.MillisecondTimeOnly.Second == FirstMillisecondTimeOnly.Second); + RunTest(s, c => c.NullableTimeOnly.Value.Second == NullableTimeOnly.Second); + + RunWrongTest(s, c => c.TimeOnly.Second == WrongTimeOnly.Second); + RunWrongTest(s, c => c.MillisecondTimeOnly.Second == WrongMillisecondTimeOnly.Second); + RunWrongTest(s, c => c.NullableTimeOnly.Value.Second == WrongTimeOnly.Second); + }); + } + + [Test] + public void ExtractMillisecondTest() + { + Require.ProviderIsNot(StorageProvider.MySql); + ExecuteInsideSession((s) => { + RunTest(s, c => c.MillisecondTimeOnly.Millisecond == FirstMillisecondTimeOnly.Millisecond); + RunWrongTest(s, c => c.MillisecondTimeOnly.Second == WrongMillisecondTimeOnly.Millisecond); + }); + } + + [Test] + public void MysqlExtractMillisecondTest() + { + Require.ProviderIs(StorageProvider.MySql); + ExecuteInsideSession((s) => { + var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + RunTest(s, c => c.MillisecondTimeOnly.Millisecond == firstMillisecondTimeOnly.Millisecond); + RunWrongTest(s, c => c.MillisecondTimeOnly.Second == WrongMillisecondTimeOnly.Millisecond); + }); + } + + [Test] + public void ExtractTicksTest() + { + Require.ProviderIsNot(StorageProvider.PostgreSql | StorageProvider.Oracle); + + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.Ticks == FirstTimeOnly.Ticks); + RunWrongTest(s, c => c.TimeOnly.Ticks < FirstTimeOnly.Ticks); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs new file mode 100644 index 0000000000..b9ab70a0ef --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class TimeOnlyToIsoStringTest : DateTimeBaseTest + { + [Test] + public void ToIsoStringTest() + { + ExecuteInsideSession((s) => { + RunTest(s, c => c.TimeOnly.ToString("s") == FirstTimeOnly.ToString("s")); + RunWrongTest(s, c => c.TimeOnly.ToString("s") == FirstTimeOnly.AddHours(1).ToString("s")); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs new file mode 100644 index 0000000000..b6e3c91112 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class WhereTest : DateTimeBaseTest + { + [Test] + public void DateTimeWhereTest() + { + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.TimeOnly == FirstTimeOnly, c => c.Id); + WherePrivate(s, c => c.TimeOnly.Hour == FirstTimeOnly.Hour, c => c.Id); + WherePrivate(s, c => c.TimeOnly.Second == FirstTimeOnly.Second, c => c.Id); + }); + } + + [Test] + public void MillisecondDateTimeWhereTest() + { + Require.ProviderIsNot(StorageProvider.MySql); + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.MillisecondTimeOnly == FirstMillisecondTimeOnly, c => c.Id); + WherePrivate(s, c => c.MillisecondTimeOnly.Hour == FirstMillisecondTimeOnly.Hour, c => c.Id); + WherePrivate(s, c => c.MillisecondTimeOnly.Millisecond == FirstMillisecondTimeOnly.Millisecond, c => c.Id); + }); + } + + [Test] + public void NullableDateTimeWhereTest() + { + ExecuteInsideSession((s) => { + WherePrivate(s, c => c.NullableTimeOnly == NullableTimeOnly, c => c.Id); + WherePrivate(s, c => c.NullableTimeOnly == null, c => c.Id); + WherePrivate(s, c => c.NullableTimeOnly.HasValue && c.NullableTimeOnly.Value.Hour == NullableTimeOnly.Hour, c => c.Id); + WherePrivate(s, c => c.NullableTimeOnly.HasValue && c.NullableTimeOnly.Value.Second == NullableTimeOnly.Second, c => c.Id); + }); + } + + private static void WherePrivate(Session session, Expression> whereExpression, Expression> orderByExpression) + where T : Entity + { + var compiledWhereExpression = whereExpression.Compile(); + var compiledOrderByExpression = orderByExpression.Compile(); + + var whereLocal = session.Query.All().ToArray().Where(compiledWhereExpression).OrderBy(compiledOrderByExpression); + var whereByServer = session.Query.All().Where(whereExpression).OrderBy(orderByExpression); + Assert.IsTrue(whereLocal.SequenceEqual(whereByServer)); + + whereByServer = session.Query.All().Where(whereExpression).OrderByDescending(orderByExpression); + Assert.IsFalse(whereLocal.SequenceEqual(whereByServer)); + } + } +} +#endif \ No newline at end of file From b7d49e337045bee5f0cb52efdf1f8b300a6b0ebf Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 24 Jan 2023 20:19:12 +0500 Subject: [PATCH 33/86] Register DateOnly/Time only member compilers --- Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs index e5ed7fa2e5..04b2fbb53d 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs @@ -128,6 +128,10 @@ protected virtual IEnumerable GetProviderCompilerContainers() typeof (StringCompilers), typeof (DateTimeCompilers), typeof (DateTimeOffsetCompilers), +#if NET6_0_OR_GREATER //DO_DATEONLY + typeof (DateOnlyCompilers), + typeof (TimeOnlyCompilers), +#endif typeof (TimeSpanCompilers), typeof (MathCompilers), typeof (NumericCompilers), From 832e8a04fec3e6e2d3cefdc610489669e01628fb Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 24 Jan 2023 20:20:04 +0500 Subject: [PATCH 34/86] Add DateOnly/TimeOnly as supported types for Min/Max ops --- .../Orm/Rse/Providers/Compilable/AggregateProvider.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index e4f26b7c1f..477d5f0de0 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -133,7 +133,11 @@ private static Type GetMinMaxColumnType(Type sourceColumnType, AggregateType agg default: if (sourceColumnType==WellKnownTypes.TimeSpan || sourceColumnType==WellKnownTypes.DateTimeOffset) return sourceColumnType; - throw AggregateNotSupported(sourceColumnType, aggregateType); +#if NET6_0_OR_GREATER + if (sourceColumnType == WellKnownTypes.DateOnly || sourceColumnType == WellKnownTypes.TimeOnly) + return sourceColumnType; +#endif + throw AggregateNotSupported(sourceColumnType, aggregateType); } } From d5241aadcf77f2116c66c06d181c161d18e8fd00 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 24 Jan 2023 21:10:38 +0500 Subject: [PATCH 35/86] TimeOnlyCompilers updated - correct namespace for compilers type - correct names for compiler methods - removed datetime operators which were accidentally added - "Add" method compiler, not implemented yet - "Ticks" property compiler, not implemented yet --- .../MemberCompilers/TimeOnlyCompilers.cs | 69 +++++-------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index f75e98c0be..a4d408cea5 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -7,13 +7,15 @@ using Xtensive.Sql.Dml; using Operator = Xtensive.Reflection.WellKnown.Operator; -namespace Xtensive.Orm.Providers.Expressions.MemberCompilers +namespace Xtensive.Orm.Providers { #if NET6_0_OR_GREATER [CompilerContainer(typeof(SqlExpression))] internal static class TimeOnlyCompilers { + private const long NanosecondsPerTick = 100; + #region Extractors [Compiler(typeof(TimeOnly), "Hour", TargetKind.PropertyGet)] @@ -32,6 +34,12 @@ public static SqlExpression TimeOnlySecond(SqlExpression _this) => public static SqlExpression TimeOnlyMillisecond(SqlExpression _this) => ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); + [Compiler(typeof(TimeOnly), "Ticks", TargetKind.PropertyGet)] + public static SqlExpression TimeOnlyTicks(SqlExpression _this) + { + throw new NotImplementedException(); + } + #endregion #region Constructors @@ -57,54 +65,6 @@ public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression tick #region Operators - [Compiler(typeof(DateOnly), Operator.Equality, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorEquality( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 == d2; - } - - [Compiler(typeof(DateOnly), Operator.Inequality, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorInequality( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 != d2; - } - - [Compiler(typeof(DateOnly), Operator.GreaterThan, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorGreaterThan( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 > d2; - } - - [Compiler(typeof(DateOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorGreaterThanOrEqual( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 >= d2; - } - - [Compiler(typeof(DateOnly), Operator.LessThan, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorLessThan( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 < d2; - } - - [Compiler(typeof(DateOnly), Operator.LessThanOrEqual, TargetKind.Operator)] - public static SqlExpression DateOnlyOperatorLessThanOrEqual( - [Type(typeof(DateOnly))] SqlExpression d1, - [Type(typeof(DateOnly))] SqlExpression d2) - { - return d1 <= d2; - } - [Compiler(typeof(TimeOnly), Operator.Equality, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorEquality( [Type(typeof(TimeOnly))] SqlExpression d1, @@ -154,16 +114,19 @@ public static SqlExpression TimeOnlyOperatorLessThanOrEqual( } [Compiler(typeof(TimeOnly), Operator.Subtraction, TargetKind.Operator)] - public static SqlExpression DateTimeOperatorSubtractionDateTime( - [Type(typeof(DateTime))] SqlExpression d1, - [Type(typeof(DateTime))] SqlExpression d2) + public static SqlExpression TimeOnlyOperatorSubtraction( + [Type(typeof(TimeOnly))] SqlExpression d1, + [Type(typeof(TimeOnly))] SqlExpression d2) { throw new NotImplementedException(); - //return SqlDml.DateTimeMinusDateTime(d1, d2); } #endregion + [Compiler(typeof(TimeOnly), "Add")] + public static SqlExpression TimeOnlyAdd(SqlExpression _this, [Type(typeof(TimeSpan))] SqlExpression value) => + throw new NotImplementedException(); + [Compiler(typeof(TimeOnly), "AddHours")] public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => SqlDml.TimeAddHours(_this, value); From 7ee35218d99299648661c78404891db4b447466e Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 24 Jan 2023 21:16:24 +0500 Subject: [PATCH 36/86] DateOnlyCompilers updated - correct namespace for compilers type - correct names for compiler methods - DayOfWeek compiler implementation --- .../MemberCompilers/DateOnlyCompilers.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index 8b7fe0736d..ad6b2457e3 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -8,7 +8,7 @@ using Xtensive.Sql.Dml; using Operator = Xtensive.Reflection.WellKnown.Operator; -namespace Xtensive.Orm.Providers.Expressions.MemberCompilers +namespace Xtensive.Orm.Providers { #if NET6_0_OR_GREATER @@ -31,24 +31,23 @@ public static SqlExpression DateOnlyDay(SqlExpression _this) => [Compiler(typeof(DateOnly), "DayOfYear", TargetKind.PropertyGet)] - public static SqlExpression DateTimeDayOfYear(SqlExpression _this) + public static SqlExpression DateOnlyDayOfYear(SqlExpression _this) { return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfYear, _this)); } [Compiler(typeof(DateOnly), "DayOfWeek", TargetKind.PropertyGet)] - public static SqlExpression DateTimeDayOfWeek(SqlExpression _this) + public static SqlExpression DateOnlyDayOfWeek(SqlExpression _this) { - throw new NotImplementedException(); - //var baseExpression = ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfWeek, _this)); - //var context = ExpressionTranslationContext.Current; - //if (context == null) { - // return baseExpression; - //} - //if (context.ProviderInfo.ProviderName == WellKnown.Provider.MySql) { - // return baseExpression - 1; //Mysql starts days of week from 1 unlike in .Net. - //} - //return baseExpression; + var baseExpression = ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfWeek, _this)); + var context = ExpressionTranslationContext.Current; + if (context == null) { + return baseExpression; + } + if (context.ProviderInfo.ProviderName == WellKnown.Provider.MySql) { + return baseExpression - 1; //Mysql starts days of week from 1 unlike in .Net. + } + return baseExpression; } #endregion @@ -129,13 +128,13 @@ public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(in SqlDml.DateAddDays(_this, value); [Compiler(typeof(DateOnly), "ToString")] - public static SqlExpression DateTimeToStringIso(SqlExpression _this) + public static SqlExpression DateOnlyToStringIso(SqlExpression _this) { throw new NotSupportedException(Strings.ExDateTimeToStringMethodIsNotSupported); } [Compiler(typeof(DateOnly), "ToString")] - public static SqlExpression DateTimeToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) + public static SqlExpression DateOnlyToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) { throw new NotImplementedException(); //var stringValue = value as SqlLiteral; From f513f5d72383e06b293e3d64e2428e98bddbadea Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Jan 2023 17:14:23 +0500 Subject: [PATCH 37/86] DateTime/DateOnly/TimeOnly ctor usage within LINQ tests --- .../DateOnly/ConstructorTest.cs | 29 ++++++ .../DateTime/ConstructorTest.cs | 53 +++++++++++ .../DateTimeBaseTest.cs | 3 + .../Linq/DateTimeAndDateTimeOffset/Model.cs | 91 +++++++++++++++++++ .../TimeOnly/ConstructorTest.cs | 57 ++++++++++++ .../TimeOnly/OperationsTest.cs | 2 +- 6 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ConstructorTest.cs create mode 100644 Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs new file mode 100644 index 0000000000..d2524233f8 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using NUnit.Framework; +using Xtensive.Core; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys +{ + public class ConstructorTest : DateTimeBaseTest + { + [Test] + public void CtorYMD() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { Entity = e, ConstructedDate = new DateOnly(e.Year, e.Month, e.Day) }) + .Where(a => a.ConstructedDate == FirstDateOnly).OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ConstructorTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ConstructorTest.cs new file mode 100644 index 0000000000..693037f7c0 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ConstructorTest.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Linq; +using NUnit.Framework; +using Xtensive.Core; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateTimes +{ + public class ConstructorTest : DateTimeBaseTest + { + [Test] + public void CtorYMDHMSM() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { + Entity = e, + ConstructedDate = new DateTime(e.Year, e.Month, e.Day, e.Hour, e.Minute, e.Second, e.Millisecond) }) + .Where(a => a.ConstructedDate == FirstMillisecondDateTime).OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + + [Test] + public void CtorYMDHMS() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { + Entity = e, + ConstructedDate = new DateTime(e.Year, e.Month, e.Day, e.Hour, e.Minute, e.Second) + }) + .Where(a => a.ConstructedDate == FirstDateTime).OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + + [Test] + public void CtorYMD() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { Entity = e, ConstructedDate = new DateTime(e.Year, e.Month, e.Day) }) + .Where(a => a.ConstructedDate == FirstDateTime.Date).OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + } +} diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs index 3ff12d5e19..d95fe15ff0 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs @@ -47,6 +47,7 @@ protected override void RegisterTypes(DomainConfiguration configuration) configuration.Types.Register(typeof(DateTimeEntity)); configuration.Types.Register(typeof(MillisecondDateTimeEntity)); configuration.Types.Register(typeof(NullableDateTimeEntity)); + configuration.Types.Register(typeof(AllPossiblePartsEntity)); #if NET6_0_OR_GREATER configuration.Types.Register(typeof(DateOnlyEntity)); configuration.Types.Register(typeof(SingleDateOnlyEntity)); @@ -202,6 +203,8 @@ protected override void PopulateEntities(Session session) _ = new NullableDateTimeEntity(session) { DateTime = null }; _ = new NullableDateTimeEntity(session) { DateTime = null }; + + _ = AllPossiblePartsEntity.FromDateTime(session, FirstMillisecondDateTime, 321); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs index 4e40aba4fe..1ad9737841 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs @@ -5,6 +5,8 @@ // Created: 2016.08.01 using System; +using System.Net.Sockets; +using Org.BouncyCastle.Crypto.Digests; namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model { @@ -153,6 +155,95 @@ public NullableDateTimeOffsetEntity(DateTimeOffsetEntity dateTimeOffsetEntity) } + [HierarchyRoot] + public class AllPossiblePartsEntity : Entity + { + [Field, Key] + public long Id { get; private set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 3000)] + public int Year { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 1, Max = 12)] + public int Month { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 1, Max = 31)] + public int Day { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 23)] + public int Hour { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 59)] + public int Minute { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 59)] + public int Second { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 999)] + public int Millisecond { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 999)] + public int Microsecond { get; set; } + + [Field] + public long Ticks { get; set; } + + [Field] + [Validation.RangeConstraint(Min = -23, Max = 23)] + public int OffsetHour { get; set; } + + [Field] + [Validation.RangeConstraint(Min = 0, Max = 59)] + public int OffsetMinute { get; set; } + + public static AllPossiblePartsEntity FromDateTime(Session session, DateTime dateTime, int microsecond) + { + return new AllPossiblePartsEntity(session) { + Year = dateTime.Year, + Month = dateTime.Month, + Day = dateTime.Day, + Hour = dateTime.Hour, + Minute = dateTime.Minute, + Second = dateTime.Second, + Millisecond = dateTime.Millisecond, + Microsecond = microsecond, + OffsetHour = 0, + OffsetMinute = 0, + Ticks = dateTime.Ticks + }; + } + + public static AllPossiblePartsEntity FromDateTimeOffset(Session session, DateTimeOffset dateTimeOffset, int microsecond) + { + return new AllPossiblePartsEntity(session) { + Year = dateTimeOffset.Year, + Month = dateTimeOffset.Month, + Day = dateTimeOffset.Day, + Hour = dateTimeOffset.Hour, + Minute = dateTimeOffset.Minute, + Second = dateTimeOffset.Second, + Millisecond = dateTimeOffset.Millisecond, + Microsecond = microsecond, + OffsetHour = dateTimeOffset.Offset.Hours, + OffsetMinute = dateTimeOffset.Offset.Minutes, + Ticks = dateTimeOffset.Ticks + }; + } + + private AllPossiblePartsEntity(Session session) + : base(session) + { + } + } + #if NET6_0_OR_GREATER //DO_DATEONLY [HierarchyRoot] public class DateOnlyEntity : Entity diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs new file mode 100644 index 0000000000..2828f5f68e --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs @@ -0,0 +1,57 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER //DO_DATEONLY + +using System; +using System.Linq; +using NUnit.Framework; +using Xtensive.Core; +using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; + +namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys +{ + public class ConstructorTest : DateTimeBaseTest + { + [Test] + public void CtorHMSM() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { + Entity = e, + ConstructedTime = new TimeOnly(e.Hour, e.Minute, e.Second, e.Millisecond) }) + .Where(a => a.ConstructedTime == FirstMillisecondTimeOnly).OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + + [Test] + public void CtorHMS() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { + Entity = e, + ConstructedTime = new TimeOnly(e.Hour, e.Minute, e.Second) + }) + .Where(a => a.ConstructedTime == FirstTimeOnly).OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + + [Test] + public void CtorHM() + { + ExecuteInsideSession((s) => { + var result = s.Query.All() + .Select(e => new { Entity = e, ConstructedTime = new TimeOnly(e.Hour, e.Minute) }) + .Where(a => a.ConstructedTime == FirstTimeOnly.Add(TimeSpan.FromSeconds(-5))) + .OrderBy(a => a.Entity.Id).ToList(3); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs index 48c8300d59..3943f7cdce 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs @@ -40,7 +40,7 @@ public void AddMinutesTest() }); } - [Test] + [Test, Ignore("Compiler's not implemented yet")] public void AddTimeSpanTest() { ExecuteInsideSession((s) => { From 86b26be3a76714c94ce15cf7a61aa0ed69e96eb9 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Jan 2023 17:16:26 +0500 Subject: [PATCH 38/86] TimeOnly ctors compilers list updated --- .../MemberCompilers/TimeOnlyCompilers.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index a4d408cea5..ce208d0c66 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -3,6 +3,7 @@ // See the License.txt file in the project root for more information. using System; +using Xtensive.Reflection; using Xtensive.Sql; using Xtensive.Sql.Dml; using Operator = Xtensive.Reflection.WellKnown.Operator; @@ -43,6 +44,13 @@ public static SqlExpression TimeOnlyTicks(SqlExpression _this) #endregion #region Constructors + [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + public static SqlExpression TimeOnlyCtor( + [Type(typeof(int))] SqlExpression hour, + [Type(typeof(int))] SqlExpression minute, + [Type(typeof(int))] SqlExpression second, + [Type(typeof(int))] SqlExpression millisecond) => + SqlDml.TimeConstruct(hour, minute, second, millisecond); [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] public static SqlExpression TimeOnlyCtor( @@ -57,9 +65,9 @@ public static SqlExpression TimeOnlyCtor( [Type(typeof(int))] SqlExpression minute) => SqlDml.TimeConstruct(hour, minute, 0, 0); - [Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] - public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression ticks) => - new SqlFunctionCall(SqlFunctionType.TimeConstruct, ticks); + //[Compiler(typeof(TimeOnly), null, TargetKind.Constructor)] + //public static SqlExpression TimeOnlyCtor([Type(typeof(long))] SqlExpression ticks) => + // new SqlFunctionCall(SqlFunctionType.TimeConstruct, ticks); #endregion @@ -123,10 +131,6 @@ public static SqlExpression TimeOnlyOperatorSubtraction( #endregion - [Compiler(typeof(TimeOnly), "Add")] - public static SqlExpression TimeOnlyAdd(SqlExpression _this, [Type(typeof(TimeSpan))] SqlExpression value) => - throw new NotImplementedException(); - [Compiler(typeof(TimeOnly), "AddHours")] public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => SqlDml.TimeAddHours(_this, value); @@ -145,7 +149,6 @@ public static SqlExpression TimeOnlyToStringIso(SqlExpression _this) public static SqlExpression TimeOnlyToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) { throw new NotImplementedException(); - //var stringValue = value as SqlLiteral; //if (stringValue == null) From e26210e564ae7ef9649d0a282678ad20f0cb596c Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Jan 2023 17:17:55 +0500 Subject: [PATCH 39/86] SqlDml: Add DateConstruct/TimeConstruct methods --- Orm/Xtensive.Orm/Sql/SqlDml.cs | 46 ++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 1f8c5dc049..00736a0cae 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -565,6 +565,19 @@ public static SqlFunctionCall DateTimeConstruct(SqlExpression year, SqlExpressio } #if NET6_0_OR_GREATER //DO_DATEONLY + + // SQL Server - DATEFROMPARTS(y, m, d, fractions, precision) https://learn.microsoft.com/en-us/sql/t-sql/functions/datefromparts-transact-sql?view=sql-server-2016 + // Mysql - MAKEDATE(year, dayofyear), propably. https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html + // Oracle - if x is the number of seconds after midnight on January 1, 1970: TO_DATE( '01/01/1970', 'MM/DD/YYYY') + (x / (24 * 60 * 60)) + // In the examples above, x can be any number, not necessarily an integer. + //https://community.oracle.com/tech/developers/discussion/958492/how-to-convert-integer-to-date + // PgSql - some magic with string concatination or by using to_timestampt(ticks-in-some-form::numeric)::time https://stackoverflow.com/questions/26198358/postgresql-cast-numeric-to-date-and-format + // https://stackoverflow.com/questions/22835874/postgresql-convert-timestamp-to-time-or-retrieve-only-time-from-timestamp-col + // from 9.4 make_date(year, month, day) + + // firebird - dateadd(second, AmountOfSeconds, cast('00:00:00' as time)) + // sqlite - probably date(seconds-form-unixepoch, 'unixepoch') + public static SqlFunctionCall DateConstruct(SqlExpression year, SqlExpression month, SqlExpression day) { ArgumentNullException.ThrowIfNull(year); @@ -576,14 +589,31 @@ public static SqlFunctionCall DateConstruct(SqlExpression year, SqlExpression mo return new SqlFunctionCall(SqlFunctionType.DateConstruct, year, month, day); } - public static SqlFunctionCall TimeConstruct(SqlExpression hours, - SqlExpression minutes, - SqlExpression seconds, - SqlExpression milliseconds) - { - var m = milliseconds + 1000L * (seconds + 60L * (minutes + 60L * hours)); - var ticks = 10_000 * m; - return new SqlFunctionCall(SqlFunctionType.TimeConstruct, ticks); + // SQL Server - TIMEFROMPARTS(h, m, s, fractions, precision) https://learn.microsoft.com/en-us/sql/t-sql/functions/timefromparts-transact-sql?view=sql-server-2016 + // Mysql - addtime (MAKETIME(h, m, s), '00:00:00.), propably. https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html + // Oracle - numtodsinterval(value-in-seconds.milliseconds, 'second') //https://docs.oracle.com/database/121/SQLRF/functions129.htm#SQLRF00682 + // PgSql - see current impl of DateConstruct for inspiration :) + // from 9.4 there is make_time(h, m,s) + // firebird - dateadd(second, ColAmountOfSeconds, cast('00:00:00' as time)) + // sqlite - time('00:00:00', '+ NNN.NNNN seconds' //https://codernotes.ru/articles/bazy-dannyh-t-sql/funkcii-daty-i-vremeni-v-sqlite.html + + public static SqlFunctionCall TimeConstruct(SqlExpression hour, + SqlExpression minute, + SqlExpression second, + SqlExpression millisecond) + { + ArgumentNullException.ThrowIfNull(hour); + ArgumentNullException.ThrowIfNull(minute); + ArgumentNullException.ThrowIfNull(second); + ArgumentNullException.ThrowIfNull(millisecond); + SqlValidator.EnsureIsArithmeticExpression(hour); + SqlValidator.EnsureIsArithmeticExpression(minute); + SqlValidator.EnsureIsArithmeticExpression(second); + SqlValidator.EnsureIsArithmeticExpression(millisecond); + + //var m = milliseconds + 1000L * (seconds + 60L * (minutes + 60L * hours)); + //var ticks = 10_000 * m; + return new SqlFunctionCall(SqlFunctionType.TimeConstruct, hour, minute, second, millisecond); } #endif From 7e52c92f4b96377348c7d8bd8ad8d44af7f4fa92 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Jan 2023 17:28:14 +0500 Subject: [PATCH 40/86] SqlServer: add support for DateConstruct/TimeConstruct additionally, improved datetime/date/time construction for versions since v11 by using built-in functions --- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 18 +++++++++ .../Sql.Drivers.SqlServer/v11/Compiler.cs | 39 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index 85a9572a5d..839c99c6e3 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -219,6 +219,24 @@ public override void Visit(SqlFunctionCall node) arguments[1] - 1), arguments[2] - 1)); return; +#if NET6_0_OR_GREATER + case SqlFunctionType.DateConstruct: { + Visit(SqlDml.Cast(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1), SqlType.Date)); + return; + } + case SqlFunctionType.TimeConstruct: { + Visit(SqlDml.Cast(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Literal(new TimeOnly(0, 0, 0)), + arguments[0]), + arguments[1]), + arguments[2]), + arguments[3]), SqlType.Time)); + return; + } + +#endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(arguments[0])); return; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs index 63736de380..351dfe4c2b 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs @@ -44,6 +44,45 @@ protected override void VisitSelectLimitOffset(SqlSelect node) } } + public override void Visit(SqlFunctionCall node) + { + var arguments = node.Arguments; + switch (node.FunctionType) { + case SqlFunctionType.DateTimeConstruct: + Visit(SqlDml.FunctionCall("DATETIME2FROMPARTS", arguments[0], arguments[1], arguments[2], 0, 0, 0, 0, 7)); + //Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), + // arguments[0] - 2001), + // arguments[1] - 1), + // arguments[2] - 1)); + return; +#if NET6_0_OR_GREATER + case SqlFunctionType.DateConstruct: { + Visit(SqlDml.FunctionCall("DATEFROMPARTS", arguments[0], arguments[1], arguments[2])); + + //Visit(SqlDml.Cast(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), + // arguments[0] - 2001), + // arguments[1] - 1), + // arguments[2] - 1), SqlType.Date)); + return; + } + case SqlFunctionType.TimeConstruct: { + // argument[3] * 10000 operation is based on statement that millisaconds use 3 digits + // default precision of time is 7, and if we use raw argument[3] value the result will be .0000xxx, + // to prevent this and make fractions part valid .xxx0000 we multiply + Visit(SqlDml.FunctionCall("TIMEFROMPARTS", arguments[0], arguments[1], arguments[2], arguments[3] * 10000, 7)); + //Visit(SqlDml.Cast(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Literal(new TimeOnly(0, 0, 0)), + // arguments[0]), + // arguments[1]), + // arguments[2]), + // arguments[3]), SqlType.Time)); + return; + } +#endif + } + + base.Visit(node); + } + public Compiler(SqlDriver driver) : base(driver) { From 6e304c3ed84729779dc928fcb211a266da89b952 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Jan 2023 19:40:44 +0500 Subject: [PATCH 41/86] Firebird: DateConstruct/TimeConstruct support --- .../Sql.Drivers.Firebird/Constants.cs | 4 ++++ .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 16 ++++++++++++++++ .../Sql.Drivers.Firebird/v2_5/Translator.cs | 15 +++++++-------- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs index d0c47590a5..e8e976c3c1 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs @@ -14,5 +14,9 @@ internal static class Constants // cannot use "FFF" cause it may lead to empty string for fractions part. public const string DateTimeFormatString = @"''\'yyyy\.MM\.dd HH\:mm\:ss\.fff\'''"; +#if NET6_0_OR_GREATER //DO_DATEONLY + public const string DateFormatString = @"''\'yyyy\.MM\.dd\'''"; + public const string TimeFormatString = @"''\'HH\:mm\:ss\.fff\'''"; +#endif } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index a8496c86a9..67e3ed22b4 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -201,6 +201,22 @@ public override void Visit(SqlFunctionCall node) node.Arguments[1] - 1), node.Arguments[2] - 1)); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateOnly(2001, 1, 1)), SqlType.Date), + node.Arguments[0] - 2001), + node.Arguments[1] - 1), + node.Arguments[2] - 1)); + return; + case SqlFunctionType.TimeConstruct: { + Visit(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Cast(SqlDml.Literal(new TimeOnly(0, 0, 0)), SqlType.Time), + node.Arguments[0]), + node.Arguments[1]), + node.Arguments[2]), + node.Arguments[3])); + return; + } +#endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(node.Arguments[0])); return; diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs index e30a69e275..2e67c0e54d 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs @@ -19,15 +19,14 @@ namespace Xtensive.Sql.Drivers.Firebird.v2_5 { internal class Translator : SqlTranslator { - public override string DateTimeFormatString - { - get { return Constants.DateTimeFormatString; } - } + public override string DateTimeFormatString => Constants.DateTimeFormatString; - public override string TimeSpanFormatString - { - get { return string.Empty; } - } +#if NET6_0_OR_GREATER //DO_DATEONLY + public override string DateOnlyFormatString => Constants.DateFormatString; + public override string TimeOnlyFormatString => Constants.TimeFormatString; +#endif + + public override string TimeSpanFormatString => string.Empty; public override SqlHelper.EscapeSetup EscapeSetup => SqlHelper.EscapeSetup.WithQuotes; From 647daed7be175eae664c93865eea833b6055bff2 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 28 Jan 2023 20:05:35 +0500 Subject: [PATCH 42/86] MySQL: Add DateConstruct/TimeConstruct functions support - updated Nuget package of client library to one that has native reading/binding of DateOnly and TimeOnly values - when placeholder is of TimeOnly type then it is wrapped by sql function TIME(), MySQL has some issues with values passed via parameter, even if DbType of DbParameter is set correctly. - 5.6 and above uses built-in fuction MAKETIME instead of general approach, should be faster --- .../Sql.Drivers.MySql/v5_0/Compiler.cs | 79 +++++++++++++++---- .../Sql.Drivers.MySql/v5_0/Translator.cs | 8 ++ .../Sql.Drivers.MySql/v5_0/TypeMapper.cs | 33 +++++++- .../Sql.Drivers.MySql/v5_6/Compiler.cs | 26 +++++- .../Xtensive.Orm.MySql.csproj | 4 +- 5 files changed, 130 insertions(+), 20 deletions(-) diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs index 51afafdf9c..502fa30f8c 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs @@ -167,17 +167,32 @@ public override void Visit(SqlFunctionCall node) Visit(CastToLong(node.Arguments[0])); return; case SqlFunctionType.DateTimeAddMonths: - Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); + Visit(DateTimeAddMonth(node.Arguments[0], node.Arguments[1])); return; case SqlFunctionType.DateTimeAddYears: - Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); + Visit(DateTimeAddYear(node.Arguments[0], node.Arguments[1])); return; case SqlFunctionType.DateTimeConstruct: - Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), + Visit(DateTimeAddDay(DateTimeAddMonth(DateTimeAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), node.Arguments[0] - 2001), node.Arguments[1] - 1), node.Arguments[2] - 1)); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), + node.Arguments[0] - 2001), + node.Arguments[1] - 1), + node.Arguments[2] - 1)); + return; + case SqlFunctionType.TimeConstruct: + Visit(SqlDml.FunctionCall("TIME", TimeAddMillisecond(TimeAddSecond(TimeAddMinute(TimeAddHour(SqlDml.Literal(new DateTime(2001, 1, 1)), + node.Arguments[0]), + node.Arguments[1]), + node.Arguments[2]), + node.Arguments[3]))); + return; +#endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(node.Arguments[0])); return; @@ -186,6 +201,20 @@ public override void Visit(SqlFunctionCall node) base.Visit(node); } +#if NET6_0_OR_GREATER //DO_DATEONLY + public override void Visit(SqlPlaceholder node) + { + if(node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type==typeof(TimeOnly)) { + _ = context.Output.Append("TIME("); + base.Visit(node); + _ = context.Output.Append(")"); + } + else { + base.Visit(node); + } + } +#endif + /// protected override void VisitSelectLimitOffset(SqlSelect node) { @@ -217,13 +246,13 @@ protected virtual SqlExpression DateTimeSubtractDateTime(SqlExpression date1, Sq { return (CastToDecimal(DateDiffDay(date1, date2), 18, 0) * NanosecondsPerDay) + - (CastToDecimal(DateDiffMicrosecond(DateAddDay(date2, DateDiffDay(date1, date2)), date1), 18, 0) * NanosecondsPerMicrosecond); + (CastToDecimal(DateTimeDiffMicrosecond(DateTimeAddDay(date2, DateDiffDay(date1, date2)), date1), 18, 0) * NanosecondsPerMicrosecond); } protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpression interval) { - return DateAddMicrosecond( - DateAddDay(date, ((interval - (interval % NanosecondsPerDay)) + ((interval % NanosecondsPerDay) > (NanosecondsPerDay / 2) ? 0 : 1)) / NanosecondsPerDay), + return DateTimeAddMicrosecond( + DateTimeAddDay(date, ((interval - (interval % NanosecondsPerDay)) + ((interval % NanosecondsPerDay) > (NanosecondsPerDay / 2) ? 0 : 1)) / NanosecondsPerDay), (interval / NanosecondsPerMillisecond * NanosecondsPerMicrosecond) % (MillisecondsPerDay * NanosecondsPerMicrosecond)); } @@ -237,20 +266,42 @@ private static SqlCast CastToDecimal(SqlExpression arg, short precision, short s private static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) => SqlDml.FunctionCall("DATEDIFF", date1, date2); - private static SqlUserFunctionCall DateDiffMicrosecond(SqlExpression date1, SqlExpression date2) => - SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("MICROSECOND"), date1, date2); + private static SqlUserFunctionCall DateTimeDiffMicrosecond(SqlExpression datetime1, SqlExpression datetime2) => + SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("MICROSECOND"), datetime1, datetime2); + + private static SqlUserFunctionCall DateTimeAddYear(SqlExpression datetime, SqlExpression years) => + SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("YEAR"), years, datetime); + + private static SqlUserFunctionCall DateTimeAddMonth(SqlExpression datetime, SqlExpression months) => + SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MONTH"), months, datetime); + + private static SqlUserFunctionCall DateTimeAddDay(SqlExpression datetime, SqlExpression days) => + SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("DAY"), days, datetime); + private static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) => + SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, datetime); + +#if NET6_0_OR_GREATER //DO_DATEONLY private static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) => - SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("YEAR"), years, date); + SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(years, SqlDml.Native("YEAR")))); private static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) => - SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MONTH"), months, date); - + SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(months, SqlDml.Native("MONTH")))); private static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) => - SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("DAY"), days, date); + SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(days, SqlDml.Native("DAY")))); + + private static SqlUserFunctionCall TimeAddHour(SqlExpression time, SqlExpression hours) => + SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(hours, SqlDml.Native("HOUR")))); + + private static SqlUserFunctionCall TimeAddMinute(SqlExpression time, SqlExpression minutes) => + SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(minutes, SqlDml.Native("MINUTE")))); + + private static SqlUserFunctionCall TimeAddSecond(SqlExpression time, SqlExpression seconds) => + SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(seconds, SqlDml.Native("SECOND")))); - private static SqlUserFunctionCall DateAddMicrosecond(SqlExpression date, SqlExpression microseconds) => - SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, date); + private static SqlUserFunctionCall TimeAddMillisecond(SqlExpression time, SqlExpression millisecond) => + SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(millisecond * 1000, SqlDml.Native("MICROSECOND")))); +#endif protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) => SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%Y-%m-%dT%T"); diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs index c2576d2dd1..35b67517d6 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs @@ -22,6 +22,10 @@ namespace Xtensive.Sql.Drivers.MySql.v5_0 internal class Translator : SqlTranslator { public override string DateTimeFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss\.ffffff\'"; +#if NET6_0_OR_GREATER //DO_DATEONLY + public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'"; + public override string TimeOnlyFormatString => @"\'HH\:mm\:ss\.ffffff\'"; +#endif public override string TimeSpanFormatString => string.Empty; @@ -106,6 +110,10 @@ public override void Translate(IOutput output, SqlFunctionType type) case SqlFunctionType.DateTimeAddYears: case SqlFunctionType.DateTimeAddMonths: case SqlFunctionType.DateTimeConstruct: +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + case SqlFunctionType.TimeConstruct: +#endif case SqlFunctionType.IntervalToMilliseconds: return; //string diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs index a3550e672e..e2e2128241 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2021 Xtensive LLC. +// Copyright (C) 2011-2023 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 @@ -14,7 +14,11 @@ namespace Xtensive.Sql.Drivers.MySql.v5_0 { internal class TypeMapper : Sql.TypeMapper { +#if NET6_0_OR_GREATER //DO_DATEONLY + private static readonly Type[] CastRequiredTypes = new[] { typeof(Guid), typeof(TimeSpan), typeof(byte[]), typeof (DateOnly), typeof(TimeOnly) }; +#else private static readonly Type[] CastRequiredTypes = new[] { typeof(Guid), typeof(TimeSpan), typeof(byte[]) }; +#endif /// public override bool IsParameterCastRequired(Type type) @@ -63,6 +67,20 @@ public override void BindGuid(DbParameter parameter, object value) parameter.Value = value==null ? (object) DBNull.Value : SqlHelper.GuidToString((Guid) value); } +#if NET6_0_OR_GREATER //DO_DATEONLY + public override void BindDateOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.Date; + parameter.Value = value ?? DBNull.Value; + } + + public override void BindTimeOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.Time; + parameter.Value = value ?? DBNull.Value; + } +#endif + /// public override SqlValueType MapByte(int? length, int? precision, int? scale) { @@ -111,6 +129,19 @@ public override object ReadGuid(DbDataReader reader, int index) return SqlHelper.GuidFromString(reader.GetString(index)); } +#if NET6_0_OR_GREATER + public override object ReadDateOnly(DbDataReader reader, int index) + { + return reader.GetFieldValue(index); + } + + public override object ReadTimeOnly(DbDataReader reader, int index) + { + return reader.GetFieldValue(index); + } + +#endif + // Constructors [SecuritySafeCritical] diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs index e35b3aebeb..9a045bbc00 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs @@ -1,13 +1,33 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2013-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alena Mikshina // Created: 2013.12.30 +using System; +using Xtensive.Sql.Dml; + namespace Xtensive.Sql.Drivers.MySql.v5_6 { internal class Compiler : v5_5.Compiler { +#if NET6_0_OR_GREATER //DO_DATEONLY + public override void Visit(SqlFunctionCall node) + { + if (node.FunctionType == SqlFunctionType.TimeConstruct) { + var arguments = node.Arguments; + Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3])); + return; + } + base.Visit(node); + } + + protected SqlUserFunctionCall MakeTime( + SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) => + SqlDml.FunctionCall("MAKETIME", hours, minutes, seconds + (milliseconds / 1000)); + +#endif + // Constructors public Compiler(SqlDriver driver) diff --git a/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj b/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj index 30a4faa26c..89d9852d80 100644 --- a/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj +++ b/Orm/Xtensive.Orm.MySql/Xtensive.Orm.MySql.csproj @@ -30,8 +30,8 @@ - - + + From 3e825c1fff9792315a28c7a0ec35238722ccc1e6 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sun, 29 Jan 2023 17:24:16 +0500 Subject: [PATCH 43/86] Oracle: Add DateConstruct/TimeConstruct functions support Additionally, changed type mapping of Interval type from 'interval day to second' to 'inteval day(2) to second(6)' which are equivalents but the last one shows precisions explicitly --- .../Sql.Drivers.Oracle/v09/Compiler.cs | 31 +++++++++++++++++-- .../v09/ServerInfoProvider.cs | 7 ++--- .../Sql.Drivers.Oracle/v09/Translator.cs | 5 ++- .../Sql.Drivers.Oracle/v09/TypeMapper.cs | 18 +++++++++++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index 6f8a73d14e..4d2c2135c3 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.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-2023 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.07.17 @@ -41,6 +41,15 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeConstruct: DateTimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + DateConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeConstruct: { + TimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2], node.Arguments[3]).AcceptVisitor(this); + return; + } +#endif case SqlFunctionType.IntervalAbs: SqlHelper.IntervalAbs(node.Arguments[0]).AcceptVisitor(this); return; @@ -270,6 +279,22 @@ private static SqlExpression DateTimeConstruct(SqlExpression years, SqlExpressio AnsiString("YYYYMMDD")); } +#if NET6_0_OR_GREATER //DO_DATEONLY + private static SqlExpression DateConstruct(SqlExpression years, SqlExpression months, SqlExpression days) + { + return SqlDml.FunctionCall("TO_DATE", + SqlDml.FunctionCall("TO_CHAR", ((years * 100) + months) * 100 + days), + AnsiString("YYYYMMDD")); + } + + private static SqlExpression TimeConstruct(SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) + { + return SqlDml.FunctionCall("NUMTODSINTERVAL", + seconds + (minutes * 60) + (hours * 3600) + (milliseconds / 1000), + AnsiString("second")); + } +#endif + private static SqlExpression DateTimeExtractDayOfWeek(SqlExpression dateTime) { // TO_CHAR with 'D' returns values depending on NLS_TERRITORY setting, diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs index d05bd938bb..cdb9d403c9 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs @@ -228,17 +228,16 @@ public override DataTypeCollection GetDataTypesInfo() types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, ValueRange.DateTime, "timestamp"); -#if NET6_0_OR_GREATER +#if NET6_0_OR_GREATER //DO_DATEONLY types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, ValueRange.DateOnly, "DATE"); types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, - ValueRange.TimeOnly, "interval day(0) to second"); + ValueRange.TimeOnly, "interval day(0) to second(6)"); #endif - types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index, ValueRange.DateTimeOffset, "TIMESTAMP WITH TIME ZONE"); types.Interval = DataTypeInfo.Range(SqlType.Interval, common | index, - ValueRange.TimeSpan, "interval day to second"); + ValueRange.TimeSpan, "interval day(2) to second(6)"); types.Char = DataTypeInfo.Stream(SqlType.Char, common | index | DataTypeFeatures.ZeroLengthValueIsNull, 2000, "nchar"); diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index 57a0d54708..dff4fe4f61 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2022 Xtensive LLC. +// Copyright (C) 2009-2023 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 @@ -33,6 +33,9 @@ internal class Translator : SqlTranslator #if NET6_0_OR_GREATER //DO_DATEONLY /// public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)"; + + /// + public override string TimeOnlyFormatString => @"'(INTERVAL '\'0 HH\:mm\:ss\.fffff\'\ DAY(0) TO SECOND(6))"; #endif /// diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs index c6b0802042..79b55b8400 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs @@ -88,6 +88,24 @@ public override void BindDouble(DbParameter parameter, object value) nativeParameter.Value = value ?? DBNull.Value; } +#if NET6_0_OR_GREATER //DO_DATEONLY + public override void BindDateOnly(DbParameter parameter, object value) + { + var nativeParameter = (OracleParameter) parameter; + nativeParameter.OracleDbType = OracleDbType.Date; + nativeParameter.Value = value == null + ? (object) DBNull.Value + : new OracleDate(((DateOnly) value).ToDateTime(TimeOnly.MinValue)); + } + + public override void BindTimeOnly(DbParameter parameter, object value) + { + var nativeParameter = (OracleParameter) parameter; + nativeParameter.OracleDbType = OracleDbType.IntervalDS; + nativeParameter.Value = value == null ? (object) DBNull.Value : new OracleIntervalDS(((TimeOnly) value).ToTimeSpan()); + } +#endif + public override void BindDateTimeOffset(DbParameter parameter, object value) { var nativeParameter = (OracleParameter) parameter; From a2bbcbc0c59010052c47b02b7bf24efa7cb4b49f Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sun, 29 Jan 2023 19:02:40 +0500 Subject: [PATCH 44/86] PgSQL: Add support for DateConstruct/TimeConstruct functions --- .../Sql.Drivers.PostgreSql/v8_0/Compiler.cs | 17 +++++++++++++++++ .../Sql.Drivers.PostgreSql/v8_0/Translator.cs | 10 +++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs index 4d278826b1..60e340e580 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs @@ -18,6 +18,8 @@ internal class Compiler : SqlCompiler private static readonly SqlNative OneYearInterval = SqlDml.Native("interval '1 year'"); private static readonly SqlNative OneMonthInterval = SqlDml.Native("interval '1 month'"); private static readonly SqlNative OneDayInterval = SqlDml.Native("interval '1 day'"); + + private static readonly SqlNative OneHourInterval = SqlDml.Native("interval '1 hour'"); private static readonly SqlNative OneMinuteInterval = SqlDml.Native("interval '1 minute'"); private static readonly SqlNative OneSecondInterval = SqlDml.Native("interval '1 second'"); @@ -106,6 +108,21 @@ public override void Visit(SqlFunctionCall node) + (OneDayInterval * (node.Arguments[2] - 1)); newNode.AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + (SqlDml.Literal(new DateOnly(2001, 1, 1)) + + (OneYearInterval * (node.Arguments[0] - 2001)) + + (OneMonthInterval * (node.Arguments[1] - 1)) + + (OneDayInterval * (node.Arguments[2] - 1))).AcceptVisitor(this); + return; + case SqlFunctionType.TimeConstruct: { + ((SqlDml.Literal(new TimeOnly(0, 0, 0)) + + (OneHourInterval * (node.Arguments[0])) + + (OneMinuteInterval * (node.Arguments[1])) + + (OneSecondInterval * (node.Arguments[2] + (SqlDml.Cast(node.Arguments[3], SqlType.Double) / 1000))))).AcceptVisitor(this); + return; + } +#endif case SqlFunctionType.DateTimeTruncate: (SqlDml.FunctionCall("date_trunc", "day", node.Arguments[0])).AcceptVisitor(this); return; diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs index 852f4b9c79..dce017dd35 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2022 Xtensive LLC. +// Copyright (C) 2012-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. @@ -53,6 +53,14 @@ public SqlFunctionTypeTranslations(int count) /// public override string DateTimeFormatString => @"\'yyyyMMdd HHmmss.ffffff\''::timestamp(6)'"; +#if NET6_0_OR_GREATER //DO_DATEONLY + + /// + public override string DateOnlyFormatString => @"\'yyyyMMdd\''::date'"; + + /// + public override string TimeOnlyFormatString => @"\'HH:mm:ss.ffffff\''::time'"; +#endif /// public override string TimeSpanFormatString => "'{0}{1} days {0}{2}:{3}:{4}.{5:000}'::interval"; From 197ce00f555b12e8d6c45dfed59a36dfc99c18a3 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 30 Jan 2023 19:02:13 +0500 Subject: [PATCH 45/86] Sqlite: Support for DateConstruct/TimeConstruct Also, reading/writing of DateOnly/TimeOnly values done correctly --- .../Sql.Drivers.Sqlite/v3/Compiler.cs | 87 +++++++++++++------ .../Sql.Drivers.Sqlite/v3/Translator.cs | 10 ++- .../Sql.Drivers.Sqlite/v3/TypeMapper.cs | 51 ++++++++++- 3 files changed, 120 insertions(+), 28 deletions(-) diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index cf044ff98e..c5e7974e52 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2022 Xtensive LLC. +// Copyright (C) 2011-2023 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 @@ -23,7 +23,9 @@ internal class Compiler : SqlCompiler private static readonly int StartOffsetIndex = DateTimeOffsetExampleString.IndexOf('+'); private const long NanosecondsPerMillisecond = 1000000L; - private const string DateFormat = "%Y-%m-%d 00:00:00.000"; + private const string DateWithZeroTimeFormat = "%Y-%m-%d 00:00:00.000"; + private const string DateFormat = "%Y-%m-%d"; + private const string TimeFormat = "%H:%M:%f"; private const string DateTimeFormat = "%Y-%m-%d %H:%M:%f"; private const string DateTimeIsoFormat = "%Y-%m-%dT%H:%M:%S"; private const string DateTimeOffsetExampleString = "2001-02-03 04:05:06.789+02.45"; @@ -131,15 +133,16 @@ public override void Visit(SqlExtract node) /// public override void Visit(SqlFunctionCall node) { + var arguments = node.Arguments; switch (node.FunctionType) { case SqlFunctionType.CharLength: - (SqlDml.FunctionCall("LENGTH", node.Arguments) / 2).AcceptVisitor(this); + (SqlDml.FunctionCall("LENGTH", arguments) / 2).AcceptVisitor(this); return; case SqlFunctionType.PadLeft: case SqlFunctionType.PadRight: return; case SqlFunctionType.Concat: - var nod = node.Arguments[0]; + var nod = arguments[0]; return; case SqlFunctionType.Round: // Round should always be called with 2 arguments @@ -149,40 +152,54 @@ public override void Visit(SqlFunctionCall node) } break; case SqlFunctionType.Truncate: - Visit(CastToLong(node.Arguments[0])); + Visit(CastToLong(arguments[0])); return; case SqlFunctionType.IntervalConstruct: - Visit(CastToLong(node.Arguments[0])); + Visit(CastToLong(arguments[0])); return; case SqlFunctionType.IntervalToNanoseconds: - Visit(CastToLong(node.Arguments[0])); + Visit(CastToLong(arguments[0])); return; case SqlFunctionType.IntervalToMilliseconds: - Visit(CastToLong(node.Arguments[0] / NanosecondsPerMillisecond)); + Visit(CastToLong(arguments[0] / NanosecondsPerMillisecond)); return; case SqlFunctionType.DateTimeAddMonths: - DateAddMonth(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); + DateTimeAddMonth(arguments[0], arguments[1]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeAddYears: - DateAddYear(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); + DateTimeAddYear(arguments[0], arguments[1]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeTruncate: - DateTimeTruncate(node.Arguments[0]).AcceptVisitor(this); + DateTimeTruncate(arguments[0]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeConstruct: - DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), - node.Arguments[0] - 2001), - node.Arguments[1] - 1), - node.Arguments[2] - 1).AcceptVisitor(this); - return; + DateTimeAddDay(DateTimeAddMonth(DateTimeAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1).AcceptVisitor(this); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1).AcceptVisitor(this); + return; + case SqlFunctionType.TimeConstruct: + TimeAddSeconds(TimeAddMinutes(TimeAddHours(SqlDml.Literal(new TimeOnly(0, 0, 0, 0)), + arguments[0]), + arguments[1]), + arguments[2], arguments[3]).AcceptVisitor(this); + return; +#endif case SqlFunctionType.DateTimeToStringIso: DateTimeToStringIso(node.Arguments[0]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetAddMonths: - SqlDml.Concat(DateAddMonth(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), node.Arguments[1]), DateTimeOffsetExtractOffsetAsString(node.Arguments[0])).AcceptVisitor(this); + SqlDml.Concat(DateTimeAddMonth(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), node.Arguments[1]), DateTimeOffsetExtractOffsetAsString(node.Arguments[0])).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetAddYears: - SqlDml.Concat(DateAddYear(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), node.Arguments[1]), DateTimeOffsetExtractOffsetAsString(node.Arguments[0])).AcceptVisitor(this); + SqlDml.Concat(DateTimeAddYear(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), node.Arguments[1]), DateTimeOffsetExtractOffsetAsString(node.Arguments[0])).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetConstruct: SqlDml.Concat(node.Arguments[0], OffsetToOffsetAsString(node.Arguments[1])).AcceptVisitor(this); @@ -351,10 +368,10 @@ private void VisitDateTimeOffset(SqlExtract node) } private static SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpression interval) => - DateAddSeconds(date, interval / Convert.ToDouble(NanosecondsPerSecond)); + DateTimeAddSeconds(date, interval / Convert.ToDouble(NanosecondsPerSecond)); private static SqlExpression DateTimeTruncate(SqlExpression date) => - DateTime(SqlDml.FunctionCall("STRFTIME", DateFormat, date)); + DateTime(SqlDml.FunctionCall("STRFTIME", DateWithZeroTimeFormat, date)); private static SqlExpression DateTime(SqlExpression date) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, date); @@ -405,20 +422,38 @@ private static SqlExpression DateTimeOffsetToLocalDateTime(SqlExpression dateTim private static SqlExpression DateTimeToStringIso(SqlExpression dateTime) => SqlDml.FunctionCall("STRFTIME", DateTimeIsoFormat, dateTime); - private static SqlExpression DateAddYear(SqlExpression date, SqlExpression years) => + private static SqlExpression DateTimeAddYear(SqlExpression date, SqlExpression years) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, date, SqlDml.Concat(years, " ", "YEARS")); - - private static SqlExpression DateAddMonth(SqlExpression date, SqlExpression months) => + private static SqlExpression DateTimeAddMonth(SqlExpression date, SqlExpression months) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, date, SqlDml.Concat(months, " ", "MONTHS")); - private static SqlExpression DateAddDay(SqlExpression date, SqlExpression days) => + private static SqlExpression DateTimeAddDay(SqlExpression date, SqlExpression days) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, date, SqlDml.Concat(days, " ", "DAYS")); - - private static SqlExpression DateAddSeconds(SqlExpression date, SqlExpression seconds) => + private static SqlExpression DateTimeAddSeconds(SqlExpression date, SqlExpression seconds) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, date, SqlDml.Concat(seconds, " ", "SECONDS")); +#if NET6_0_OR_GREATER // DO_DATEONLY + private static SqlExpression DateAddYear(SqlExpression date, SqlExpression years) => + SqlDml.FunctionCall("STRFTIME", DateFormat, date, SqlDml.Concat(years, " ", "YEARS")); + + private static SqlExpression DateAddMonth(SqlExpression date, SqlExpression months) => + SqlDml.FunctionCall("STRFTIME", DateFormat, date, SqlDml.Concat(months, " ", "MONTHS")); + + private static SqlExpression DateAddDay(SqlExpression date, SqlExpression days) => + SqlDml.FunctionCall("STRFTIME", DateFormat, date, SqlDml.Concat(days, " ", "DAYS")); + + private static SqlExpression TimeAddHours(SqlExpression date, SqlExpression seconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, " ", "HOURS")); + + private static SqlExpression TimeAddMinutes(SqlExpression date, SqlExpression seconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, " ", "MINUTES")); + + private static SqlExpression TimeAddSeconds(SqlExpression date, SqlExpression seconds, SqlExpression milliseconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, ".", milliseconds, " ", "SECONDS")); +#endif + private static SqlExpression DateGetMilliseconds(SqlExpression date) => CastToLong(SqlDml.FunctionCall("STRFTIME", "%f", date) * MillisecondsPerSecond) - CastToLong(SqlDml.FunctionCall("STRFTIME", "%S", date) * MillisecondsPerSecond); diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs index 737c31aedf..5e09534777 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2022 Xtensive LLC. +// Copyright (C) 2011-2023 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 @@ -22,6 +22,14 @@ internal class Translator : SqlTranslator /// public override string DateTimeFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss.fff\'"; +#if NET6_0_OR_GREATER //DO_DATEONLY + + /// + public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'"; + + /// + public override string TimeOnlyFormatString => @"\'HH\:mm\:ss.fff\'"; +#endif public virtual string DateTimeOffsetFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss.fffK\'"; diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs index 900b64d8c9..97d47ebdcd 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2021 Xtensive LLC. +// Copyright (C) 2011-2023 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 @@ -9,17 +9,24 @@ using System.Data.Common; using System.Data.SQLite; using System.Globalization; +using System.Security.AccessControl; using Xtensive.Sql.Info; namespace Xtensive.Sql.Drivers.Sqlite.v3 { internal class TypeMapper : Sql.TypeMapper { +#if NET6_0_OR_GREATER + private ValueRange dateOnlyRange; + private ValueRange timeOnlyRange; +#endif private ValueRange dateTimeRange; private ValueRange dateTimeOffsetRange; private const string DateTimeOffsetFormat = "yyyy-MM-dd HH:mm:ss.fffK"; private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fffffff"; + private const string DateFormat = "yyyy-MM-dd"; + private const string TimeFormat = "HH:mm:ss.fff"; public override object ReadBoolean(DbDataReader reader, int index) { @@ -27,6 +34,20 @@ public override object ReadBoolean(DbDataReader reader, int index) return SQLiteConvert.ToBoolean(value); } +#if NET6_0_OR_GREATER + public override object ReadDateOnly(DbDataReader reader, int index) + { + var value = reader.GetString(index); + return DateOnly.ParseExact(value, DateTimeOffsetFormat, CultureInfo.InvariantCulture); + } + + public override object ReadTimeOnly(DbDataReader reader, int index) + { + var value = reader.GetString(index); + return TimeOnly.ParseExact(value, DateTimeOffsetFormat, CultureInfo.InvariantCulture); + } +#endif + public override object ReadDateTimeOffset(DbDataReader reader, int index) { var value = reader.GetString(index); @@ -68,6 +89,30 @@ public override void BindDateTime(DbParameter parameter, object value) parameter.Value = correctValue.ToString(DateTimeFormat, CultureInfo.InvariantCulture); } +#if NET6_0_OR_GREATER //DO_DATEONLY + public override void BindDateOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.String; + if (value == null) { + parameter.Value = DBNull.Value; + return; + } + var correctValue = ValueRangeValidator.Correct((DateOnly) value, dateOnlyRange); + parameter.Value = correctValue.ToString(DateFormat, CultureInfo.InvariantCulture); + } + + public override void BindTimeOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.String; + if (value == null) { + parameter.Value = DBNull.Value; + return; + } + var correctValue = ValueRangeValidator.Correct((TimeOnly) value, timeOnlyRange); + parameter.Value = correctValue.ToString(TimeFormat, CultureInfo.InvariantCulture); + } +#endif + public override void BindDateTimeOffset(DbParameter parameter, object value) { parameter.DbType = DbType.String; @@ -129,6 +174,10 @@ public override void Initialize() base.Initialize(); dateTimeRange = (ValueRange) Driver.ServerInfo.DataTypes.DateTime.ValueRange; dateTimeOffsetRange = (ValueRange) Driver.ServerInfo.DataTypes.DateTimeOffset.ValueRange; +#if NET6_0_OR_GREATER + dateOnlyRange = (ValueRange) Driver.ServerInfo.DataTypes.DateOnly.ValueRange; + timeOnlyRange = (ValueRange) Driver.ServerInfo.DataTypes.TimeOnly.ValueRange; +#endif } // Constructors From 47eab77c85a478bb6483370a51e479a871172cfc Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 2 Feb 2023 20:57:45 +0500 Subject: [PATCH 46/86] SqlExtract changes - Dedicated SqlXxxParts for time and date - SqlExtract node stores the widest enum - SqlDateTimeOffsetPart, other enums share the same values of parts so it is fast-convertible from storable enum and to it, to track actual types of argument (date, time, datetime, datetimeoffset, interval) there is a marker, added some calculated properties which could improve code on translation - SqlDml has overloads with SqlTimePart/SqlDatePart, which is more reliable and error-proof than using SqlDateTimePart, SqlDateTimePart can't even be validated to prevent bad sql --- .../SqlDateTimePartsTest.cs | 227 ++++++++++++++++++ .../Sql/Dml/Expressions/SqlExtract.cs | 134 +++++++++-- Orm/Xtensive.Orm/Sql/Dml/Extensions.cs | 15 ++ Orm/Xtensive.Orm/Sql/Dml/SqlDatePart.cs | 46 ++++ .../Sql/Dml/SqlDateTimeOffsetPart.cs | 66 +++-- Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs | 50 +++- Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs | 44 +++- Orm/Xtensive.Orm/Sql/Dml/SqlTimePart.cs | 45 ++++ Orm/Xtensive.Orm/Sql/SqlDml.cs | 34 ++- 9 files changed, 588 insertions(+), 73 deletions(-) create mode 100644 Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs create mode 100644 Orm/Xtensive.Orm/Sql/Dml/SqlDatePart.cs create mode 100644 Orm/Xtensive.Orm/Sql/Dml/SqlTimePart.cs diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs new file mode 100644 index 0000000000..b0008246e3 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs @@ -0,0 +1,227 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using NUnit.Framework; +using Xtensive.Sql.Dml; + +namespace Xtensive.Orm.Sql.Tests +{ + [TestFixture] + public class SqlDateTimePartsTest + { + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + + [Test] + public void MainTest() + { + var validNames = new HashSet() { + "Year", "Month", "Day", "Hour", + "Minute", "Second", "Millisecond", "Nanosecond", + "TimeZoneHour", "TimeZoneMinute", + "DayOfYear", "DayOfWeek", + "Date", "DateTime", + "LocalDateTime","UtcDateTime", "Offset", + "Nothing", + }; + + var enums = typeof(SqlDateTimeOffsetPart).Assembly.GetTypes() + .Where(t => t.Namespace == "Xtensive.Sql.Dml" && t.IsEnum && (t.Name.StartsWith("Sql") && t.Name.EndsWith("Part"))) + .ToList(); + foreach (var @enum in enums) { + foreach(var name in Enum.GetNames(@enum)) { + Assert.That(validNames.Contains(name), $"Does the enum {@enum.Name} have new item?"); + } + } + } + + [Test] + public void DateTimeOffsetPartsValueTest() + { + Assert.That((int) SqlDateTimeOffsetPart.Year, Is.EqualTo(0)); + Assert.That((int) SqlDateTimeOffsetPart.Month, Is.EqualTo(1)); + Assert.That((int) SqlDateTimeOffsetPart.Day, Is.EqualTo(2)); + Assert.That((int) SqlDateTimeOffsetPart.Hour, Is.EqualTo(3)); + Assert.That((int) SqlDateTimeOffsetPart.Minute, Is.EqualTo(4)); + Assert.That((int) SqlDateTimeOffsetPart.Second, Is.EqualTo(5)); + Assert.That((int) SqlDateTimeOffsetPart.Millisecond, Is.EqualTo(6)); + Assert.That((int) SqlDateTimeOffsetPart.Nanosecond, Is.EqualTo(7)); + Assert.That((int) SqlDateTimeOffsetPart.TimeZoneHour, Is.EqualTo(8)); + Assert.That((int) SqlDateTimeOffsetPart.TimeZoneMinute, Is.EqualTo(9)); + Assert.That((int) SqlDateTimeOffsetPart.DayOfYear, Is.EqualTo(10)); + Assert.That((int) SqlDateTimeOffsetPart.DayOfWeek, Is.EqualTo(11)); + Assert.That((int) SqlDateTimeOffsetPart.Date, Is.EqualTo(12)); + Assert.That((int) SqlDateTimeOffsetPart.DateTime, Is.EqualTo(13)); + Assert.That((int) SqlDateTimeOffsetPart.LocalDateTime, Is.EqualTo(14)); + Assert.That((int) SqlDateTimeOffsetPart.UtcDateTime, Is.EqualTo(15)); + Assert.That((int) SqlDateTimeOffsetPart.Offset, Is.EqualTo(16)); + Assert.That((int) SqlDateTimeOffsetPart.Nothing, Is.EqualTo(25)); + } + + [Test] + public void DateTimePartsValueTest() + { + Assert.That((int) SqlDateTimePart.Year, Is.EqualTo(0)); + Assert.That((int) SqlDateTimePart.Month, Is.EqualTo(1)); + Assert.That((int) SqlDateTimePart.Day, Is.EqualTo(2)); + Assert.That((int) SqlDateTimePart.Hour, Is.EqualTo(3)); + Assert.That((int) SqlDateTimePart.Minute, Is.EqualTo(4)); + Assert.That((int) SqlDateTimePart.Second, Is.EqualTo(5)); + Assert.That((int) SqlDateTimePart.Millisecond, Is.EqualTo(6)); + Assert.That((int) SqlDateTimePart.Nanosecond, Is.EqualTo(7)); + Assert.That((int) SqlDateTimePart.TimeZoneHour, Is.EqualTo(8)); + Assert.That((int) SqlDateTimePart.TimeZoneMinute, Is.EqualTo(9)); + Assert.That((int) SqlDateTimePart.DayOfYear, Is.EqualTo(10)); + Assert.That((int) SqlDateTimePart.DayOfWeek, Is.EqualTo(11)); + Assert.That((int) SqlDateTimePart.Nothing, Is.EqualTo(25)); + } + +#if NET6_0_OR_GREATER + [Test] + public void DatePartsValueTest() + { + Assert.That((int) SqlDatePart.Year, Is.EqualTo(0)); + Assert.That((int) SqlDatePart.Month, Is.EqualTo(1)); + Assert.That((int) SqlDatePart.Day, Is.EqualTo(2)); + Assert.That((int) SqlDatePart.DayOfYear, Is.EqualTo(10)); + Assert.That((int) SqlDatePart.DayOfWeek, Is.EqualTo(11)); + Assert.That((int) SqlDatePart.Nothing, Is.EqualTo(25)); + } + + [Test] + public void TimePartsValueTest() + { + Assert.That((int) SqlTimePart.Hour, Is.EqualTo(3)); + Assert.That((int) SqlTimePart.Minute, Is.EqualTo(4)); + Assert.That((int) SqlTimePart.Second, Is.EqualTo(5)); + Assert.That((int) SqlTimePart.Millisecond, Is.EqualTo(6)); + Assert.That((int) SqlTimePart.Nanosecond, Is.EqualTo(7)); + Assert.That((int) SqlTimePart.Nothing, Is.EqualTo(25)); + } +#endif + + [Test] + public void IntervalPartsValueTest() + { + Assert.That((int) SqlIntervalPart.Day, Is.EqualTo(2)); + Assert.That((int) SqlIntervalPart.Hour, Is.EqualTo(3)); + Assert.That((int) SqlIntervalPart.Minute, Is.EqualTo(4)); + Assert.That((int) SqlIntervalPart.Second, Is.EqualTo(5)); + Assert.That((int) SqlIntervalPart.Millisecond, Is.EqualTo(6)); + Assert.That((int) SqlIntervalPart.Nanosecond, Is.EqualTo(7)); + + Assert.That((int) SqlIntervalPart.Nothing, Is.EqualTo(25)); + } + + [Test] + public void DateTimePartConversionTest() + { + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Year, Is.EqualTo(SqlDateTimePart.Year)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Month, Is.EqualTo(SqlDateTimePart.Month)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Day, Is.EqualTo(SqlDateTimePart.Day)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Hour, Is.EqualTo(SqlDateTimePart.Hour)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Minute, Is.EqualTo(SqlDateTimePart.Minute)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Second, Is.EqualTo(SqlDateTimePart.Second)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Millisecond, Is.EqualTo(SqlDateTimePart.Millisecond)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Nanosecond, Is.EqualTo(SqlDateTimePart.Nanosecond)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.TimeZoneHour, Is.EqualTo(SqlDateTimePart.TimeZoneHour)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.TimeZoneMinute, Is.EqualTo(SqlDateTimePart.TimeZoneMinute)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.DayOfYear, Is.EqualTo(SqlDateTimePart.DayOfYear)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.DayOfWeek, Is.EqualTo(SqlDateTimePart.DayOfWeek)); + Assert.That((SqlDateTimePart) (int) SqlDateTimeOffsetPart.Nothing, Is.EqualTo(SqlDateTimePart.Nothing)); + + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Year, Is.EqualTo(SqlDateTimeOffsetPart.Year)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Month, Is.EqualTo(SqlDateTimeOffsetPart.Month)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Day, Is.EqualTo(SqlDateTimeOffsetPart.Day)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Hour, Is.EqualTo(SqlDateTimeOffsetPart.Hour)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Minute, Is.EqualTo(SqlDateTimeOffsetPart.Minute)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Second, Is.EqualTo(SqlDateTimeOffsetPart.Second)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Millisecond, Is.EqualTo(SqlDateTimeOffsetPart.Millisecond)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Nanosecond, Is.EqualTo(SqlDateTimeOffsetPart.Nanosecond)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.TimeZoneHour, Is.EqualTo(SqlDateTimeOffsetPart.TimeZoneHour)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.TimeZoneMinute, Is.EqualTo(SqlDateTimeOffsetPart.TimeZoneMinute)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.DayOfYear, Is.EqualTo(SqlDateTimeOffsetPart.DayOfYear)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.DayOfWeek, Is.EqualTo(SqlDateTimeOffsetPart.DayOfWeek)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDateTimePart.Nothing, Is.EqualTo(SqlDateTimeOffsetPart.Nothing)); + } +#if NET6_0_OR_GREATER + + [Test] + public void DatePartConversionTest() + { + Assert.That((SqlDatePart) (int) SqlDateTimeOffsetPart.Year, Is.EqualTo(SqlDatePart.Year)); + Assert.That((SqlDatePart) (int) SqlDateTimeOffsetPart.Month, Is.EqualTo(SqlDatePart.Month)); + Assert.That((SqlDatePart) (int) SqlDateTimeOffsetPart.Day, Is.EqualTo(SqlDatePart.Day)); + Assert.That((SqlDatePart) (int) SqlDateTimeOffsetPart.DayOfYear, Is.EqualTo(SqlDatePart.DayOfYear)); + Assert.That((SqlDatePart) (int) SqlDateTimeOffsetPart.DayOfWeek, Is.EqualTo(SqlDatePart.DayOfWeek)); + Assert.That((SqlDatePart) (int) SqlDateTimeOffsetPart.Nothing, Is.EqualTo(SqlDatePart.Nothing)); + + Assert.That((SqlDateTimeOffsetPart) (int) SqlDatePart.Year, Is.EqualTo(SqlDateTimeOffsetPart.Year)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDatePart.Month, Is.EqualTo(SqlDateTimeOffsetPart.Month)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDatePart.Day, Is.EqualTo(SqlDateTimeOffsetPart.Day)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDatePart.DayOfYear, Is.EqualTo(SqlDateTimeOffsetPart.DayOfYear)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDatePart.DayOfWeek, Is.EqualTo(SqlDateTimeOffsetPart.DayOfWeek)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlDatePart.Nothing, Is.EqualTo(SqlDateTimeOffsetPart.Nothing)); + } + + [Test] + public void TimePartConversionTest() + { + Assert.That((SqlTimePart) (int) SqlDateTimeOffsetPart.Hour, Is.EqualTo(SqlTimePart.Hour)); + Assert.That((SqlTimePart) (int) SqlDateTimeOffsetPart.Minute, Is.EqualTo(SqlTimePart.Minute)); + Assert.That((SqlTimePart) (int) SqlDateTimeOffsetPart.Second, Is.EqualTo(SqlTimePart.Second)); + Assert.That((SqlTimePart) (int) SqlDateTimeOffsetPart.Millisecond, Is.EqualTo(SqlTimePart.Millisecond)); + Assert.That((SqlTimePart) (int) SqlDateTimeOffsetPart.Nanosecond, Is.EqualTo(SqlTimePart.Nanosecond)); + Assert.That((SqlTimePart) (int) SqlDateTimeOffsetPart.Nothing, Is.EqualTo(SqlTimePart.Nothing)); + + Assert.That((SqlDateTimeOffsetPart) (int) SqlTimePart.Hour, Is.EqualTo(SqlDateTimeOffsetPart.Hour)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlTimePart.Minute, Is.EqualTo(SqlDateTimeOffsetPart.Minute)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlTimePart.Second, Is.EqualTo(SqlDateTimeOffsetPart.Second)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlTimePart.Millisecond, Is.EqualTo(SqlDateTimeOffsetPart.Millisecond)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlTimePart.Nanosecond, Is.EqualTo(SqlDateTimeOffsetPart.Nanosecond)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlTimePart.Nothing, Is.EqualTo(SqlDateTimeOffsetPart.Nothing)); + } +#endif + + [Test] + public void IntervalPartConversionTest() + { + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Day, Is.EqualTo(SqlIntervalPart.Day)); + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Hour, Is.EqualTo(SqlIntervalPart.Hour)); + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Minute, Is.EqualTo(SqlIntervalPart.Minute)); + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Second, Is.EqualTo(SqlIntervalPart.Second)); + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Millisecond, Is.EqualTo(SqlIntervalPart.Millisecond)); + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Nanosecond, Is.EqualTo(SqlIntervalPart.Nanosecond)); + Assert.That((SqlIntervalPart) (int) SqlDateTimeOffsetPart.Nothing, Is.EqualTo(SqlIntervalPart.Nothing)); + + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Day, Is.EqualTo(SqlDateTimeOffsetPart.Day)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Hour, Is.EqualTo(SqlDateTimeOffsetPart.Hour)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Minute, Is.EqualTo(SqlDateTimeOffsetPart.Minute)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Second, Is.EqualTo(SqlDateTimeOffsetPart.Second)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Millisecond, Is.EqualTo(SqlDateTimeOffsetPart.Millisecond)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Nanosecond, Is.EqualTo(SqlDateTimeOffsetPart.Nanosecond)); + Assert.That((SqlDateTimeOffsetPart) (int) SqlIntervalPart.Nothing, Is.EqualTo(SqlDateTimeOffsetPart.Nothing)); + } + } +} diff --git a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs index 1f08db33d9..f886cb7776 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs @@ -1,40 +1,85 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2023 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.07.24 using System; using System.Diagnostics; +using System.Reflection; using Xtensive.Core; namespace Xtensive.Sql.Dml { public class SqlExtract : SqlExpression { - public SqlDateTimePart DateTimePart { get; private set; } - public SqlDateTimeOffsetPart DateTimeOffsetPart { get; private set; } - public SqlIntervalPart IntervalPart { get; private set; } + private const int TimeTypeId = 1; + private const int DateTypeId = 2; + private const int DateTimeTypeId = 3; + private const int DateTimeOffsetTypeId = 4; + private const int IntervalTypeId = 5; + + private SqlDateTimeOffsetPart internalValue; + private int typeMarker; + private bool typeHasTime; + + public SqlDateTimePart DateTimePart => + typeMarker == DateTimeTypeId ? internalValue.ToDateTimePartFast() : SqlDateTimePart.Nothing; + +#if NET6_0_OR_GREATER + public SqlDatePart DatePart => + typeMarker == DateTypeId ? internalValue.ToDatePartFast() : SqlDatePart.Nothing; + + public SqlTimePart TimePart => + typeMarker == TimeTypeId ? internalValue.ToTimePartFast() : SqlTimePart.Nothing; + +#endif + public SqlDateTimeOffsetPart DateTimeOffsetPart => + typeMarker == DateTimeOffsetTypeId ? internalValue : SqlDateTimeOffsetPart.Nothing; + + public SqlIntervalPart IntervalPart => + typeMarker == IntervalTypeId ? internalValue.ToIntervalPartFast(): SqlIntervalPart.Nothing; public SqlExpression Operand { get; private set; } + public bool IsSecondExtraction => + typeHasTime && internalValue == SqlDateTimeOffsetPart.Second; + public bool IsMillisecondExtraction => + typeHasTime && internalValue == SqlDateTimeOffsetPart.Millisecond; + + public bool IsDateTimeOffsetPart => typeMarker == DateTimeOffsetTypeId; + + public bool IsDateTimePart => typeMarker == DateTimeTypeId; + +#if NET6_0_OR_GREATER + public bool IsDatePart => typeMarker == DateTypeId; + + public bool IsTimePart => typeMarker == TimeTypeId; +#endif + + public bool IsIntervalPart => typeMarker == IntervalTypeId; + public override void ReplaceWith(SqlExpression expression) { var replacingExpression = ArgumentValidator.EnsureArgumentIs(expression); - DateTimePart = replacingExpression.DateTimePart; - DateTimeOffsetPart = replacingExpression.DateTimeOffsetPart; - IntervalPart = replacingExpression.IntervalPart; + internalValue = replacingExpression.internalValue; + typeMarker = replacingExpression.typeMarker; + typeHasTime = replacingExpression.typeHasTime; + //DateTimePart = replacingExpression.DateTimePart; + //DateTimeOffsetPart = replacingExpression.DateTimeOffsetPart; + //IntervalPart = replacingExpression.IntervalPart; Operand = replacingExpression.Operand; } internal override object Clone(SqlNodeCloneContext context) => context.NodeMapping.TryGetValue(this, out var clone) ? clone - : context.NodeMapping[this] = DateTimePart!=SqlDateTimePart.Nothing - ? new SqlExtract(DateTimePart, (SqlExpression) Operand.Clone(context)) - : IntervalPart!=SqlIntervalPart.Nothing - ? new SqlExtract(IntervalPart, (SqlExpression) Operand.Clone(context)) - : new SqlExtract(DateTimeOffsetPart, (SqlExpression) Operand.Clone(context)); + : context.NodeMapping[this] = new SqlExtract(internalValue, typeMarker, (SqlExpression)Operand.Clone(context)); + //DateTimePart!=SqlDateTimePart.Nothing + //? new SqlExtract(DateTimePart, (SqlExpression) Operand.Clone(context)) + //: IntervalPart!=SqlIntervalPart.Nothing + // ? new SqlExtract(IntervalPart, (SqlExpression) Operand.Clone(context)) + // : new SqlExtract(DateTimeOffsetPart, (SqlExpression) Operand.Clone(context)); public override void AcceptVisitor(ISqlVisitor visitor) { @@ -46,27 +91,68 @@ public override void AcceptVisitor(ISqlVisitor visitor) internal SqlExtract(SqlDateTimePart dateTimePart, SqlExpression operand) : base(SqlNodeType.Extract) { - DateTimePart = dateTimePart; - DateTimeOffsetPart = SqlDateTimeOffsetPart.Nothing; - IntervalPart = SqlIntervalPart.Nothing; + internalValue = dateTimePart.ToDtoPartFast(); + typeMarker = DateTimeTypeId; + typeHasTime = true; + + //DateTimePart = dateTimePart; + //DateTimeOffsetPart = SqlDateTimeOffsetPart.Nothing; + //IntervalPart = SqlIntervalPart.Nothing; Operand = operand; } internal SqlExtract(SqlIntervalPart intervalPart, SqlExpression operand) : base(SqlNodeType.Extract) { - DateTimePart = SqlDateTimePart.Nothing; - DateTimeOffsetPart = SqlDateTimeOffsetPart.Nothing; - IntervalPart = intervalPart; + internalValue = intervalPart.ToDtoPartFast(); + typeMarker = IntervalTypeId; + typeHasTime = true; + + //DateTimePart = SqlDateTimePart.Nothing; + //DateTimeOffsetPart = SqlDateTimeOffsetPart.Nothing; + //IntervalPart = intervalPart; Operand = operand; } - public SqlExtract(SqlDateTimeOffsetPart dateTimeOffsetPart, SqlExpression operand) + internal SqlExtract(SqlDateTimeOffsetPart dateTimeOffsetPart, SqlExpression operand) : base(SqlNodeType.Extract) { - DateTimePart = SqlDateTimePart.Nothing; - IntervalPart = SqlIntervalPart.Nothing; - DateTimeOffsetPart = dateTimeOffsetPart; + internalValue = dateTimeOffsetPart; + typeMarker = DateTimeOffsetTypeId; + typeHasTime = true; + + //DateTimePart = SqlDateTimePart.Nothing; + //IntervalPart = SqlIntervalPart.Nothing; + //DateTimeOffsetPart = dateTimeOffsetPart; + Operand = operand; + } + +#if NET6_0_OR_GREATER + internal SqlExtract(SqlDatePart datePart, SqlExpression operand) + : base(SqlNodeType.Extract) + { + internalValue = datePart.ToDtoPartFast(); + typeMarker = DateTypeId; + typeHasTime = false; + Operand = operand; + } + + internal SqlExtract(SqlTimePart timePart, SqlExpression operand) + : base(SqlNodeType.Extract) + { + internalValue = timePart.ToDtoPartFast(); + typeMarker = TimeTypeId; + typeHasTime = true; + Operand = operand; + } +#endif + + private SqlExtract(SqlDateTimeOffsetPart internalValue, int typeMarker, SqlExpression operand) + :base(SqlNodeType.Extract) + { + this.internalValue = internalValue; + this.typeMarker = typeMarker; + typeHasTime = typeMarker == DateTimeTypeId || typeMarker == DateTimeOffsetTypeId || typeMarker == TimeTypeId || typeMarker == IntervalTypeId; Operand = operand; } } diff --git a/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs b/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs index bc574ede13..13a50393ca 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs @@ -77,5 +77,20 @@ public static string ToString(this SqlLockType lockType, bool humanReadable) result.Append("|Throw exception if locked"); return result.ToString(); } + + // See the description in the Parts file. Be careful. +#if NET6_0_OR_GREATER + internal static SqlDateTimeOffsetPart ToDtoPartFast(this SqlDatePart datePart) => (SqlDateTimeOffsetPart) (int) datePart; + internal static SqlDateTimeOffsetPart ToDtoPartFast(this SqlTimePart datePart) => (SqlDateTimeOffsetPart) (int) datePart; +#endif + internal static SqlDateTimeOffsetPart ToDtoPartFast(this SqlDateTimePart datePart) => (SqlDateTimeOffsetPart) (int) datePart; + internal static SqlDateTimeOffsetPart ToDtoPartFast(this SqlIntervalPart datePart) => (SqlDateTimeOffsetPart) (int) datePart; + +#if NET6_0_OR_GREATER + internal static SqlDatePart ToDatePartFast(this SqlDateTimeOffsetPart dtoPart) => (SqlDatePart)(int) dtoPart; + internal static SqlTimePart ToTimePartFast(this SqlDateTimeOffsetPart dtoPart) => (SqlTimePart)(int) dtoPart; +#endif + internal static SqlDateTimePart ToDateTimePartFast(this SqlDateTimeOffsetPart dtoPart) => (SqlDateTimePart) (int) dtoPart; + internal static SqlIntervalPart ToIntervalPartFast(this SqlDateTimeOffsetPart dtoPart) => (SqlIntervalPart)(int) dtoPart; } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlDatePart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlDatePart.cs new file mode 100644 index 0000000000..0dfcf351fa --- /dev/null +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlDatePart.cs @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER + +using System; + +namespace Xtensive.Sql.Dml +{ + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + + [Serializable] + public enum SqlDatePart + { + Year = 0, + Month = 1, + Day = 2, + DayOfYear = 10, + DayOfWeek = 11, + Nothing = 25, + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs index ef07fee4f7..c1d0b61a9c 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs @@ -1,31 +1,55 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2014-2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. using System; namespace Xtensive.Sql.Dml { + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] public enum SqlDateTimeOffsetPart { - Year, - Month, - Day, - Hour, - Minute, - Second, - Millisecond, - Nanosecond, - TimeZoneHour, - TimeZoneMinute, - DayOfYear, - DayOfWeek, - Date, - DateTime, - LocalDateTime, - UtcDateTime, - Offset, - Nothing, + Year = 0, + Month = 1, + Day = 2, + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + TimeZoneHour = 8, + TimeZoneMinute = 9, + DayOfYear = 10, + DayOfWeek = 11, + Date = 12, + DateTime = 13, + LocalDateTime = 14, + UtcDateTime = 15, + Offset = 16, + Nothing = 25, } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs index 6650b64122..e431858e08 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs @@ -6,21 +6,45 @@ namespace Xtensive.Sql.Dml { + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] public enum SqlDateTimePart { - Year, - Month, - Day, - Hour, - Minute, - Second, - Millisecond, - Nanosecond, - TimeZoneHour, - TimeZoneMinute, - DayOfYear, - DayOfWeek, - Nothing, + Year = 0, + Month = 1, + Day = 2, + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + TimeZoneHour = 8, + TimeZoneMinute = 9, + DayOfYear = 10, + DayOfWeek = 11, + Nothing = 25, } } diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs index f5fcfe39f8..45e6a7dbb4 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.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-2023 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.02.25 @@ -8,15 +8,39 @@ namespace Xtensive.Sql.Dml { + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] public enum SqlIntervalPart { - Day = 0, - Hour = 1, - Minute = 2, - Second = 3, - Millisecond = 4, - Nanosecond = 5, - Nothing = 10, + Day = 2, + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + Nothing = 25, } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlTimePart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlTimePart.cs new file mode 100644 index 0000000000..fb861c59e6 --- /dev/null +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlTimePart.cs @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +#if NET6_0_OR_GREATER + +using System; + +namespace Xtensive.Sql.Dml +{ + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] + public enum SqlTimePart + { + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + Nothing = 25, + } +} +#endif \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 00736a0cae..a8169364e0 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -539,17 +539,41 @@ public static SqlExtract Extract(SqlDateTimePart part, SqlExpression operand) { ArgumentValidator.EnsureArgumentNotNull(operand, "operand"); SqlValidator.EnsureIsArithmeticExpression(operand); - if (part==SqlDateTimePart.Nothing) - throw new ArgumentException(); + if (part == SqlDateTimePart.Nothing) { + throw new ArgumentException(string.Format("Unable to extract {0} part", SqlDateTimePart.Nothing.ToString())); + } return new SqlExtract(part, operand); } - + +#if NET6_0_OR_GREATER + public static SqlExtract Extract(SqlDatePart part, SqlExpression operand) + { + ArgumentNullException.ThrowIfNull(operand, nameof(operand)); + SqlValidator.EnsureIsArithmeticExpression(operand); + if(part==SqlDatePart.Nothing) { + throw new ArgumentException(string.Format("Unable to extract {0} part", SqlDatePart.Nothing.ToString())); + } + return new SqlExtract(part, operand); + } + + public static SqlExtract Extract(SqlTimePart part, SqlExpression operand) + { + ArgumentNullException.ThrowIfNull(operand, nameof(operand)); + SqlValidator.EnsureIsArithmeticExpression(operand); + if (part == SqlTimePart.Nothing) { + throw new ArgumentException(string.Format("Unable to extract {0} part", SqlTimePart.Nothing.ToString())); + } + return new SqlExtract(part, operand); + } +#endif + public static SqlExtract Extract(SqlIntervalPart part, SqlExpression operand) { ArgumentValidator.EnsureArgumentNotNull(operand, "operand"); SqlValidator.EnsureIsArithmeticExpression(operand); - if (part==SqlIntervalPart.Nothing) - throw new ArgumentException(); + if (part == SqlIntervalPart.Nothing) { + throw new ArgumentException(string.Format("Unable to extract {0} part", SqlIntervalPart.Nothing.ToString())); + } return new SqlExtract(part, operand); } From 005e62d22b789aa0ff6698eb936d6b42d2d60d51 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 2 Feb 2023 21:02:02 +0500 Subject: [PATCH 47/86] TimeOnly and DateOnly compilers use their own SqlDml.Extract() --- .../MemberCompilers/DateOnlyCompilers.cs | 22 +++++++++---------- .../MemberCompilers/TimeOnlyCompilers.cs | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index ad6b2457e3..b9c709152e 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -19,27 +19,27 @@ internal static class DateOnlyCompilers [Compiler(typeof(DateOnly), "Year", TargetKind.PropertyGet)] public static SqlExpression DateOnlyYear(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Year, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDatePart.Year, _this)); [Compiler(typeof(DateOnly), "Month", TargetKind.PropertyGet)] public static SqlExpression DateOnlyMonth(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Month, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDatePart.Month, _this)); [Compiler(typeof(DateOnly), "Day", TargetKind.PropertyGet)] public static SqlExpression DateOnlyDay(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Day, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDatePart.Day, _this)); [Compiler(typeof(DateOnly), "DayOfYear", TargetKind.PropertyGet)] public static SqlExpression DateOnlyDayOfYear(SqlExpression _this) { - return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfYear, _this)); + return ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDatePart.DayOfYear, _this)); } [Compiler(typeof(DateOnly), "DayOfWeek", TargetKind.PropertyGet)] public static SqlExpression DateOnlyDayOfWeek(SqlExpression _this) { - var baseExpression = ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.DayOfWeek, _this)); + var baseExpression = ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDatePart.DayOfWeek, _this)); var context = ExpressionTranslationContext.Current; if (context == null) { return baseExpression; @@ -137,15 +137,15 @@ public static SqlExpression DateOnlyToStringIso(SqlExpression _this) public static SqlExpression DateOnlyToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) { throw new NotImplementedException(); - //var stringValue = value as SqlLiteral; + var stringValue = value as SqlLiteral; - //if (stringValue == null) - // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + if (stringValue == null) + throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); - //if (!stringValue.Value.Equals("s")) - // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + if (!stringValue.Value.Equals("s")) + throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); - //return SqlDml.DateTimeToStringIso(_this); + return SqlDml.DateTimeToStringIso(_this); } } #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index ce208d0c66..888e79f9b4 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -21,19 +21,19 @@ internal static class TimeOnlyCompilers [Compiler(typeof(TimeOnly), "Hour", TargetKind.PropertyGet)] public static SqlExpression TimeOnlyHour(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Hour, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlTimePart.Hour, _this)); [Compiler(typeof(TimeOnly), "Minute", TargetKind.PropertyGet)] public static SqlExpression TimeOnlyMinute(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Minute, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlTimePart.Minute, _this)); [Compiler(typeof(TimeOnly), "Second", TargetKind.PropertyGet)] public static SqlExpression TimeOnlySecond(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Second, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlTimePart.Second, _this)); [Compiler(typeof(TimeOnly), "Millisecond", TargetKind.PropertyGet)] public static SqlExpression TimeOnlyMillisecond(SqlExpression _this) => - ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlDateTimePart.Millisecond, _this)); + ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlTimePart.Millisecond, _this)); [Compiler(typeof(TimeOnly), "Ticks", TargetKind.PropertyGet)] public static SqlExpression TimeOnlyTicks(SqlExpression _this) From 9d866ce89ce52094c45a93c75c4df54cdbfa7e4e Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 2 Feb 2023 21:06:17 +0500 Subject: [PATCH 48/86] Support for DateOnly/TimeOnly parts extraction Ticks part is not supported yet. MySQL: - time for mysql 5.5 and below does not support fractions of second, precision declaration works from 5.6. - changes extraction of milliseconds (seems to be working, on tests but some issues might appear) --- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 31 ++++- .../Sql.Drivers.Firebird/v2_5/Translator.cs | 12 ++ .../Sql.Drivers.MySql/v5_0/Compiler.cs | 7 + .../Sql.Drivers.MySql/v5_0/Extractor.cs | 8 +- .../Sql.Drivers.MySql/v5_0/Translator.cs | 32 ++++- .../v5_6/ServerInfoProvider.cs | 21 ++- .../Sql.Drivers.Oracle/v09/Compiler.cs | 128 ++++++++++-------- .../Sql.Drivers.Oracle/v09/Translator.cs | 31 ++++- .../Sql.Drivers.PostgreSql/v8_0/Compiler.cs | 36 ++--- .../Sql.Drivers.PostgreSql/v8_0/Translator.cs | 34 ++++- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 6 + .../Sql.Drivers.Sqlite/v3/Compiler.cs | 31 +++-- .../Sql.Drivers.Sqlite/v3/Translator.cs | 26 ++++ .../TimeOnly/PartsExtractionTest.cs | 2 + Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs | 12 +- .../Sql/Compiler/SqlTranslator.cs | 36 +++++ 16 files changed, 346 insertions(+), 107 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index 67e3ed22b4..c21a22ec76 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -101,6 +101,31 @@ public override void Visit(SqlExtract node) Visit(CastToLong(node.Operand)); return; } +#if NET6_0_OR_GREATER + if (((node.IsDatePart && node.DatePart == SqlDatePart.DayOfYear) + || (node.IsDateTimePart && node.DateTimePart == SqlDateTimePart.DayOfYear))) { + if (!case_SqlDateTimePart_DayOfYear) { + case_SqlDateTimePart_DayOfYear = true; + Visit(SqlDml.Add(node, SqlDml.Literal(1))); + case_SqlDateTimePart_DayOfYear = false; + } + else { + base.Visit(node); + } + return; + } + else if (node.IsSecondExtraction) { + if (!case_SqlDateTimePart_Second) { + case_SqlDateTimePart_Second = true; + Visit(SqlDml.Truncate(node)); + case_SqlDateTimePart_Second = false; + } + else { + base.Visit(node); + } + return; + } +#else switch (node.DateTimePart) { case SqlDateTimePart.DayOfYear: if (!case_SqlDateTimePart_DayOfYear) { @@ -123,6 +148,8 @@ public override void Visit(SqlExtract node) } return; } +#endif + base.Visit(node); } @@ -235,7 +262,7 @@ public override void Visit(SqlAlterSequence node) translator.Translate(context, node, NodeSection.Exit); } - #region Static helpers +#region Static helpers protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) { @@ -301,7 +328,7 @@ protected static SqlConcat DateTimeToStringIso(SqlExpression dateTime) return SqlDml.Concat(date, SqlDml.Literal("T"), time); } - #endregion +#endregion protected internal Compiler(SqlDriver driver) : base(driver) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs index 2e67c0e54d..71b15559dd 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs @@ -221,6 +221,18 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) } } +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + public override void Translate(IOutput output, SqlDatePart datePart) + { + switch (datePart) { + case SqlDatePart.DayOfYear: _ = output.Append("YEARDAY"); break; + case SqlDatePart.DayOfWeek: _ = output.Append("WEEKDAY"); break; + default: base.Translate(output, datePart); break; + } + } +#endif + /// public override void Translate(SqlCompilerContext context, SqlSelect node, SelectSection section) { diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs index 502fa30f8c..c10a370ca1 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs @@ -239,6 +239,13 @@ public override void Visit(SqlExtract node) Visit(SqlDml.FunctionCall(node.DateTimePart.ToString(), node.Operand)); return; } +#if NET6_0_OR_GREATER //DO_DATEONLY + if (node.DatePart == SqlDatePart.DayOfWeek || node.DatePart == SqlDatePart.DayOfYear) { + Visit(SqlDml.FunctionCall(node.DatePart.ToString(), node.Operand)); + return; + } +#endif + base.Visit(node); } 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 db5b369da3..392699ae6e 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 @@ -602,7 +602,7 @@ private SqlValueType CreateValueType(IDataRecord row, int typeNameIndex, int precisionIndex, int scaleIndex, int charLengthIndex) { var typeName = row.GetString(typeNameIndex).ToUpperInvariant(); - var columnName = row.GetString(6).ToUpperInvariant(); + var dataTypeName = row.GetString(6).ToUpperInvariant(); var precision = row.IsDBNull(precisionIndex) ? DefaultPrecision : ReadInt(row, precisionIndex); var scale = row.IsDBNull(scaleIndex) ? DefaultScale : ReadInt(row, scaleIndex); @@ -618,7 +618,7 @@ private SqlValueType CreateValueType(IDataRecord row, return new SqlValueType(SqlType.Double); } - if (columnName.Equals("TINYINT(1)", StringComparison.Ordinal)) { + if (dataTypeName.Equals("TINYINT(1)", StringComparison.Ordinal)) { return new SqlValueType(SqlType.Boolean); } @@ -654,8 +654,8 @@ private SqlValueType CreateValueType(IDataRecord row, } #if NET6_0_OR_GREATER //DO_DATEONLY if (typeName.Equals("TIME", StringComparison.Ordinal)) { - - return new SqlValueType(SqlType.Time); + var time_precision = (dataTypeName.Length > 4) ? int.Parse(dataTypeName[5].ToString()) : 0; + return new SqlValueType(SqlType.Time, precision); } else if (typeName.StartsWith("TIME", StringComparison.Ordinal)) { // "timestamp precision" is saved as "scale", ignoring too diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs index 35b67517d6..d421bf9880 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs @@ -411,9 +411,8 @@ public override void Translate(SqlCompilerContext context, object literalValue) /// public override void Translate(SqlCompilerContext context, SqlExtract node, ExtractSection section) { - var isSecond = node.DateTimePart == SqlDateTimePart.Second || node.IntervalPart == SqlIntervalPart.Second; - var isMillisecond = node.DateTimePart == SqlDateTimePart.Millisecond - || node.IntervalPart == SqlIntervalPart.Millisecond; + var isSecond = node.IsSecondExtraction; + var isMillisecond = node.IsMillisecondExtraction; if (!(isSecond || isMillisecond)) { base.Translate(context, node, section); return; @@ -423,7 +422,7 @@ public override void Translate(SqlCompilerContext context, SqlExtract node, Extr _ = context.Output.AppendOpeningPunctuation("(extract("); break; case ExtractSection.Exit: - _ = context.Output.Append(isMillisecond ? ") % 1000)" : "))"); + _ = context.Output.Append(isMillisecond ? ") / 1000)" : "))"); break; default: base.Translate(context, node, section); @@ -519,6 +518,31 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) } } +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + public override void Translate(IOutput output, SqlDatePart datePart) + { + switch (datePart) { + case SqlDatePart.Day: _ = output.Append("DAY"); break; + case SqlDatePart.Year: _ = output.Append("YEAR"); break; + case SqlDatePart.Month: _ = output.Append("MONTH"); break; + default: base.Translate(output, datePart); break; + } + } + + /// + public override void Translate(IOutput output, SqlTimePart dateTimePart) + { + switch (dateTimePart) { + case SqlTimePart.Millisecond: _ = output.Append("MICROSECOND"); break; + case SqlTimePart.Hour: _ = output.Append("HOUR"); break; + case SqlTimePart.Minute: _ = output.Append("MINUTE"); break; + default: base.Translate(output, dateTimePart); break; + } + } +#endif + + /// public override void Translate(IOutput output, SqlLockType lockType) { diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/ServerInfoProvider.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/ServerInfoProvider.cs index e8586c20c8..be2cfd25ef 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/ServerInfoProvider.cs @@ -1,13 +1,32 @@ -// Copyright (C) 2003-2010 Xtensive LLC. +// Copyright (C) 2003-2010 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Alena Mikshina // Created: 2013.12.30 +using Xtensive.Sql.Info; + namespace Xtensive.Sql.Drivers.MySql.v5_6 { internal class ServerInfoProvider : v5_5.ServerInfoProvider { +#if NET6_0_OR_GREATER + /// + public override DataTypeCollection GetDataTypesInfo() + { + var types = base.GetDataTypesInfo(); + + var common = DataTypeFeatures.Default | DataTypeFeatures.Nullable | DataTypeFeatures.NonKeyIndexing | + DataTypeFeatures.Grouping | DataTypeFeatures.Ordering | DataTypeFeatures.Multiple; + + var index = DataTypeFeatures.Indexing | DataTypeFeatures.KeyConstraint; + + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, ValueRange.TimeOnly, "time(6)"); + + return types; + } +#endif + // Constructors public ServerInfoProvider(SqlDriver driver) diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index 4d2c2135c3..e39d9beea9 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -108,69 +108,77 @@ public override void Visit(SqlTrim node) public override void Visit(SqlExtract node) { switch (node.DateTimeOffsetPart) { - case SqlDateTimeOffsetPart.Day: - DateTimeOffsetExtractPart(node.Operand, "DD").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Hour: - DateTimeOffsetExtractPart(node.Operand, "HH24").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Millisecond: - DateTimeOffsetExtractPart(node.Operand, "FF3").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Nanosecond: - DateTimeOffsetExtractPart(node.Operand, "FF9").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Minute: - DateTimeOffsetExtractPart(node.Operand, "MI").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Month: - DateTimeOffsetExtractPart(node.Operand, "MM").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Second: - DateTimeOffsetExtractPart(node.Operand, "SS").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Year: - DateTimeOffsetExtractPart(node.Operand, "YYYY").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.TimeZoneHour: - DateTimeOffsetExtractPart(node.Operand, "TZH").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.TimeZoneMinute: - DateTimeOffsetExtractPart(node.Operand, "TZM").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.DayOfWeek: - DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.DayOfYear: - DateTimeOffsetExtractPart(node.Operand, "DDD").AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Date: - DateTimeOffsetTruncate(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.DateTime: - DateTimeOffsetTruncateOffset(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.LocalDateTime: - DateTimeOffsetToLocalDateTime(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.UtcDateTime: - DateTimeOffsetToUtcDateTime(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Offset: - DateTimeOffsetPartOffset(node.Operand).AcceptVisitor(this); - return; + case SqlDateTimeOffsetPart.Day: + DateTimeOffsetExtractPart(node.Operand, "DD").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Hour: + DateTimeOffsetExtractPart(node.Operand, "HH24").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Millisecond: + DateTimeOffsetExtractPart(node.Operand, "FF3").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Nanosecond: + DateTimeOffsetExtractPart(node.Operand, "FF9").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Minute: + DateTimeOffsetExtractPart(node.Operand, "MI").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Month: + DateTimeOffsetExtractPart(node.Operand, "MM").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Second: + DateTimeOffsetExtractPart(node.Operand, "SS").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Year: + DateTimeOffsetExtractPart(node.Operand, "YYYY").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.TimeZoneHour: + DateTimeOffsetExtractPart(node.Operand, "TZH").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.TimeZoneMinute: + DateTimeOffsetExtractPart(node.Operand, "TZM").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.DayOfWeek: + DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.DayOfYear: + DateTimeOffsetExtractPart(node.Operand, "DDD").AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Date: + DateTimeOffsetTruncate(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.DateTime: + DateTimeOffsetTruncateOffset(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.LocalDateTime: + DateTimeOffsetToLocalDateTime(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.UtcDateTime: + DateTimeOffsetToUtcDateTime(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Offset: + DateTimeOffsetPartOffset(node.Operand).AcceptVisitor(this); + return; } switch (node.DateTimePart) { - case SqlDateTimePart.DayOfYear: - DateTimeExtractDayOfYear(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimePart.DayOfWeek: - DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); - return; - default: - base.Visit(node); - return; + case SqlDateTimePart.DayOfYear: + DateTimeExtractDayOfYear(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimePart.DayOfWeek: + DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); + return; + } +#if NET6_0_OR_GREATER //DO_DATEONLY + switch (node.DatePart) { + case SqlDatePart.DayOfYear: + DateTimeExtractDayOfYear(node.Operand).AcceptVisitor(this); + return; + case SqlDatePart.DayOfWeek: + DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); + return; } +#endif + base.Visit(node); } protected override void VisitSelectFrom(SqlSelect node) diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index dff4fe4f61..88c9be7c0e 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -116,7 +116,7 @@ public override void Translate(SqlCompilerContext context, SqlNextValue node, No /// public override void Translate(SqlCompilerContext context, SqlExtract node, ExtractSection section) { - if (node.DateTimePart == SqlDateTimePart.Second || node.IntervalPart == SqlIntervalPart.Second) { + if (node.IsSecondExtraction) { switch (section) { case ExtractSection.Entry: _ = context.Output.Append("TRUNC(EXTRACT("); @@ -127,7 +127,7 @@ public override void Translate(SqlCompilerContext context, SqlExtract node, Extr } } - if (node.DateTimePart == SqlDateTimePart.Millisecond || node.IntervalPart == SqlIntervalPart.Millisecond) { + if (node.IsMillisecondExtraction) { switch (section) { case ExtractSection.Entry: _ = context.Output.Append("MOD(EXTRACT("); @@ -357,6 +357,32 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) } } +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + public override void Translate(IOutput output, SqlDatePart datePart) + { + switch (datePart) { + case SqlDatePart.DayOfWeek: + case SqlDatePart.DayOfYear: + throw new NotSupportedException(); + default: + base.Translate(output, datePart); + break; + } + } + + /// + public override void Translate(IOutput output, SqlTimePart timePart) + { + if (timePart== SqlTimePart.Millisecond) { + _ = output.Append("SECOND"); + } + else { + base.Translate(output, timePart); + } + } +#endif + /// public override void Translate(IOutput output, SqlIntervalPart part) { @@ -366,7 +392,6 @@ public override void Translate(IOutput output, SqlIntervalPart part) else { base.Translate(output, part); } - } /// diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs index 60e340e580..03310c5d57 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs @@ -300,24 +300,26 @@ private static SqlExpression NpgsqlTypeConstructor(SqlExpression left, SqlExpres } public override void Visit(SqlExtract node) - { - switch (node.DateTimeOffsetPart) { - case SqlDateTimeOffsetPart.Date: - DateTimeOffsetExtractDate(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.DateTime: - DateTimeOffsetExtractDateTime(node.Operand).AcceptVisitor(this); - return; + { + if (node.IsDateTimeOffsetPart) { + switch (node.DateTimeOffsetPart) { + case SqlDateTimeOffsetPart.Date: + DateTimeOffsetExtractDate(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.DateTime: + DateTimeOffsetExtractDateTime(node.Operand).AcceptVisitor(this); + return; - case SqlDateTimeOffsetPart.UtcDateTime: - DateTimeOffsetToUtcDateTime(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.LocalDateTime: - DateTimeOffsetToLocalDateTime(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Offset: - DateTimeOffsetExtractOffset(node); - return; + case SqlDateTimeOffsetPart.UtcDateTime: + DateTimeOffsetToUtcDateTime(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.LocalDateTime: + DateTimeOffsetToLocalDateTime(node.Operand).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Offset: + DateTimeOffsetExtractOffset(node); + return; + } } base.Visit(node); } diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs index dce017dd35..0b5a8a087f 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs @@ -396,12 +396,8 @@ public override void Translate(SqlCompilerContext context, SqlArray node, ArrayS /// public override void Translate(SqlCompilerContext context, SqlExtract node, ExtractSection section) { - var isSecond = node.DateTimePart == SqlDateTimePart.Second - || node.IntervalPart == SqlIntervalPart.Second - || node.DateTimeOffsetPart == SqlDateTimeOffsetPart.Second; - var isMillisecond = node.DateTimePart == SqlDateTimePart.Millisecond - || node.IntervalPart == SqlIntervalPart.Millisecond - || node.DateTimeOffsetPart == SqlDateTimeOffsetPart.Millisecond; + var isSecond = node.IsSecondExtraction; + var isMillisecond = node.IsMillisecondExtraction; if (!(isSecond || isMillisecond)) { base.Translate(context, node, section); return; @@ -832,6 +828,7 @@ public override void Translate(SqlCompilerContext context, SqlCast node, NodeSec } } + /// public override void Translate(IOutput output, SqlDateTimePart part) { switch (part) { @@ -842,6 +839,7 @@ public override void Translate(IOutput output, SqlDateTimePart part) } } + /// public override void Translate(IOutput output, SqlDateTimeOffsetPart part) { switch (part) { @@ -855,6 +853,30 @@ public override void Translate(IOutput output, SqlDateTimeOffsetPart part) } } +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + public override void Translate(IOutput output, SqlDatePart part) + { + switch (part) { + case SqlDatePart.DayOfYear: _ = output.Append("DOY"); break; + case SqlDatePart.DayOfWeek: _ = output.Append("DOW"); break; + default: base.Translate(output, part); break; + } + } + + /// + public override void Translate(IOutput output, SqlTimePart part) + { + if (part == SqlTimePart.Millisecond) { + _ = output.Append("MILLISECONDS"); + } + else { + base.Translate(output, part); + } + } + +#endif + /// public override void Translate(IOutput output, SqlLockType lockType) { diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index 839c99c6e3..44c50eab9b 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -266,6 +266,12 @@ public override void Visit(SqlExtract node) Visit((DatePartWeekDay(node.Operand) + DateFirst + 6) % 7); return; } +#if NET6_0_OR_GREATER + if (node.DatePart == SqlDatePart.DayOfWeek) { + Visit((DatePartWeekDay(node.Operand) + DateFirst + 6) % 7); + return; + } +#endif switch (node.IntervalPart) { case SqlIntervalPart.Day: diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index c5e7974e52..7c6a849a2f 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -112,18 +112,25 @@ public override void Visit(SqlAlterTable node) /// public override void Visit(SqlExtract node) { - if (node.IntervalPart != SqlIntervalPart.Nothing) { + if (node.IsIntervalPart) { VisitInterval(node); return; } - if (node.DateTimePart != SqlDateTimePart.Nothing) { + if (node.IsDateTimePart) { VisitDateTime(node); return; } - if (node.DateTimeOffsetPart != SqlDateTimeOffsetPart.Nothing) { + if (node.IsDateTimeOffsetPart) { VisitDateTimeOffset(node); return; } +#if NET6_0_OR_GREATER //DO_DATEONLY + if(node.IsTimePart) { + VisitTime(node); + return; + } +#endif + base.Visit(node); } @@ -332,8 +339,17 @@ private void VisitInterval(SqlExtract node) private void VisitDateTime(SqlExtract node) { - if (node.DateTimePart==SqlDateTimePart.Millisecond) { - Visit(CastToLong(DateGetMilliseconds(node.Operand))); + if (node.IsMillisecondExtraction) { + Visit(CastToLong(DateOrTimeGetMilliseconds(node.Operand))); + return; + } + base.Visit(node); + } + + private void VisitTime(SqlExtract node) + { + if (node.IsMillisecondExtraction) { + Visit(CastToLong(DateOrTimeGetMilliseconds(node.Operand))); return; } base.Visit(node); @@ -454,17 +470,16 @@ private static SqlExpression TimeAddSeconds(SqlExpression date, SqlExpression se SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, ".", milliseconds, " ", "SECONDS")); #endif - private static SqlExpression DateGetMilliseconds(SqlExpression date) => + private static SqlExpression DateOrTimeGetMilliseconds(SqlExpression date) => CastToLong(SqlDml.FunctionCall("STRFTIME", "%f", date) * MillisecondsPerSecond) - CastToLong(SqlDml.FunctionCall("STRFTIME", "%S", date) * MillisecondsPerSecond); - private static SqlExpression DateGetTotalSeconds(SqlExpression date) => SqlDml.FunctionCall("STRFTIME", "%s", date); private static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) => (((DateGetTotalSeconds(date1) - DateGetTotalSeconds(date2)) * MillisecondsPerSecond) - + DateGetMilliseconds(date1) - DateGetMilliseconds(date2)) * NanosecondsPerMillisecond; + + DateOrTimeGetMilliseconds(date1) - DateOrTimeGetMilliseconds(date2)) * NanosecondsPerMillisecond; private static SqlExpression ServerOffsetAsString() diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs index 5e09534777..f9c5629f87 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs @@ -347,6 +347,32 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) } } +#if NET6_0_OR_GREATER + /// + public override void Translate(IOutput output, SqlDatePart dateTimePart) + { + switch (dateTimePart) { + case SqlDatePart.Year: _ = output.Append("'%Y'"); break; + case SqlDatePart.Month: _ = output.Append("'%m'"); break; + case SqlDatePart.Day: _ = output.Append("'%d'"); break; + case SqlDatePart.DayOfWeek: _ = output.Append("'%w'"); break; + case SqlDatePart.DayOfYear: _ = output.Append("'%j'"); break; + default: base.Translate(output, dateTimePart); break; + } + } + + /// + public override void Translate(IOutput output, SqlTimePart dateTimePart) + { + switch (dateTimePart) { + case SqlTimePart.Hour: _ = output.Append("'%H'"); break; + case SqlTimePart.Minute: _ = output.Append("'%M'"); break; + case SqlTimePart.Second: _ = output.Append("'%S'"); break; + default: base.Translate(output, dateTimePart); break; + } + } +#endif + /// public override void Translate(IOutput output, SqlIntervalPart intervalPart) => throw SqlHelper.NotSupported(intervalPart.ToString()); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs index 1e58b63fe3..fcd65dca3a 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs @@ -68,6 +68,7 @@ public void ExtractMillisecondTest() public void MysqlExtractMillisecondTest() { Require.ProviderIs(StorageProvider.MySql); + Require.ProviderVersionAtLeast(new Version(5, 6));// no support for fractions below 5.6 ExecuteInsideSession((s) => { var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); RunTest(s, c => c.MillisecondTimeOnly.Millisecond == firstMillisecondTimeOnly.Millisecond); @@ -76,6 +77,7 @@ public void MysqlExtractMillisecondTest() } [Test] + [Ignore("Not implemented yet.")] public void ExtractTicksTest() { Require.ProviderIsNot(StorageProvider.PostgreSql | StorageProvider.Oracle); diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs index d682a2fcf3..f4bda01366 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs @@ -2083,12 +2083,20 @@ public virtual void Visit(SqlExtract node) { using (context.EnterScope(node)) { AppendTranslatedEntry(node); - if (node.DateTimePart!= SqlDateTimePart.Nothing) { + if (node.IsDateTimePart) { translator.Translate(context.Output, node.DateTimePart); } - else if (node.IntervalPart!= SqlIntervalPart.Nothing) { + else if (node.IsIntervalPart) { translator.Translate(context.Output, node.IntervalPart); } +#if NET6_0_OR_GREATER + else if (node.IsDatePart) { + translator.Translate(context.Output, node.DatePart); + } + else if (node.IsTimePart) { + translator.Translate(context.Output, node.TimePart); + } +#endif else { translator.Translate(context.Output, node.DateTimeOffsetPart); } diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs index 3a4cdc75ef..80a51014be 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs @@ -2278,6 +2278,42 @@ public virtual void Translate(IOutput output, SqlDateTimePart dateTimePart) }); } +#if NET6_0_OR_GREATER + /// + /// Translates writes the result to the . + /// + /// The output to write to. + /// Enum value to translate. + public virtual void Translate(IOutput output, SqlDatePart datePart) + { + _ = output.Append(datePart switch { + SqlDatePart.Year => "YEAR", + SqlDatePart.Month => "MONTH", + SqlDatePart.Day => "DAY", + SqlDatePart.DayOfYear => "DAYOFYEAR", + SqlDatePart.DayOfWeek => "DAYOFWEEK", + _ => throw new ArgumentOutOfRangeException(nameof(datePart)) + }); + } + + /// + /// Translates writes the result to the . + /// + /// The output to write to. + /// Enum value to translate. + public virtual void Translate(IOutput output, SqlTimePart timePart) + { + _ = output.Append(timePart switch { + SqlTimePart.Hour => "HOUR", + SqlTimePart.Minute => "MINUTE", + SqlTimePart.Second => "SECOND", + SqlTimePart.Millisecond => "MILLISECOND", + SqlTimePart.Nanosecond => "NANOSECOND", + _ => throw new ArgumentOutOfRangeException(nameof(timePart)) + }); + } +#endif + /// /// Translates and writes result to the . /// From 435abfb0d0e40f31b15a5ff349ed653f66f5c1a8 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 2 Feb 2023 21:07:25 +0500 Subject: [PATCH 49/86] Reminder of certain particularity of using SqlFunctionType --- Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index a312a17797..5dee89f7b6 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -99,5 +99,6 @@ public enum SqlFunctionType RoundDecimalToZero, RoundDoubleToEven, RoundDoubleToZero, + //!!! max value is used for array size } } From ec00730942224b79b1c18078fa29cfc103cd038c Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 2 Feb 2023 21:09:01 +0500 Subject: [PATCH 50/86] Several tests fixed, one kept ignored just in case Net6 version works though --- .../Storage/LegacyDb/CrazyColumns2008Test.cs | 34 +++++++++++++++++++ .../Storage/SessionEventsTest.cs | 1 - .../FullText/DomainUpgradeFullTextTest.cs | 5 +-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Storage/LegacyDb/CrazyColumns2008Test.cs b/Orm/Xtensive.Orm.Tests/Storage/LegacyDb/CrazyColumns2008Test.cs index 5ac445c963..d2e976d9f4 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/LegacyDb/CrazyColumns2008Test.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/LegacyDb/CrazyColumns2008Test.cs @@ -19,11 +19,19 @@ public class Crazy : Entity [Field, Key] public Guid Id { get; private set; } +#if NET6_0_OR_GREATER + [Field] + public TimeOnly Time { get; set; } + + [Field] + public DateOnly Date { get; set; } +#else [Field] public DateTime Time { get; set; } [Field] public DateTime Date { get; set; } +#endif } } @@ -46,6 +54,31 @@ protected override DomainConfiguration BuildConfiguration() return config; } +#if NET6_0_OR_GREATER + [Test] + [Ignore("Fix later")] + public void CombinedTest() + { + var date = new DateTime(2000, 01, 01); + var time = new DateTime(1, 1, 1, 12, 00, 00); + using (var session = Domain.OpenSession()) + using (var ts = session.OpenTransaction()) { + var crazy1 = new Crazy { Date = DateOnly.FromDateTime(date), Time = TimeOnly.FromDateTime(DateTime.Now) }; + var crazy2 = new Crazy { Date = DateOnly.FromDateTime(DateTime.Now), Time = TimeOnly.FromDateTime(time) }; + ts.Complete(); + } + + using (var session = Domain.OpenSession()) + using (session.OpenTransaction()) { + foreach (var item in session.Query.All()) { + Console.WriteLine(item.Date); + Console.WriteLine(item.Time); + } + Assert.AreEqual(1, session.Query.All().Where(o => o.Date == DateOnly.FromDateTime(date)).Count()); + Assert.AreEqual(1, session.Query.All().Where(o => o.Time == TimeOnly.FromDateTime(time)).Count()); + } + } +#else [Test] [Ignore("Fix later")] public void CombinedTest() @@ -69,6 +102,7 @@ public void CombinedTest() Assert.AreEqual(1, session.Query.All().Where(o => o.Time==time).Count()); } } +#endif protected override string GetCreateDbScript(DomainConfiguration config) { diff --git a/Orm/Xtensive.Orm.Tests/Storage/SessionEventsTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SessionEventsTest.cs index 9a47014ed7..ab4d3837bb 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SessionEventsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SessionEventsTest.cs @@ -10,7 +10,6 @@ using Xtensive.Orm.Configuration; using Xtensive.Orm.Tests.Storage.SessionEventsTestModel; using System.Threading.Tasks; -using Renci.SshNet; using System.Linq; namespace Xtensive.Orm.Tests.Storage.SessionEventsTestModel diff --git a/Orm/Xtensive.Orm.Tests/Upgrade/FullText/DomainUpgradeFullTextTest.cs b/Orm/Xtensive.Orm.Tests/Upgrade/FullText/DomainUpgradeFullTextTest.cs index 15ef5a3c30..b95d43b3f0 100644 --- a/Orm/Xtensive.Orm.Tests/Upgrade/FullText/DomainUpgradeFullTextTest.cs +++ b/Orm/Xtensive.Orm.Tests/Upgrade/FullText/DomainUpgradeFullTextTest.cs @@ -49,7 +49,8 @@ private Domain BuildDomain(string version, DomainUpgradeMode upgradeMode) configuration.UpgradeMode = upgradeMode; configuration.Types.Register(Assembly.GetExecutingAssembly(), "Xtensive.Orm.Tests.Upgrade.FullText.Model." + version); - using (Upgrader.Enable(version)) { + configuration.Types.Register(typeof(Model.Upgrader)); + using (Model.Upgrader.Enable(version)) { var domain = Domain.Build(configuration); return domain; } @@ -61,7 +62,7 @@ private async Task BuildDomainAsync(string version, DomainUpgradeMode up configuration.UpgradeMode = upgradeMode; configuration.Types.Register(Assembly.GetExecutingAssembly(), "Xtensive.Orm.Tests.Upgrade.FullText.Model." + version); - using (Upgrader.Enable(version)) { + using (Model.Upgrader.Enable(version)) { var domain = await Domain.BuildAsync(configuration); return domain; } From 2af4f9673cf77a1c1bb80a9d285d272220becdc9 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 8 Feb 2023 20:24:48 +0500 Subject: [PATCH 51/86] Compilers use dedicated SqlDml methods for Date and Time --- .../DateTimeIntervalTest.cs | 34 +++++++------- .../MemberCompilers/DateOnlyCompilers.cs | 4 +- .../MemberCompilers/TimeOnlyCompilers.cs | 46 ++++++++++--------- .../Sql/Internals/SqlValidator.cs | 4 ++ Orm/Xtensive.Orm/Sql/SqlDml.cs | 23 ++++++---- Orm/Xtensive.Orm/Sql/SqlNodeType.cs | 5 +- 6 files changed, 67 insertions(+), 49 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs index ab3997e06d..4b46ee311a 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs @@ -181,19 +181,11 @@ public virtual void DateOnlyConstructTest() DefaultDateOnly); } - [Test] - public virtual void DateOnlySubtractDateTimeTest() - { - CheckEquality( - SqlDml.DateMinusDate(DefaultDateOnly, SecondDateOnly), - DefaultDateTime - SecondDateTime); - } - [Test] public virtual void DateOnlyExtractYearTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Year, DefaultDateOnly), + SqlDml.Extract(SqlDatePart.Year, DefaultDateOnly), DefaultDateOnly.Year); } @@ -201,7 +193,7 @@ public virtual void DateOnlyExtractYearTest() public virtual void DateOnlyExtractMonthTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Month, DefaultDateOnly), + SqlDml.Extract(SqlDatePart.Month, DefaultDateOnly), DefaultDateOnly.Month); } @@ -209,7 +201,7 @@ public virtual void DateOnlyExtractMonthTest() public virtual void DateOnlyExtractDayTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Day, DefaultDateOnly), + SqlDml.Extract(SqlDatePart.Day, DefaultDateOnly), DefaultDateOnly.Day); } @@ -217,7 +209,7 @@ public virtual void DateOnlyExtractDayTest() public virtual void DateOnlyExtractDayOfWeekTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.DayOfWeek, DefaultDateOnly), + SqlDml.Extract(SqlDatePart.DayOfWeek, DefaultDateOnly), (int) DefaultDateOnly.DayOfWeek); } @@ -225,7 +217,7 @@ public virtual void DateOnlyExtractDayOfWeekTest() public virtual void DateOnlyExtractDayOfYearTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.DayOfYear, DefaultDateOnly), + SqlDml.Extract(SqlDatePart.DayOfYear, DefaultDateOnly), DefaultDateOnly.DayOfYear); } @@ -233,7 +225,7 @@ public virtual void DateOnlyExtractDayOfYearTest() public virtual void TimeOnlyExtractHourTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Hour, DefaultTimeOnly), + SqlDml.Extract(SqlTimePart.Hour, DefaultTimeOnly), DefaultTimeOnly.Hour); } @@ -241,7 +233,7 @@ public virtual void TimeOnlyExtractHourTest() public virtual void TimeOnlyExtractMinuteTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Minute, DefaultTimeOnly), + SqlDml.Extract(SqlTimePart.Minute, DefaultTimeOnly), DefaultTimeOnly.Minute); } @@ -249,7 +241,7 @@ public virtual void TimeOnlyExtractMinuteTest() public virtual void TimeOnlyExtractSecondTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Second, DefaultTimeOnly), + SqlDml.Extract(SqlTimePart.Second, DefaultTimeOnly), DefaultTimeOnly.Second); } @@ -257,7 +249,7 @@ public virtual void TimeOnlyExtractSecondTest() public virtual void TimeOnlyExtractMillisecondTest() { CheckEquality( - SqlDml.Extract(SqlDateTimePart.Millisecond, DefaultTimeOnly), + SqlDml.Extract(SqlTimePart.Millisecond, DefaultTimeOnly), DefaultTimeOnly.Millisecond); } @@ -268,6 +260,14 @@ public virtual void TimeOnlyConstructTest() SqlDml.TimeConstruct(DefaultTimeOnly.Hour, DefaultTimeOnly.Minute, DefaultTimeOnly.Second, DefaultTimeOnly.Millisecond), DefaultTimeOnly); } + + [Test] + public virtual void TimeOnlySubtractTimeOnlyTest() + { + CheckEquality( + SqlDml.TimeMinusTime(DefaultTimeOnly, SecondTimeOnly), + DefaultTimeOnly - SecondTimeOnly); + } #endif [Test] diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index b9c709152e..d6e645e9ce 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -117,11 +117,11 @@ public static SqlExpression DateOnlyOperatorLessThanOrEqual( [Compiler(typeof(DateOnly), "AddYears")] public static SqlExpression DateOnlyAddYears(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => - SqlDml.DateTimeAddYears(_this, value); + SqlDml.DateAddYears(_this, value); [Compiler(typeof(DateOnly), "AddMonths")] public static SqlExpression DateOnlyAddMonths(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => - SqlDml.DateTimeAddMonths(_this, value); + SqlDml.DateAddMonths(_this, value); [Compiler(typeof(DateOnly), "AddDays")] public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(int))] SqlExpression value) => diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index 888e79f9b4..f700ba126f 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -75,58 +75,58 @@ public static SqlExpression TimeOnlyCtor( [Compiler(typeof(TimeOnly), Operator.Equality, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorEquality( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - return d1 == d2; + return t1 == t2; } [Compiler(typeof(TimeOnly), Operator.Inequality, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorInequality( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - return d1 != d2; + return t1 != t2; } [Compiler(typeof(TimeOnly), Operator.GreaterThan, TargetKind.Operator)] public static SqlExpression TimeOnlyyOperatorGreaterThan( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - return d1 > d2; + return t1 > t2; } [Compiler(typeof(TimeOnly), Operator.GreaterThanOrEqual, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorGreaterThanOrEqual( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - return d1 >= d2; + return t1 >= t2; } [Compiler(typeof(TimeOnly), Operator.LessThan, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorLessThan( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - return d1 < d2; + return t1 < t2; } [Compiler(typeof(TimeOnly), Operator.LessThanOrEqual, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorLessThanOrEqual( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - return d1 <= d2; + return t1 <= t2; } [Compiler(typeof(TimeOnly), Operator.Subtraction, TargetKind.Operator)] public static SqlExpression TimeOnlyOperatorSubtraction( - [Type(typeof(TimeOnly))] SqlExpression d1, - [Type(typeof(TimeOnly))] SqlExpression d2) + [Type(typeof(TimeOnly))] SqlExpression t1, + [Type(typeof(TimeOnly))] SqlExpression t2) { - throw new NotImplementedException(); + return SqlDml.TimeMinusTime(t1, t2); } #endregion @@ -139,6 +139,10 @@ public static SqlExpression TimeOnlyAddHours(SqlExpression _this, [Type(typeof(d public static SqlExpression TimeOnlyAddMinutes(SqlExpression _this, [Type(typeof(double))] SqlExpression value) => SqlDml.TimeAddMinutes(_this, value); + [Compiler(typeof(TimeOnly), "Add")] + public static SqlExpression TimeOnlyAdd(SqlExpression _this, [Type(typeof(TimeSpan))] SqlExpression value) => + SqlDml.TimePlusInterval(_this, value); + [Compiler(typeof(TimeOnly), "ToString")] public static SqlExpression TimeOnlyToStringIso(SqlExpression _this) { diff --git a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs index 2082832491..499ca9f999 100644 --- a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs +++ b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs @@ -180,6 +180,10 @@ public static bool IsArithmeticExpression(SqlExpression node) case SqlNodeType.DateTimeOffsetMinusInterval: case SqlNodeType.DateTimeOffsetPlusInterval: case SqlNodeType.DateTimeOffsetMinusDateTimeOffset: +#if NET6_0_OR_GREATER + case SqlNodeType.TimePlusInterval: + case SqlNodeType.TimeMinusTime: +#endif return true; case SqlNodeType.Variant: var variant = (SqlVariant) node; diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index a8169364e0..13875038a4 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -648,28 +648,35 @@ public static SqlBinary DateTimePlusInterval(SqlExpression left, SqlExpression r return new SqlBinary(SqlNodeType.DateTimePlusInterval, left, right); } - public static SqlBinary DateTimeMinusInterval(SqlExpression left, SqlExpression right) +#if NET6_0_OR_GREATER //DO_DATEONLY + public static SqlBinary TimePlusInterval(SqlExpression left, SqlExpression right) { ArgumentValidator.EnsureArgumentNotNull(left, "left"); ArgumentValidator.EnsureArgumentNotNull(right, "right"); - return new SqlBinary(SqlNodeType.DateTimeMinusInterval, left, right); + return new SqlBinary(SqlNodeType.TimePlusInterval, left, right); } - public static SqlBinary DateTimeMinusDateTime(SqlExpression left, SqlExpression right) + public static SqlBinary TimeMinusTime(SqlExpression left, SqlExpression right) { ArgumentValidator.EnsureArgumentNotNull(left, "left"); ArgumentValidator.EnsureArgumentNotNull(right, "right"); - return new SqlBinary(SqlNodeType.DateTimeMinusDateTime, left, right); + return new SqlBinary(SqlNodeType.TimeMinusTime, left, right); } +#endif -#if NET6_0_OR_GREATER //DO_DATEONLY - public static SqlBinary DateMinusDate(SqlExpression left, SqlExpression right) + public static SqlBinary DateTimeMinusInterval(SqlExpression left, SqlExpression right) { ArgumentValidator.EnsureArgumentNotNull(left, "left"); ArgumentValidator.EnsureArgumentNotNull(right, "right"); - return new SqlBinary(SqlNodeType.DateMinusDate, left, right); + return new SqlBinary(SqlNodeType.DateTimeMinusInterval, left, right); + } + + public static SqlBinary DateTimeMinusDateTime(SqlExpression left, SqlExpression right) + { + ArgumentValidator.EnsureArgumentNotNull(left, "left"); + ArgumentValidator.EnsureArgumentNotNull(right, "right"); + return new SqlBinary(SqlNodeType.DateTimeMinusDateTime, left, right); } -#endif public static SqlFunctionCall DateTimeAddYears(SqlExpression source, SqlExpression years) { diff --git a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs index 67454150e7..4487571a47 100644 --- a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs +++ b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs @@ -47,6 +47,10 @@ public enum SqlNodeType DateTimeOffsetPlusInterval, DateTimeOffsetMinusInterval, DateTimeOffsetMinusDateTimeOffset, +#if NET6_0_OR_GREATER //DO_DATEONLY + TimePlusInterval, + TimeMinusTime, +#endif DeclareCursor, DefaultValue, Delete, @@ -113,6 +117,5 @@ public enum SqlNodeType While, Fragment, Metadata, - DateMinusDate, } } \ No newline at end of file From 2d80867f7006686015c6b53d8d7c755df1942302 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 14 Feb 2023 15:16:03 +0500 Subject: [PATCH 52/86] Date/Time operations support AddXxx() methods, operators, .ToString() support - for SQLite: fixed reading of DateOnly/TimeOnly values from DbDataReader - for MySQL: return default reading from DbDataReader - other minor changes(formatting, copyright, etc.) --- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 65 ++- .../Sql.Drivers.Firebird/v2_5/Translator.cs | 5 +- .../Sql.Drivers.MySql/v5_0/Compiler.cs | 142 ++++-- .../Sql.Drivers.MySql/v5_0/Translator.cs | 14 +- .../Sql.Drivers.MySql/v5_0/TypeMapper.cs | 13 - .../Sql.Drivers.MySql/v5_6/Compiler.cs | 51 ++- .../Sql.Drivers.Oracle/v09/Compiler.cs | 407 ++++++++++-------- .../Sql.Drivers.Oracle/v09/Translator.cs | 14 +- .../Sql.Drivers.PostgreSql/v10_0/Compiler.cs | 37 +- .../Sql.Drivers.PostgreSql/v8_0/Compiler.cs | 86 +++- .../Sql.Drivers.PostgreSql/v8_0/Translator.cs | 7 +- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 67 ++- .../Sql.Drivers.SqlServer/v10/Compiler.cs | 10 +- .../Sql.Drivers.SqlServer/v13/Compiler.cs | 49 ++- .../Sql.Drivers.Sqlite/v3/Compiler.cs | 95 +++- .../Sql.Drivers.Sqlite/v3/Translator.cs | 6 + .../Sql.Drivers.Sqlite/v3/TypeMapper.cs | 4 +- .../MemberCompilers/DateOnlyCompilers.cs | 12 +- .../MemberCompilers/DateTimeCompilers.cs | 7 +- .../MemberCompilers/TimeOnlyCompilers.cs | 14 +- .../MemberCompilers/TimeSpanCompilers.cs | 9 +- Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 2 + Orm/Xtensive.Orm/Sql/SqlDml.cs | 12 + Orm/Xtensive.Orm/Strings.Designer.cs | 40 +- Orm/Xtensive.Orm/Strings.resx | 14 +- 25 files changed, 847 insertions(+), 335 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index c21a22ec76..b13ac7edc1 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2022 Xtensive LLC. +// Copyright (C) 2011-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Csaba Beer @@ -176,6 +176,14 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeMinusDateTime: DateTimeSubtractDateTime(node.Left, node.Right).AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: + TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.TimeMinusTime: + TimeSubtractTime(node.Left, node.Right).AcceptVisitor(this); + return; +#endif case SqlNodeType.Modulo: Visit(SqlDml.FunctionCall(translator.TranslateToString(SqlNodeType.Modulo), node.Left, node.Right)); return; @@ -217,11 +225,6 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); return; -#if NET6_0_OR_GREATER //DO_DATEONLY - case SqlFunctionType.DateAddDays: - Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); - return; -#endif case SqlFunctionType.DateTimeConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime), node.Arguments[0] - 2001), @@ -229,20 +232,40 @@ public override void Visit(SqlFunctionCall node) node.Arguments[2] - 1)); return; #if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateAddYears: + Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); + return; + case SqlFunctionType.DateAddMonths: + Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); + return; + case SqlFunctionType.DateAddDays: + Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); + return; case SqlFunctionType.DateConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateOnly(2001, 1, 1)), SqlType.Date), node.Arguments[0] - 2001), node.Arguments[1] - 1), node.Arguments[2] - 1)); return; - case SqlFunctionType.TimeConstruct: { + case SqlFunctionType.TimeAddHours: + Visit(DateAddHour(node.Arguments[0], node.Arguments[1])); + return; + case SqlFunctionType.TimeAddMinutes: + Visit(DateAddMinute(node.Arguments[0], node.Arguments[1])); + return; + case SqlFunctionType.TimeConstruct: Visit(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Cast(SqlDml.Literal(new TimeOnly(0, 0, 0)), SqlType.Time), node.Arguments[0]), node.Arguments[1]), node.Arguments[2]), node.Arguments[3])); return; - } + case SqlFunctionType.DateToString: + Visit(DateToString(node.Arguments[0])); + return; + case SqlFunctionType.TimeToString: + Visit(TimeToString(node.Arguments[0])); + return; #endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(node.Arguments[0])); @@ -262,7 +285,7 @@ public override void Visit(SqlAlterSequence node) translator.Translate(context, node, NodeSection.Exit); } -#region Static helpers + #region Static helpers protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) { @@ -272,6 +295,15 @@ protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, Sql NanosecondsPerMillisecond); } +#if NET6_0_OR_GREATER //DO_DATEONLY + protected static SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) + { + return SqlDml.Modulo( + NanosecondsPerDay + CastToLong(DateDiffMillisecond(time2, time1)) * NanosecondsPerMillisecond, + NanosecondsPerDay); + } +#endif + protected static SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpression interval) { return DateAddMillisecond( @@ -279,6 +311,9 @@ protected static SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpres (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); } + protected static SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) => + DateAddMillisecond(time, (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); + protected static SqlCast CastToLong(SqlExpression arg) => SqlDml.Cast(arg, SqlType.Int64); protected static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) => @@ -320,6 +355,18 @@ protected static SqlUserFunctionCall BitXor(SqlExpression left, SqlExpression ri protected static SqlUserFunctionCall BitNot(SqlExpression operand) => SqlDml.FunctionCall("BIN_NOT", operand); +#if NET6_0_OR_GREATER //DO_DATEONLY + protected static SqlFunctionCall DateToString(SqlExpression date) + { + return SqlDml.Substring(date, 0, 10);; + } + + protected static SqlConcat TimeToString(SqlExpression time) + { + return SqlDml.Concat(SqlDml.Substring(time, 0, 12), SqlDml.Literal("0000")); + } +#endif + protected static SqlConcat DateTimeToStringIso(SqlExpression dateTime) { var date = SqlDml.Substring(dateTime, 0, 10); diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs index 71b15559dd..3e2358a5a2 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2022 Xtensive LLC. +// Copyright (C) 2011-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Csaba Beer @@ -122,6 +122,9 @@ public override void Translate(IOutput output, SqlNodeType type) case SqlNodeType.Modulo: _ = output.Append("MOD"); break; case SqlNodeType.DateTimeMinusDateTime: +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimeMinusTime: +#endif _ = output.Append("-"); break; case SqlNodeType.Except: case SqlNodeType.Intersect: diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs index c10a370ca1..aca48c3dd0 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2022 Xtensive LLC. +// Copyright (C) 2011-2023 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 @@ -94,6 +94,14 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeMinusInterval: DateTimeAddInterval(node.Left, -node.Right).AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: + TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.TimeMinusTime: + TimeSubtractTime(node.Left, node.Right).AcceptVisitor(this); + return; +#endif default: base.Visit(node); return; @@ -137,13 +145,14 @@ public override void Visit(SqlQueryExpression node) /// public override void Visit(SqlFunctionCall node) { + var arguments = node.Arguments; + switch (node.FunctionType) { case SqlFunctionType.Truncate: - var argument = node.Arguments[0]; - SqlDml.FunctionCall("TRUNCATE", argument, SqlDml.Literal(0)).AcceptVisitor(this); + SqlDml.FunctionCall("TRUNCATE", arguments[0], SqlDml.Literal(0)).AcceptVisitor(this); return; case SqlFunctionType.Concat: - Visit(SqlDml.Concat(node.Arguments.ToArray(node.Arguments.Count))); + Visit(SqlDml.Concat(arguments.ToArray(node.Arguments.Count))); return; case SqlFunctionType.CharLength: SqlDml.FunctionCall(translator.TranslateToString(SqlFunctionType.CharLength), node.Arguments[0]).AcceptVisitor(this); @@ -157,44 +166,75 @@ public override void Visit(SqlFunctionCall node) SqlDml.FunctionCall(translator.TranslateToString(SqlFunctionType.Rand)).AcceptVisitor(this); return; case SqlFunctionType.Square: - SqlDml.Power(node.Arguments[0], 2).AcceptVisitor(this); + SqlDml.Power(arguments[0], 2).AcceptVisitor(this); return; case SqlFunctionType.IntervalToMilliseconds: - Visit(CastToLong(node.Arguments[0]) / NanosecondsPerMillisecond); + Visit(CastToLong(arguments[0]) / NanosecondsPerMillisecond); return; case SqlFunctionType.IntervalConstruct: case SqlFunctionType.IntervalToNanoseconds: - Visit(CastToLong(node.Arguments[0])); + Visit(CastToLong(arguments[0])); return; case SqlFunctionType.DateTimeAddMonths: - Visit(DateTimeAddMonth(node.Arguments[0], node.Arguments[1])); + Visit(DateTimeAddMonth(arguments[0], arguments[1])); return; case SqlFunctionType.DateTimeAddYears: - Visit(DateTimeAddYear(node.Arguments[0], node.Arguments[1])); + Visit(DateTimeAddYear(arguments[0], arguments[1])); return; case SqlFunctionType.DateTimeConstruct: Visit(DateTimeAddDay(DateTimeAddMonth(DateTimeAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), - node.Arguments[0] - 2001), - node.Arguments[1] - 1), - node.Arguments[2] - 1)); + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1)); return; #if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateAddYears: + Visit(DateAddYear(arguments[0], arguments[1])); + return; + case SqlFunctionType.DateAddMonths: + Visit(DateAddMonth(arguments[0], arguments[1])); + return; + case SqlFunctionType.DateAddDays: + Visit(DateAddDay(arguments[0], arguments[1])); + return; case SqlFunctionType.DateConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), - node.Arguments[0] - 2001), - node.Arguments[1] - 1), - node.Arguments[2] - 1)); + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1)); + return; + case SqlFunctionType.TimeAddHours: + Visit(SqlDml.FunctionCall("TIME", SqlDml.FunctionCall( + "DATE_ADD", + SqlDml.Literal(new DateTime(2001, 1, 1)), + SqlDml.RawConcat( + SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.FunctionCall("TIME_TO_SEC", arguments[0]) + arguments[1] * 3600), + SqlDml.Native("SECOND"))))); + return; + case SqlFunctionType.TimeAddMinutes: + Visit(SqlDml.FunctionCall("TIME", + SqlDml.FunctionCall("DATE_ADD", + SqlDml.Literal(new DateTime(2001, 1, 1)), + SqlDml.RawConcat( + SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.FunctionCall("TIME_TO_SEC", arguments[0]) + arguments[1] * 60), + SqlDml.Native("SECOND"))))); return; case SqlFunctionType.TimeConstruct: Visit(SqlDml.FunctionCall("TIME", TimeAddMillisecond(TimeAddSecond(TimeAddMinute(TimeAddHour(SqlDml.Literal(new DateTime(2001, 1, 1)), - node.Arguments[0]), - node.Arguments[1]), - node.Arguments[2]), - node.Arguments[3]))); + arguments[0]), + arguments[1]), + arguments[2]), + arguments[3]))); + return; + case SqlFunctionType.DateToString: + Visit(DateToString(arguments[0])); + return; + case SqlFunctionType.TimeToString: + Visit(TimeToString(arguments[0])); return; #endif case SqlFunctionType.DateTimeToStringIso: - Visit(DateTimeToStringIso(node.Arguments[0])); + Visit(DateTimeToStringIso(arguments[0])); return; } @@ -204,7 +244,7 @@ public override void Visit(SqlFunctionCall node) #if NET6_0_OR_GREATER //DO_DATEONLY public override void Visit(SqlPlaceholder node) { - if(node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type==typeof(TimeOnly)) { + if (node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type == typeof(TimeOnly)) { _ = context.Output.Append("TIME("); base.Visit(node); _ = context.Output.Append(")"); @@ -263,51 +303,81 @@ protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpre (interval / NanosecondsPerMillisecond * NanosecondsPerMicrosecond) % (MillisecondsPerDay * NanosecondsPerMicrosecond)); } +#if NET6_0_OR_GREATER //DO_DATEONLY + protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) => + SqlDml.Modulo( + NanosecondsPerDay + CastToDecimal(SqlDml.FunctionCall("TIME_TO_SEC", time1) - SqlDml.FunctionCall("TIME_TO_SEC", time2), 18, 0) * NanosecondsPerSecond, + NanosecondsPerDay); + + protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) => + + SqlDml.FunctionCall("TIME", + SqlDml.FunctionCall( + "DATE_ADD", + SqlDml.Literal(new DateTime(2001, 1, 1)), + SqlDml.RawConcat( + SqlDml.RawConcat(SqlDml.Native("INTERVAL "), + SqlDml.FunctionCall("TIME_TO_SEC", time) + interval / NanosecondsPerSecond), + SqlDml.Native("SECOND")))); +#endif + #region Static helpers - private static SqlCast CastToLong(SqlExpression arg) => SqlDml.Cast(arg, SqlType.Int64); + protected static SqlCast CastToLong(SqlExpression arg) => SqlDml.Cast(arg, SqlType.Int64); - private static SqlCast CastToDecimal(SqlExpression arg, short precision, short scale) => + protected static SqlCast CastToDecimal(SqlExpression arg, short precision, short scale) => SqlDml.Cast(arg, SqlType.Decimal, precision, scale); - private static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) => + protected static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) => SqlDml.FunctionCall("DATEDIFF", date1, date2); - private static SqlUserFunctionCall DateTimeDiffMicrosecond(SqlExpression datetime1, SqlExpression datetime2) => + protected static SqlUserFunctionCall DateTimeDiffMicrosecond(SqlExpression datetime1, SqlExpression datetime2) => SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("MICROSECOND"), datetime1, datetime2); - private static SqlUserFunctionCall DateTimeAddYear(SqlExpression datetime, SqlExpression years) => + protected static SqlUserFunctionCall DateTimeDiffSecond(SqlExpression datetime1, SqlExpression datetime2) => + SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("SECOND"), datetime1, datetime2); + + protected static SqlUserFunctionCall DateTimeAddYear(SqlExpression datetime, SqlExpression years) => SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("YEAR"), years, datetime); - private static SqlUserFunctionCall DateTimeAddMonth(SqlExpression datetime, SqlExpression months) => + protected static SqlUserFunctionCall DateTimeAddMonth(SqlExpression datetime, SqlExpression months) => SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MONTH"), months, datetime); - private static SqlUserFunctionCall DateTimeAddDay(SqlExpression datetime, SqlExpression days) => + protected static SqlUserFunctionCall DateTimeAddDay(SqlExpression datetime, SqlExpression days) => SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("DAY"), days, datetime); - private static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) => + protected static SqlUserFunctionCall DateTimeAddHour(SqlExpression datetime, SqlExpression days) => + SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("HOUR"), days, datetime); + + protected static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) => SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, datetime); #if NET6_0_OR_GREATER //DO_DATEONLY - private static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) => + protected static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) => SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(years, SqlDml.Native("YEAR")))); - private static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) => + protected static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) => SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(months, SqlDml.Native("MONTH")))); - private static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) => + protected static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) => SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(days, SqlDml.Native("DAY")))); - private static SqlUserFunctionCall TimeAddHour(SqlExpression time, SqlExpression hours) => + protected static SqlUserFunctionCall TimeAddHour(SqlExpression time, SqlExpression hours) => SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(hours, SqlDml.Native("HOUR")))); - private static SqlUserFunctionCall TimeAddMinute(SqlExpression time, SqlExpression minutes) => + protected static SqlUserFunctionCall TimeAddMinute(SqlExpression time, SqlExpression minutes) => SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(minutes, SqlDml.Native("MINUTE")))); - private static SqlUserFunctionCall TimeAddSecond(SqlExpression time, SqlExpression seconds) => + protected static SqlUserFunctionCall TimeAddSecond(SqlExpression time, SqlExpression seconds) => SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(seconds, SqlDml.Native("SECOND")))); - private static SqlUserFunctionCall TimeAddMillisecond(SqlExpression time, SqlExpression millisecond) => + protected static SqlUserFunctionCall TimeAddMillisecond(SqlExpression time, SqlExpression millisecond) => SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(millisecond * 1000, SqlDml.Native("MICROSECOND")))); + + protected static SqlUserFunctionCall DateToString(SqlExpression dateTime) => + SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%Y-%m-%d"); + + protected static SqlUserFunctionCall TimeToString(SqlExpression dateTime) => + SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%H:%i:%s.%f0"); #endif protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) => diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs index d421bf9880..69e7c5190b 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2022 Xtensive LLC. +// Copyright (C) 2011-2023 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 @@ -111,6 +111,9 @@ public override void Translate(IOutput output, SqlFunctionType type) case SqlFunctionType.DateTimeAddMonths: case SqlFunctionType.DateTimeConstruct: #if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateAddYears: + case SqlFunctionType.DateAddMonths: + case SqlFunctionType.DateAddDays: case SqlFunctionType.DateConstruct: case SqlFunctionType.TimeConstruct: #endif @@ -166,10 +169,17 @@ public override void Translate(IOutput output, SqlNodeType type) { switch (type) { case SqlNodeType.Concat: _ = output.Append(","); break; - case SqlNodeType.DateTimePlusInterval: _ = output.Append("+"); + case SqlNodeType.DateTimePlusInterval: +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: +#endif + _ = output.Append("+"); break; case SqlNodeType.DateTimeMinusInterval: case SqlNodeType.DateTimeMinusDateTime: +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimeMinusTime: +#endif _ = output.Append("-"); break; case SqlNodeType.Equals: _ = output.Append("="); break; case SqlNodeType.NotEquals: _ = output.Append("<>"); break; diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs index e2e2128241..35928be8d2 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs @@ -129,19 +129,6 @@ public override object ReadGuid(DbDataReader reader, int index) return SqlHelper.GuidFromString(reader.GetString(index)); } -#if NET6_0_OR_GREATER - public override object ReadDateOnly(DbDataReader reader, int index) - { - return reader.GetFieldValue(index); - } - - public override object ReadTimeOnly(DbDataReader reader, int index) - { - return reader.GetFieldValue(index); - } - -#endif - // Constructors [SecuritySafeCritical] diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs index 9a045bbc00..5fa5e73e70 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs @@ -12,20 +12,59 @@ namespace Xtensive.Sql.Drivers.MySql.v5_6 internal class Compiler : v5_5.Compiler { #if NET6_0_OR_GREATER //DO_DATEONLY + public override void Visit(SqlBinary node) + { + if (node.NodeType == SqlNodeType.TimePlusInterval) { + SqlDml.FunctionCall("TIME", TimeAddInterval(SqlDml.Cast(node.Left, SqlType.DateTime), node.Right)).AcceptVisitor(this); + } + else { + base.Visit(node); + } + } + public override void Visit(SqlFunctionCall node) { - if (node.FunctionType == SqlFunctionType.TimeConstruct) { - var arguments = node.Arguments; - Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3])); - return; + var arguments = node.Arguments; + switch (node.FunctionType) { + case SqlFunctionType.TimeAddHours: + Visit( + SqlDml.FunctionCall("TIME", + SqlDml.FunctionCall("ADDTIME", + SqlDml.Cast(arguments[0], SqlType.DateTime), + arguments[1] * 10000))); // 10000 = 1:00:00 :) + return; + case SqlFunctionType.TimeAddMinutes: + Visit( + SqlDml.FunctionCall("TIME", + SqlDml.FunctionCall("ADDTIME", + SqlDml.Cast(arguments[0], SqlType.DateTime), + arguments[1] * 100))); // 100 = 0:01:00 :) + return; + case SqlFunctionType.TimeConstruct: { + Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3])); + return; + } + default: + base.Visit(node); + return; } - base.Visit(node); } + protected override SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) + { + var timeAsDate = SqlDml.Cast(time, SqlType.DateTime); + return DateTimeAddMicrosecond(timeAsDate, + (interval / NanosecondsPerMillisecond * NanosecondsPerMicrosecond) % (MillisecondsPerDay * NanosecondsPerMicrosecond)); + } + + protected override SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) => + SqlDml.Modulo( + NanosecondsPerDay + CastToDecimal(DateTimeDiffMicrosecond(time2, time1), 18, 0) * NanosecondsPerMicrosecond, + NanosecondsPerDay); + protected SqlUserFunctionCall MakeTime( SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) => SqlDml.FunctionCall("MAKETIME", hours, minutes, seconds + (milliseconds / 1000)); - #endif // Constructors diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index e39d9beea9..57e770b420 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -11,81 +11,130 @@ using Xtensive.Sql.Dml; using Xtensive.Sql.Model; using Xtensive.Sql.Drivers.Oracle.Resources; -using System.Linq; namespace Xtensive.Sql.Drivers.Oracle.v09 { internal class Compiler : SqlCompiler { + protected const string DayPartFormat = "DD"; + protected const string HourPartFormat = "HH24"; + protected const string MillisecondPartFormat = "FF3"; + protected const string NanosecondPartFormat = "FF9"; + protected const string MinutePartFormat = "MI"; + protected const string MonthPartFormat = "MM"; + protected const string SecondPartFormat = "SS"; + protected const string YearPartFormat = "YYYY"; + protected const string TimeZoneHourPartFormat = "TZH"; + protected const string TimeZoneMinutePartFormat = "TZM"; + protected const string DayOfYearPartFormat = "DDD"; + protected const string DayOfWeekPartFormat = "D"; + + protected const string YearIntervalPart = "YEAR"; + protected const string MonthIntervalPart = "MONTH"; + protected const string DayIntervalPart = "DAY"; + protected const string HourIntervalPart = "HOUR"; + protected const string MinuteIntervalPart = "MINUTE"; + protected const string SecondIntervalPart = "SECOND"; + + protected const string ToCharFunctionName = "TO_CHAR"; + private const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL"; private static readonly SqlExpression SundayNumber = SqlDml.Native( "TO_NUMBER(TO_CHAR(TIMESTAMP '2009-07-26 00:00:00.000', 'D'))"); + private static readonly SqlNative RefTimestamp = SqlDml.Native("timestamp '2009-01-01 00:00:00.000000'"); public override void Visit(SqlFunctionCall node) { switch (node.FunctionType) { - case SqlFunctionType.PadLeft: - case SqlFunctionType.PadRight: - SqlHelper.GenericPad(node).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeOffsetAddYears: - case SqlFunctionType.DateTimeAddYears: - DateTimeAddComponent(node.Arguments[0], node.Arguments[1], true).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeOffsetAddMonths: - case SqlFunctionType.DateTimeAddMonths: - DateTimeAddComponent(node.Arguments[0], node.Arguments[1], false).AcceptVisitor(this); - return; - case SqlFunctionType.IntervalConstruct: - IntervalConstruct(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeConstruct: - DateTimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); - return; + case SqlFunctionType.PadLeft: + case SqlFunctionType.PadRight: + SqlHelper.GenericPad(node).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetAddYears: + case SqlFunctionType.DateTimeAddYears: + DateTimeAddYMInterval(node.Arguments[0], node.Arguments[1], YearIntervalPart).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetAddMonths: + case SqlFunctionType.DateTimeAddMonths: + DateTimeAddYMInterval(node.Arguments[0], node.Arguments[1], MonthIntervalPart).AcceptVisitor(this); + return; + case SqlFunctionType.IntervalConstruct: + IntervalConstruct(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeConstruct: + DateTimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); + return; #if NET6_0_OR_GREATER //DO_DATEONLY - case SqlFunctionType.DateConstruct: - DateConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); - return; - case SqlFunctionType.TimeConstruct: { - TimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2], node.Arguments[3]).AcceptVisitor(this); - return; - } + case SqlFunctionType.DateConstruct: + DateConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeConstruct: + TimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2], node.Arguments[3]).AcceptVisitor(this); + return; + case SqlFunctionType.DateAddYears: { + DateTimeAddYMInterval(node.Arguments[0], node.Arguments[1], YearIntervalPart).AcceptVisitor(this); + return; + } + case SqlFunctionType.DateAddMonths: { + DateTimeAddYMInterval(node.Arguments[0], node.Arguments[1], MonthIntervalPart).AcceptVisitor(this); + return; + } + case SqlFunctionType.DateAddDays: { + DateTimeAddDSInterval(node.Arguments[0], node.Arguments[1], DayIntervalPart).AcceptVisitor(this); + return; + } + case SqlFunctionType.TimeAddHours: { + TimeAddHourOrMinute(node.Arguments[0], node.Arguments[1], true).AcceptVisitor(this); + return; + } + case SqlFunctionType.TimeAddMinutes: { + TimeAddHourOrMinute(node.Arguments[0], node.Arguments[1], false).AcceptVisitor(this); + return; + } + case SqlFunctionType.DateToString: { + DateToString(node.Arguments[0]).AcceptVisitor(this); + return; + } + case SqlFunctionType.TimeToString: { + TimeToString(node.Arguments[0]).AcceptVisitor(this); + return; + } #endif - case SqlFunctionType.IntervalAbs: - SqlHelper.IntervalAbs(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.IntervalToMilliseconds: - SqlHelper.IntervalToMilliseconds(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.IntervalToNanoseconds: - SqlHelper.IntervalToNanoseconds(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.Position: - Position(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); - return; - case SqlFunctionType.CharLength: - SqlDml.Coalesce(SqlDml.FunctionCall("LENGTH", node.Arguments[0]), 0).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeToStringIso: - DateTimeToStringIso(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeOffsetConstruct: - DateTimeOffsetConstruct(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeOffsetTimeOfDay: - DateTimeOffsetTimeOfDay(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeOffsetToLocalTime: - DateTimeOffsetToLocalTime(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeToDateTimeOffset: - DateTimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); - return; - case SqlFunctionType.DateTimeOffsetToUtcTime: - DateTimeOffsetToUtcTime(node.Arguments[0]).AcceptVisitor(this); - return; - default: - base.Visit(node); - return; + case SqlFunctionType.IntervalAbs: + SqlHelper.IntervalAbs(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.IntervalToMilliseconds: + SqlHelper.IntervalToMilliseconds(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.IntervalToNanoseconds: + SqlHelper.IntervalToNanoseconds(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.Position: + Position(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.CharLength: + SqlDml.Coalesce(SqlDml.FunctionCall("LENGTH", node.Arguments[0]), 0).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToStringIso: + DateTimeToStringIso(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetConstruct: + DateTimeOffsetConstruct(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetTimeOfDay: + DateTimeOffsetTimeOfDay(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToLocalTime: + DateTimeOffsetToLocalTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToDateTimeOffset: + DateTimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToUtcTime: + DateTimeOffsetToUtcTime(node.Arguments[0]).AcceptVisitor(this); + return; + default: + base.Visit(node); + return; } } @@ -109,40 +158,40 @@ public override void Visit(SqlExtract node) { switch (node.DateTimeOffsetPart) { case SqlDateTimeOffsetPart.Day: - DateTimeOffsetExtractPart(node.Operand, "DD").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, DayPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Hour: - DateTimeOffsetExtractPart(node.Operand, "HH24").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, HourPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Millisecond: - DateTimeOffsetExtractPart(node.Operand, "FF3").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, MillisecondPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Nanosecond: - DateTimeOffsetExtractPart(node.Operand, "FF9").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, NanosecondPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Minute: - DateTimeOffsetExtractPart(node.Operand, "MI").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, MinutePartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Month: - DateTimeOffsetExtractPart(node.Operand, "MM").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, MonthPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Second: - DateTimeOffsetExtractPart(node.Operand, "SS").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, SecondPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Year: - DateTimeOffsetExtractPart(node.Operand, "YYYY").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, YearPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.TimeZoneHour: - DateTimeOffsetExtractPart(node.Operand, "TZH").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, TimeZoneHourPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.TimeZoneMinute: - DateTimeOffsetExtractPart(node.Operand, "TZM").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, TimeZoneMinutePartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.DayOfWeek: DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.DayOfYear: - DateTimeOffsetExtractPart(node.Operand, "DDD").AcceptVisitor(this); + DateTimeOffsetExtractPart(node.Operand, DayOfYearPartFormat).AcceptVisitor(this); return; case SqlDateTimeOffsetPart.Date: DateTimeOffsetTruncate(node.Operand).AcceptVisitor(this); @@ -249,58 +298,84 @@ public override void Visit(SqlUnary node) public override void Visit(SqlBinary node) { switch (node.NodeType) { - case SqlNodeType.Modulo: - SqlDml.FunctionCall("MOD", node.Left, node.Right).AcceptVisitor(this); - return; - case SqlNodeType.BitAnd: - BitAnd(node.Left, node.Right).AcceptVisitor(this); - return; - case SqlNodeType.BitOr: - BitOr(node.Left, node.Right).AcceptVisitor(this); - return; - case SqlNodeType.BitXor: - BitXor(node.Left, node.Right).AcceptVisitor(this); - return; - default: - base.Visit(node); - return; + case SqlNodeType.Modulo: + SqlDml.FunctionCall("MOD", node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.BitAnd: + BitAnd(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.BitOr: + BitOr(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.BitXor: + BitXor(node.Left, node.Right).AcceptVisitor(this); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: + TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.TimeMinusTime: + TimeAddInterval(node.Left, node.Right, true).AcceptVisitor(this); + return; +#endif + default: + base.Visit(node); + return; } } - private static SqlExpression DateTimeAddComponent(SqlExpression dateTime, SqlExpression units, bool isYear) - { - return dateTime + SqlDml.FunctionCall( - "NumToYmInterval", units, AnsiString(isYear ? "year" : "month")); - } + private static SqlExpression DateTimeAddYMInterval(SqlExpression dateTime, SqlExpression units, in string component) => + dateTime + SqlDml.FunctionCall(NumToDSIntervalFunctionName, units, AnsiString(component)); + + private static SqlExpression DateTimeAddDSInterval(SqlExpression dateTime, SqlExpression units, in string component) => + dateTime + SqlDml.FunctionCall(NumToDSIntervalFunctionName, units, AnsiString(component)); private static SqlExpression IntervalConstruct(SqlExpression nanoseconds) { const long nanosecondsPerSecond = 1000000000; - return SqlDml.FunctionCall("NumToDsInterval", - nanoseconds / SqlDml.Literal(nanosecondsPerSecond), AnsiString("second")); + return SqlDml.FunctionCall(NumToDSIntervalFunctionName, + nanoseconds / SqlDml.Literal(nanosecondsPerSecond), AnsiString(SecondIntervalPart)); } - private static SqlExpression DateTimeConstruct(SqlExpression years, SqlExpression months, SqlExpression days) - { - return SqlDml.FunctionCall("TO_TIMESTAMP", - SqlDml.FunctionCall("TO_CHAR", ((years * 100) + months) * 100 + days), + private static SqlExpression DateTimeConstruct(SqlExpression years, SqlExpression months, SqlExpression days) => + SqlDml.FunctionCall("TO_TIMESTAMP", + SqlDml.FunctionCall(ToCharFunctionName, ((years * 100) + months) * 100 + days), AnsiString("YYYYMMDD")); - } #if NET6_0_OR_GREATER //DO_DATEONLY - private static SqlExpression DateConstruct(SqlExpression years, SqlExpression months, SqlExpression days) - { - return SqlDml.FunctionCall("TO_DATE", - SqlDml.FunctionCall("TO_CHAR", ((years * 100) + months) * 100 + days), + private static SqlExpression DateConstruct(SqlExpression years, SqlExpression months, SqlExpression days) => + SqlDml.FunctionCall("TO_DATE", + SqlDml.FunctionCall(ToCharFunctionName, ((years * 100) + months) * 100 + days), AnsiString("YYYYMMDD")); + + private static SqlExpression TimeAddHourOrMinute(SqlExpression time, SqlExpression hourOrMinute, bool isHour) + { + var intervalLiteral = isHour ? "INTERVAL '1' HOUR" : "INTERVAL '1' MINUTE"; + return TimeAddInterval(time, hourOrMinute * SqlDml.Native(intervalLiteral)); } - private static SqlExpression TimeConstruct(SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) + private static SqlExpression TimeAddInterval(SqlExpression time, SqlExpression intervalToAdd, bool negateInterval = false) { - return SqlDml.FunctionCall("NUMTODSINTERVAL", - seconds + (minutes * 60) + (hours * 3600) + (milliseconds / 1000), - AnsiString("second")); + var baseOp = (negateInterval) ? RefTimestamp + time - intervalToAdd + : RefTimestamp + time + intervalToAdd; + var getTimeOnly = SqlDml.FunctionCall(ToCharFunctionName, baseOp, AnsiString("HH24:MI:SS.FF6")); + var pretendZeroDays = (SqlExpression) SqlDml.Concat(AnsiString("0 "), getTimeOnly); + var dsInterval = SqlDml.FunctionCall("TO_DSINTERVAL", pretendZeroDays); + var castToCorrectInterval = SqlDml.Cast(dsInterval, SqlType.Time); + return castToCorrectInterval; } + + private static SqlExpression TimeConstruct( + SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) => + SqlDml.FunctionCall(NumToDSIntervalFunctionName, + seconds + (minutes * 60) + (hours * 3600) + (milliseconds / 1000), + AnsiString(SecondIntervalPart)); + + private static SqlExpression DateToString(SqlExpression date) => + SqlDml.FunctionCall(ToCharFunctionName, date, "YYYY-MM-DD"); + + private static SqlExpression TimeToString(SqlExpression time) => + SqlDml.FunctionCall(ToCharFunctionName, RefTimestamp + time, AnsiString("HH24:MI:SS.FF7")); #endif private static SqlExpression DateTimeExtractDayOfWeek(SqlExpression dateTime) @@ -309,111 +384,75 @@ private static SqlExpression DateTimeExtractDayOfWeek(SqlExpression dateTime) // so sunday can be 1 or 7 // there is no equivalent for sqlserver's @@DATEFIRST function // so we need to emulate it with very stupid code - return (SqlDml.FunctionCall("TO_NUMBER", SqlDml.FunctionCall("TO_CHAR", dateTime, AnsiString("D"))) + 7 - SundayNumber) % 7; + return (SqlDml.FunctionCall("TO_NUMBER", SqlDml.FunctionCall(ToCharFunctionName, dateTime, AnsiString(DayOfWeekPartFormat))) + 7 - SundayNumber) % 7; } - private static SqlExpression DateTimeExtractDayOfYear(SqlExpression dateTime) - { - return SqlDml.FunctionCall("TO_NUMBER", SqlDml.FunctionCall("TO_CHAR", dateTime, AnsiString("DDD"))); - } + private static SqlExpression DateTimeExtractDayOfYear(SqlExpression dateTime) => + SqlDml.FunctionCall("TO_NUMBER", SqlDml.FunctionCall(ToCharFunctionName, dateTime, AnsiString(DayOfYearPartFormat))); - private static SqlExpression BitAnd(SqlExpression left, SqlExpression right) - { - return SqlDml.FunctionCall("BITAND", left, right); - } + private static SqlExpression BitAnd(SqlExpression left, SqlExpression right) => + SqlDml.FunctionCall("BITAND", left, right); - private static SqlExpression BitOr(SqlExpression left, SqlExpression right) - { - return left + right - BitAnd(left, right); - } + private static SqlExpression BitOr(SqlExpression left, SqlExpression right) => + left + right - BitAnd(left, right); - private static SqlExpression BitXor(SqlExpression left, SqlExpression right) - { - return BitOr(left, right) - BitAnd(left, right); - } + private static SqlExpression BitXor(SqlExpression left, SqlExpression right) => + BitOr(left, right) - BitAnd(left, right); - private static SqlExpression BitNot(SqlExpression operand) - { - return -1 - operand; - } + private static SqlExpression BitNot(SqlExpression operand) => -1 - operand; - private static SqlExpression Position(SqlExpression substring, SqlExpression _string) //TODO : look into this (Malisa) - { - return SqlDml.FunctionCall("INSTR", _string, substring) - 1; - } + private static SqlExpression Position(SqlExpression substring, SqlExpression _string) => //TODO : look into this (Malisa) + SqlDml.FunctionCall("INSTR", _string, substring) - 1; - private static SqlExpression DateTimeToStringIso(SqlExpression dateTime) - { - return SqlDml.FunctionCall("To_Char", dateTime, "YYYY-MM-DD\"T\"HH24:MI:SS"); - } + private static SqlExpression DateTimeToStringIso(SqlExpression dateTime) => + SqlDml.FunctionCall(ToCharFunctionName, dateTime, "YYYY-MM-DD\"T\"HH24:MI:SS"); private static SqlExpression DateTimeOffsetConstruct(SqlExpression dateTime, SqlExpression offset) { - var offsetToInt = offset as SqlLiteral; + if (offset is not SqlLiteral intOffset) { + throw new InvalidOperationException(); + } + var offsetValue = intOffset.Value; return SqlDml.FunctionCall("FROM_TZ", dateTime, - AnsiString($"{((offsetToInt.Value < 0) ? "-" : "+")}{offsetToInt.Value / 60}:{offsetToInt.Value % 60}") + AnsiString($"{((offsetValue < 0) ? "-" : "+")}{offsetValue / 60}:{offsetValue % 60}") ); } - private static SqlExpression DateTimeOffsetPartOffset(SqlExpression dateTimeOffset) - { - return SqlDml.Cast(dateTimeOffset, SqlType.DateTime) + private static SqlExpression DateTimeOffsetPartOffset(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.DateTime) - SqlDml.Cast(DateTimeOffsetToUtcDateTime(dateTimeOffset), SqlType.DateTime); - } - private static SqlExpression DateTimeOffsetTimeOfDay(SqlExpression dateTimeOffset) - { - return SqlDml.Cast(dateTimeOffset, SqlType.DateTime) + private static SqlExpression DateTimeOffsetTimeOfDay(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.DateTime) - SqlDml.Truncate(SqlDml.Cast(dateTimeOffset, SqlType.DateTime)); - } - private static SqlExpression DateTimeOffsetTruncateOffset(SqlExpression dateTimeOffset) - { - return SqlDml.Cast(dateTimeOffset, SqlType.DateTime); - } - - private static SqlExpression DateTimeOffsetTruncate(SqlExpression dateTimeOffset) - { - return SqlDml.Truncate(dateTimeOffset); - } + private static SqlExpression DateTimeOffsetTruncateOffset(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.DateTime); - private static SqlExpression DateTimeOffsetToUtcDateTime(SqlExpression dateTimeOffset) - { - return SqlDml.FunctionCall("SYS_EXTRACT_UTC", dateTimeOffset); - } + private static SqlExpression DateTimeOffsetTruncate(SqlExpression dateTimeOffset) => + SqlDml.Truncate(dateTimeOffset); - private static SqlExpression DateTimeOffsetExtractPart(SqlExpression dateTimeOffset, string dateTimeOffsetPart) - { - return SqlDml.FunctionCall("TO_CHAR", dateTimeOffset, AnsiString(dateTimeOffsetPart)); - } + private static SqlExpression DateTimeOffsetToUtcDateTime(SqlExpression dateTimeOffset) => + SqlDml.FunctionCall("SYS_EXTRACT_UTC", dateTimeOffset); - private static SqlExpression DateTimeOffsetToLocalDateTime(SqlExpression dateTimeOffset) - { - return SqlDml.Cast(DateTimeOffsetToLocalTime(dateTimeOffset), SqlType.DateTime); - } + private static SqlExpression DateTimeOffsetExtractPart(SqlExpression dateTimeOffset, string dateTimeOffsetPart) => + SqlDml.FunctionCall(ToCharFunctionName, dateTimeOffset, AnsiString(dateTimeOffsetPart)); + private static SqlExpression DateTimeOffsetToLocalDateTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(DateTimeOffsetToLocalTime(dateTimeOffset), SqlType.DateTime); - private static SqlExpression DateTimeOffsetToLocalTime(SqlExpression dateTimeOffset) - { - return SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" AT LOCAL")); - } + private static SqlExpression DateTimeOffsetToLocalTime(SqlExpression dateTimeOffset) => + SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" AT LOCAL")); - private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) - { - return SqlDml.Cast(dateTime, SqlType.DateTimeOffset); - } + private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.DateTimeOffset); - private static SqlExpression DateTimeOffsetToUtcTime(SqlExpression dateTimeOffset) - { - return SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" at time zone 'UTC'")); - } + private static SqlExpression DateTimeOffsetToUtcTime(SqlExpression dateTimeOffset) => + SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" at time zone 'UTC'")); - private static SqlExpression AnsiString(string value) - { - return SqlDml.Native("'" + value + "'"); - } + private static SqlExpression AnsiString(string value) => SqlDml.Native("'" + value + "'"); // Constructors diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index 88c9be7c0e..b88fbf7dc7 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -35,7 +35,7 @@ internal class Translator : SqlTranslator public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)"; /// - public override string TimeOnlyFormatString => @"'(INTERVAL '\'0 HH\:mm\:ss\.fffff\'\ DAY(0) TO SECOND(6))"; + public override string TimeOnlyFormatString => @"'(INTERVAL '\'0 HH\:mm\:ss\.ffffff\'\ DAY(0) TO SECOND(6))"; #endif /// @@ -335,10 +335,19 @@ public override void Translate(SqlCompilerContext context, SqlCast node, NodeSec /// public override string Translate(SqlValueType type) { +#if NET6_0_OR_GREATER + // we need to explicitly specify maximum interval precision + return type.Type == SqlType.Interval + ? "INTERVAL DAY(6) TO SECOND(3)" + : type.Type == SqlType.Time + ? "INTERVAL DAY(0) TO SECOND(6)" + : base.Translate(type); +#else // we need to explicitly specify maximum interval precision return type.Type == SqlType.Interval ? "INTERVAL DAY(6) TO SECOND(3)" : base.Translate(type); +#endif } /// @@ -418,6 +427,9 @@ public override void Translate(IOutput output, SqlNodeType type) switch (type) { case SqlNodeType.DateTimeOffsetPlusInterval: case SqlNodeType.DateTimePlusInterval: +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: +#endif _ = output.Append("+"); break; case SqlNodeType.DateTimeOffsetMinusDateTimeOffset: case SqlNodeType.DateTimeOffsetMinusInterval: diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs index 60eab1c1e3..3c4dc5de19 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs @@ -1,13 +1,48 @@ -// Copyright (C) 2019-2020 Xtensive LLC. +// Copyright (C) 2019-2023 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: 2019.09.25 +using Xtensive.Sql.Dml; + namespace Xtensive.Sql.Drivers.PostgreSql.v10_0 { internal class Compiler : v9_1.Compiler { + public override void Visit(SqlFunctionCall node) + { + var arguments = node.Arguments; + switch (node.FunctionType) { + case SqlFunctionType.DateTimeConstruct: + Visit(MakeDateTime(arguments[0], arguments[1], arguments[2])); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateConstruct: + Visit(MakeDate(arguments[0], arguments[1], arguments[2])); + return; + case SqlFunctionType.TimeConstruct: + Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3])); + return; +#endif + default: + base.Visit(node); + return; + } + } + + protected static SqlUserFunctionCall MakeDateTime(SqlExpression year, SqlExpression month, SqlExpression day) => + SqlDml.FunctionCall("MAKE_TIMESTAMP", year, month, day, SqlDml.Literal(0.0), SqlDml.Literal(0.0), SqlDml.Literal(0.0)); + +#if NET6_0_OR_GREATER //DO_DATEONLY + protected static SqlUserFunctionCall MakeDate(SqlExpression year, SqlExpression month, SqlExpression day) => + SqlDml.FunctionCall("MAKE_DATE", year, month, day); + + protected static SqlUserFunctionCall MakeTime( + SqlExpression hours, SqlExpression minutes, SqlExpression seconds, SqlExpression milliseconds) => + SqlDml.FunctionCall("MAKE_TIME", hours, minutes, seconds + (SqlDml.Cast(milliseconds, SqlType.Double) / 1000)); +#endif + // Constructors public Compiler(SqlDriver driver) diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs index 03310c5d57..8d2b6965b6 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2003-2022 Xtensive LLC. +// Copyright (C) 2003-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. @@ -13,6 +13,12 @@ namespace Xtensive.Sql.Drivers.PostgreSql.v8_0 { internal class Compiler : SqlCompiler { + private const string DateTimeIsoFormat = "YYYY-MM-DD\"T\"HH24:MI:SS"; +#if NET6_0_OR_GREATER //DO_DATEONLY + private const string DateFormat = "YYYY-MM-DD"; + private const string TimeFormat = "HH24:MI:SS.US0"; +#endif + private readonly static Type SqlPlaceholderType = typeof(SqlPlaceholder); private static readonly SqlNative OneYearInterval = SqlDml.Native("interval '1 year'"); @@ -23,6 +29,12 @@ internal class Compiler : SqlCompiler private static readonly SqlNative OneMinuteInterval = SqlDml.Native("interval '1 minute'"); private static readonly SqlNative OneSecondInterval = SqlDml.Native("interval '1 second'"); + private static readonly SqlLiteral ReferenceDateTimeLiteral = SqlDml.Literal(new DateTime(2001, 1, 1)); +#if NET6_0_OR_GREATER //DO_DATEONLY + private static readonly SqlLiteral ReferenceDateLiteral = SqlDml.Literal(new DateOnly(2001, 1, 1)); + private static readonly SqlLiteral ZeroTimeLiteral = SqlDml.Literal(new TimeOnly(0, 0, 0)); +#endif + /// public override void Visit(SqlDeclareCursor node) { @@ -56,20 +68,30 @@ public override void Visit(SqlBinary node) var row = SqlDml.Row(right.GetValues().Select(value => SqlDml.Literal(value)).ToArray()); base.Visit(node.NodeType == SqlNodeType.In ? SqlDml.In(node.Left, row) : SqlDml.NotIn(node.Left, row)); } + return; } - else { - switch (node.NodeType) { - case SqlNodeType.DateTimeOffsetMinusDateTimeOffset: - (node.Left - node.Right).AcceptVisitor(this); - return; - case SqlNodeType.DateTimeOffsetMinusInterval: - (node.Left - node.Right).AcceptVisitor(this); - return; - case SqlNodeType.DateTimeOffsetPlusInterval: - (node.Left + node.Right).AcceptVisitor(this); - return; - } - base.Visit(node); + switch (node.NodeType) { + case SqlNodeType.DateTimeOffsetMinusDateTimeOffset: + (node.Left - node.Right).AcceptVisitor(this); + return; + case SqlNodeType.DateTimeOffsetMinusInterval: + (node.Left - node.Right).AcceptVisitor(this); + return; + case SqlNodeType.DateTimeOffsetPlusInterval: + (node.Left + node.Right).AcceptVisitor(this); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimeMinusTime: + SqlDml.Cast( + SqlDml.Cast( + (ReferenceDateLiteral + node.Left) - (ReferenceDateLiteral + node.Right), + SqlType.Time), + SqlType.Interval).AcceptVisitor(this); + return; +#endif + default: + base.Visit(node); + return; } } @@ -102,7 +124,7 @@ public override void Visit(SqlFunctionCall node) SqlHelper.IntervalAbs(node.Arguments[0]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeConstruct: - var newNode = SqlDml.Literal(new DateTime(2001, 1, 1)) + var newNode = ReferenceDateTimeLiteral + (OneYearInterval * (node.Arguments[0] - 2001)) + (OneMonthInterval * (node.Arguments[1] - 1)) + (OneDayInterval * (node.Arguments[2] - 1)); @@ -110,13 +132,13 @@ public override void Visit(SqlFunctionCall node) return; #if NET6_0_OR_GREATER //DO_DATEONLY case SqlFunctionType.DateConstruct: - (SqlDml.Literal(new DateOnly(2001, 1, 1)) + (ReferenceDateLiteral + (OneYearInterval * (node.Arguments[0] - 2001)) + (OneMonthInterval * (node.Arguments[1] - 1)) + (OneDayInterval * (node.Arguments[2] - 1))).AcceptVisitor(this); return; case SqlFunctionType.TimeConstruct: { - ((SqlDml.Literal(new TimeOnly(0, 0, 0)) + ((ZeroTimeLiteral + (OneHourInterval * (node.Arguments[0])) + (OneMinuteInterval * (node.Arguments[1])) + (OneSecondInterval * (node.Arguments[2] + (SqlDml.Cast(node.Arguments[3], SqlType.Double) / 1000))))).AcceptVisitor(this); @@ -132,8 +154,31 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: (node.Arguments[0] + node.Arguments[1] * OneYearInterval).AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateAddYears: + (node.Arguments[0] + node.Arguments[1] * OneYearInterval).AcceptVisitor(this); + return; + case SqlFunctionType.DateAddMonths: + (node.Arguments[0] + node.Arguments[1] * OneMonthInterval).AcceptVisitor(this); + return; + case SqlFunctionType.DateAddDays: + (node.Arguments[0] + node.Arguments[1] * OneDayInterval).AcceptVisitor(this); + return; + case SqlFunctionType.DateToString: + DateTimeToStringIso(node.Arguments[0], DateFormat).AcceptVisitor(this); + return; + case SqlFunctionType.TimeAddHours: + (node.Arguments[0] + node.Arguments[1] * OneHourInterval).AcceptVisitor(this); + return; + case SqlFunctionType.TimeAddMinutes: + (node.Arguments[0] + node.Arguments[1] * OneMinuteInterval).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToString: + DateTimeToStringIso(node.Arguments[0], TimeFormat).AcceptVisitor(this); + return; +#endif case SqlFunctionType.DateTimeToStringIso: - DateTimeToStringIso(node.Arguments[0]).AcceptVisitor(this); + DateTimeToStringIso(node.Arguments[0], DateTimeIsoFormat).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetTimeOfDay: DateTimeOffsetTimeOfDay(node.Arguments[0]).AcceptVisitor(this); @@ -223,9 +268,8 @@ public override void Visit(SqlCustomFunctionCall node) base.Visit(node); } - - private static SqlExpression DateTimeToStringIso(SqlExpression dateTime) => - SqlDml.FunctionCall("To_Char", dateTime, "YYYY-MM-DD\"T\"HH24:MI:SS"); + private static SqlExpression DateTimeToStringIso(SqlExpression dateTime, in string isoFormat) => + SqlDml.FunctionCall("TO_CHAR", dateTime, isoFormat); private static SqlExpression IntervalToIsoString(SqlExpression interval, bool signed) { diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs index 0b5a8a087f..bbca9ec06c 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs @@ -205,8 +205,14 @@ public override void Translate(IOutput output, SqlNodeType type) case SqlNodeType.Modulo: _ = output.Append("%"); break; case SqlNodeType.Overlaps: _ = output.Append("OVERLAPS"); break; case SqlNodeType.DateTimePlusInterval: _ = output.Append("+"); break; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: _ = output.Append("+"); break; +#endif case SqlNodeType.DateTimeMinusInterval: case SqlNodeType.DateTimeMinusDateTime: +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimeMinusTime: +#endif _ = output.Append("-"); break; default: base.Translate(output, type); break; }; @@ -874,7 +880,6 @@ public override void Translate(IOutput output, SqlTimePart part) base.Translate(output, part); } } - #endif /// diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index 44c50eab9b..bc1780aea1 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2022 Xtensive LLC. +// Copyright (C) 2009-2023 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 @@ -200,6 +200,12 @@ public override void Visit(SqlFunctionCall node) Visit(DateAddYear(arguments[0], arguments[1])); return; #if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateAddYears: + Visit(DateAddYear(arguments[0], arguments[1])); + return; + case SqlFunctionType.DateAddMonths: + Visit(DateAddMonth(arguments[0], arguments[1])); + return; case SqlFunctionType.DateAddDays: Visit(DateAddDay(arguments[0], arguments[1])); return; @@ -220,22 +226,25 @@ public override void Visit(SqlFunctionCall node) arguments[2] - 1)); return; #if NET6_0_OR_GREATER - case SqlFunctionType.DateConstruct: { + case SqlFunctionType.DateConstruct: Visit(SqlDml.Cast(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), arguments[0] - 2001), arguments[1] - 1), arguments[2] - 1), SqlType.Date)); return; - } - case SqlFunctionType.TimeConstruct: { + case SqlFunctionType.TimeConstruct: Visit(SqlDml.Cast(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Literal(new TimeOnly(0, 0, 0)), arguments[0]), arguments[1]), arguments[2]), arguments[3]), SqlType.Time)); return; - } - + case SqlFunctionType.DateToString: + Visit(DateToString(arguments[0])); + return; + case SqlFunctionType.TimeToString: + Visit(TimeToString(arguments[0])); + return; #endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(arguments[0])); @@ -309,6 +318,14 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeMinusInterval: DateTimeAddInterval(node.Left, -node.Right).AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: + TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.TimeMinusTime: + TimeSubtractTime(node.Left, node.Right).AcceptVisitor(this); + return; +#endif default: base.Visit(node); return; @@ -453,6 +470,33 @@ protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpre (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); } +#if NET6_0_OR_GREATER + /// + /// Creates expression that represents addition to the given . + /// + /// Time expression. + /// Interval expression to add. + /// + protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) + { + return DateAddMillisecond(time, (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); + } + + /// + /// Creates expression that represents subtraction of two expressions. + /// + /// First expression. + /// Second expression. + /// Result expression. + /// + protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) + { + return SqlDml.Modulo( + NanosecondsPerDay + CastToDecimal(DateDiffMillisecond(time2, time1), 18,0) * NanosecondsPerMillisecond, + NanosecondsPerDay); + } +#endif + private SqlExpression GenericPad(SqlFunctionCall node) { var operand = node.Arguments[0]; @@ -511,6 +555,9 @@ protected static SqlUserFunctionCall DatePartWeekDay(SqlExpression date) => protected static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) => SqlDml.FunctionCall("DATEDIFF", SqlDml.Native(DayPart), date1, date2); + protected static SqlUserFunctionCall DateDiffHour(SqlExpression date1, SqlExpression date2) => + SqlDml.FunctionCall("DATEDIFF", SqlDml.Native(HourPart), date1, date2); + protected static SqlUserFunctionCall DateDiffMillisecond(SqlExpression date1, SqlExpression date2) => SqlDml.FunctionCall("DATEDIFF", SqlDml.Native(MillisecondPart), date1, date2); @@ -535,6 +582,14 @@ protected static SqlUserFunctionCall DateAddSecond(SqlExpression date, SqlExpres protected static SqlUserFunctionCall DateAddMillisecond(SqlExpression date, SqlExpression milliseconds) => SqlDml.FunctionCall("DATEADD", SqlDml.Native(MillisecondPart), milliseconds, date); +#if NET6_0_OR_GREATER //DO_DATEONLY + protected static SqlUserFunctionCall TimeToString(SqlExpression time) => + SqlDml.FunctionCall("CONVERT", SqlDml.Native("NVARCHAR(16)"), time, SqlDml.Native("114")); + + protected static SqlUserFunctionCall DateToString(SqlExpression time) => + SqlDml.FunctionCall("CONVERT", SqlDml.Native("NVARCHAR(10)"), time, SqlDml.Native("23")); +#endif + protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) => SqlDml.FunctionCall("CONVERT", SqlDml.Native("NVARCHAR(19)"), dateTime, SqlDml.Native("126")); diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs index 9e952ad69a..8b484688fa 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2022 Xtensive LLC. +// Copyright (C) 2009-2023 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 @@ -45,13 +45,7 @@ protected override SqlExpression DateTimeTruncate(SqlExpression date) => SqlDml.Cast(date, new SqlValueType(SqlDateTypeName)), new SqlValueType(SqlDateTime2TypeName)); - /// - /// Creates expression that represents subtraction of two expressions. - /// - /// First expression. - /// Second expression. - /// Result expression. - /// + /// protected override SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) { return base.DateTimeSubtractDateTime(date1, date2) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs index 371cc0f398..f706181aca 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2021 Xtensive LLC. +// Copyright (C) 2018-2023 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 @@ -13,6 +13,48 @@ internal class Compiler : v12.Compiler protected const string MicrosecondPart = "MCS"; protected const long NanosecondsPerMicrosecond = 1000; +#if NET6_0_OR_GREATER //DO_DATEONLY + /// + public override void Visit(SqlFunctionCall node) + { + switch (node.FunctionType) { + case SqlFunctionType.DateTimeOffsetTimeOfDay: + DateTimeOffsetTimeOfDay(node.Arguments[0]).AcceptVisitor(this); + break; + case SqlFunctionType.IntervalToMilliseconds: { + if (node.Arguments[0] is SqlBinary binary + && (binary.NodeType is SqlNodeType.DateTimeMinusDateTime or SqlNodeType.DateTimeOffsetMinusDateTimeOffset or SqlNodeType.TimeMinusTime)) { + Visit(DateDiffBigMicrosecond(binary.Right, binary.Left) / CastToLong(1000)); + } + else { + base.Visit(node); + } + break; + } + case SqlFunctionType.IntervalToNanoseconds: { + if (node.Arguments[0] is SqlBinary binary) { + if (binary.NodeType is SqlNodeType.DateTimeMinusDateTime or SqlNodeType.DateTimeOffsetMinusDateTimeOffset) { + // we have to use time consuming algorithm here because + // DATEDIFF_BIG can throw arithmetic overflow on nanoseconds + // so we should handle it by this big formula + Visit(CastToLong(DateTimeSubtractDateTimeExpensive(binary.Left, binary.Right))); + } + else if (binary.NodeType is SqlNodeType.TimeMinusTime) + //but for time it is OK + Visit(DateDiffBigMicrosecond(binary.Right, binary.Left)); + else + base.Visit(node); + } + else { + base.Visit(node); + } + break; + } + default: + base.Visit(node); break; + } + } +#else /// public override void Visit(SqlFunctionCall node) { @@ -47,6 +89,7 @@ public override void Visit(SqlFunctionCall node) base.Visit(node); break; } } +#endif protected override SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) { @@ -59,7 +102,7 @@ private SqlExpression DateTimeSubtractDateTimeExpensive(SqlExpression date1, Sql + CastToDecimal(DateDiffBigMillisecond(DateAddDay(date2, DateDiffBigDay(date2, date1)), date1), 18, 0) * NanosecondsPerMillisecond; } - #region Static Helpers +#region Static Helpers protected static SqlExpression DateTimeOffsetTimeOfDay(SqlExpression dateTimeOffset) => DateDiffBigNanosecond( @@ -78,7 +121,7 @@ protected static SqlUserFunctionCall DateDiffBigMillisecond(SqlExpression date1, protected static SqlUserFunctionCall DateDiffBigDay(SqlExpression date1, SqlExpression date2) => SqlDml.FunctionCall("DATEDIFF_BIG", SqlDml.Native(DayPart), date1, date2); - #endregion +#endregion public Compiler(SqlDriver driver) : base(driver) diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index 7c6a849a2f..8ebe39cc2d 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -16,20 +16,23 @@ namespace Xtensive.Sql.Drivers.Sqlite.v3 { internal class Compiler : SqlCompiler { - private static readonly long NanosecondsPerDay = (long) TimeSpan.FromDays(1).TotalMilliseconds * NanosecondsPerMillisecond; - private static readonly long NanosecondsPerHour = (long) TimeSpan.FromHours(1).TotalMilliseconds * NanosecondsPerMillisecond; - private static readonly long NanosecondsPerSecond = (long) TimeSpan.FromSeconds(1).TotalMilliseconds * NanosecondsPerMillisecond; - private static readonly long MillisecondsPerSecond = (long) TimeSpan.FromSeconds(1).TotalMilliseconds; - private static readonly int StartOffsetIndex = DateTimeOffsetExampleString.IndexOf('+'); - private const long NanosecondsPerMillisecond = 1000000L; private const string DateWithZeroTimeFormat = "%Y-%m-%d 00:00:00.000"; +#if NET6_0_OR_GREATER //DO_DATEONLY private const string DateFormat = "%Y-%m-%d"; private const string TimeFormat = "%H:%M:%f"; + private const string TimeToStringFormat = "%H:%M:%f0000"; +#endif private const string DateTimeFormat = "%Y-%m-%d %H:%M:%f"; private const string DateTimeIsoFormat = "%Y-%m-%dT%H:%M:%S"; private const string DateTimeOffsetExampleString = "2001-02-03 04:05:06.789+02.45"; + private static readonly long NanosecondsPerDay = (long) TimeSpan.FromDays(1).TotalMilliseconds * NanosecondsPerMillisecond; + private static readonly long NanosecondsPerHour = (long) TimeSpan.FromHours(1).TotalMilliseconds * NanosecondsPerMillisecond; + private static readonly long NanosecondsPerSecond = (long) TimeSpan.FromSeconds(1).TotalMilliseconds * NanosecondsPerMillisecond; + private static readonly long MillisecondsPerSecond = (long) TimeSpan.FromSeconds(1).TotalMilliseconds; + private static readonly int StartOffsetIndex = DateTimeOffsetExampleString.IndexOf('+'); + protected override bool VisitCreateTableConstraints(SqlCreateTable node, IEnumerable constraints, bool hasItems) { // SQLite has special syntax for autoincrement primary keys @@ -75,6 +78,14 @@ public override void Visit(SqlBinary node) DateTimeOffsetExtractOffsetAsString(node.Left)) .AcceptVisitor(this); return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlNodeType.TimePlusInterval: + TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); + return; + case SqlNodeType.TimeMinusTime: + TimeSubtractTime(node.Left, node.Right).AcceptVisitor(this); + return; +#endif default: base.Visit(node); return; @@ -186,6 +197,18 @@ public override void Visit(SqlFunctionCall node) arguments[2] - 1).AcceptVisitor(this); return; #if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateAddYears: + DateAddYear(arguments[0], arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.DateAddMonths: + DateAddMonth(arguments[0], arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.DateAddDays: + DateAddDay(arguments[0], arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToString: + DateToString(node.Arguments[0]).AcceptVisitor(this); + return; case SqlFunctionType.DateConstruct: DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), arguments[0] - 2001), @@ -198,6 +221,15 @@ public override void Visit(SqlFunctionCall node) arguments[1]), arguments[2], arguments[3]).AcceptVisitor(this); return; + case SqlFunctionType.TimeAddHours: + TimeAddHours(arguments[0], arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeAddMinutes: + TimeAddMinutes(arguments[0], arguments[1]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToString: + TimeToString(node.Arguments[0]).AcceptVisitor(this); + return; #endif case SqlFunctionType.DateTimeToStringIso: DateTimeToStringIso(node.Arguments[0]).AcceptVisitor(this); @@ -346,6 +378,7 @@ private void VisitDateTime(SqlExtract node) base.Visit(node); } +#if NET6_0_OR_GREATER //DO_DATEONLY private void VisitTime(SqlExtract node) { if (node.IsMillisecondExtraction) { @@ -354,6 +387,7 @@ private void VisitTime(SqlExtract node) } base.Visit(node); } +#endif private void VisitDateTimeOffset(SqlExtract node) { @@ -380,7 +414,10 @@ private void VisitDateTimeOffset(SqlExtract node) (((DateTimeOffsetExtractOffsetAsTotalNanoseconds(node.Operand)) % NanosecondsPerHour) / (60 * NanosecondsPerSecond)).AcceptVisitor(this); return; } - Visit(SqlDml.Extract(ConvertDateTimeOffsetPartToDateTimePart(node.DateTimeOffsetPart), DateTimeOffsetExtractDateTimeAsString(node.Operand))); + Visit( + SqlDml.Extract( + ConvertDateTimeOffsetPartToDateTimePart(node.DateTimeOffsetPart), + DateTimeOffsetExtractDateTimeAsString(node.Operand))); } private static SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpression interval) => @@ -460,25 +497,53 @@ private static SqlExpression DateAddMonth(SqlExpression date, SqlExpression mont private static SqlExpression DateAddDay(SqlExpression date, SqlExpression days) => SqlDml.FunctionCall("STRFTIME", DateFormat, date, SqlDml.Concat(days, " ", "DAYS")); - private static SqlExpression TimeAddHours(SqlExpression date, SqlExpression seconds) => - SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, " ", "HOURS")); + private static SqlExpression TimeAddHours(SqlExpression time, SqlExpression seconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, time, SqlDml.Concat(seconds, " ", "HOURS")); + + private static SqlExpression TimeAddMinutes(SqlExpression time, SqlExpression seconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, time, SqlDml.Concat(seconds, " ", "MINUTES")); + + private static SqlExpression TimeAddSeconds(SqlExpression time, SqlExpression seconds, SqlExpression milliseconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, time, SqlDml.Concat(seconds, ".", milliseconds, " ", "SECONDS")); + private static SqlExpression TimeAddSeconds(SqlExpression time, SqlExpression seconds) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, time, SqlDml.Concat(seconds, " ", "SECONDS")); + + private static SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) => + TimeAddSeconds(time, interval / Convert.ToDouble(NanosecondsPerSecond)); + + private static SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) + { + var hoursInSecs1 = SqlDml.Extract(SqlTimePart.Hour, time1) * 3600; + var hoursInSecs2 = SqlDml.Extract(SqlTimePart.Hour, time2) * 3600; + + var minutesInSecs1 = SqlDml.Extract(SqlTimePart.Minute, time1) * 60; + var minutesInSecs2 = SqlDml.Extract(SqlTimePart.Minute, time2) * 60; + + var seconds1 = SqlDml.FunctionCall("STRFTIME", "%f", time1); + var seconds2 = SqlDml.FunctionCall("STRFTIME", "%f", time2); + + var difference = ((hoursInSecs1 + minutesInSecs1 + seconds1) * NanosecondsPerSecond) + - ((hoursInSecs2 + minutesInSecs2 + seconds2) * NanosecondsPerSecond); + + return SqlDml.Modulo(NanosecondsPerDay + difference, NanosecondsPerDay); + } - private static SqlExpression TimeAddMinutes(SqlExpression date, SqlExpression seconds) => - SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, " ", "MINUTES")); + private static SqlExpression DateToString(SqlExpression dateTime) => + SqlDml.FunctionCall("STRFTIME", DateFormat, dateTime); - private static SqlExpression TimeAddSeconds(SqlExpression date, SqlExpression seconds, SqlExpression milliseconds) => - SqlDml.FunctionCall("STRFTIME", TimeFormat, date, SqlDml.Concat(seconds, ".", milliseconds, " ", "SECONDS")); + private static SqlExpression TimeToString(SqlExpression dateTime) => + SqlDml.FunctionCall("STRFTIME", TimeToStringFormat, dateTime); #endif private static SqlExpression DateOrTimeGetMilliseconds(SqlExpression date) => CastToLong(SqlDml.FunctionCall("STRFTIME", "%f", date) * MillisecondsPerSecond) - CastToLong(SqlDml.FunctionCall("STRFTIME", "%S", date) * MillisecondsPerSecond); - private static SqlExpression DateGetTotalSeconds(SqlExpression date) => + private static SqlExpression DateOrTimeGetTotalSeconds(SqlExpression date) => SqlDml.FunctionCall("STRFTIME", "%s", date); private static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) => - (((DateGetTotalSeconds(date1) - DateGetTotalSeconds(date2)) * MillisecondsPerSecond) + (((DateOrTimeGetTotalSeconds(date1) - DateOrTimeGetTotalSeconds(date2)) * MillisecondsPerSecond) + DateOrTimeGetMilliseconds(date1) - DateOrTimeGetMilliseconds(date2)) * NanosecondsPerMillisecond; diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs index f9c5629f87..3b630b0270 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs @@ -516,12 +516,18 @@ public override void Translate(IOutput output, SqlNodeType type) switch (type) { case SqlNodeType.DateTimePlusInterval: case SqlNodeType.DateTimeOffsetPlusInterval: +#if NET6_0_OR_GREATER + case SqlNodeType.TimePlusInterval: +#endif _ = output.Append("+"); break; case SqlNodeType.DateTimeMinusInterval: case SqlNodeType.DateTimeMinusDateTime: case SqlNodeType.DateTimeOffsetMinusInterval: case SqlNodeType.DateTimeOffsetMinusDateTimeOffset: +#if NET6_0_OR_GREATER + case SqlNodeType.TimeMinusTime: +#endif _ = output.Append("-"); break; case SqlNodeType.Overlaps: diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs index 97d47ebdcd..5bbe812276 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs @@ -38,13 +38,13 @@ public override object ReadBoolean(DbDataReader reader, int index) public override object ReadDateOnly(DbDataReader reader, int index) { var value = reader.GetString(index); - return DateOnly.ParseExact(value, DateTimeOffsetFormat, CultureInfo.InvariantCulture); + return DateOnly.ParseExact(value, DateFormat, CultureInfo.InvariantCulture); } public override object ReadTimeOnly(DbDataReader reader, int index) { var value = reader.GetString(index); - return TimeOnly.ParseExact(value, DateTimeOffsetFormat, CultureInfo.InvariantCulture); + return TimeOnly.ParseExact(value, TimeFormat, CultureInfo.InvariantCulture); } #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs index d6e645e9ce..691b7c560a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateOnlyCompilers.cs @@ -130,22 +130,18 @@ public static SqlExpression DateOnlyAddDays(SqlExpression _this, [Type(typeof(in [Compiler(typeof(DateOnly), "ToString")] public static SqlExpression DateOnlyToStringIso(SqlExpression _this) { - throw new NotSupportedException(Strings.ExDateTimeToStringMethodIsNotSupported); + throw new NotSupportedException(Strings.ExDateOnlyToStringMethodIsNotSupported); } [Compiler(typeof(DateOnly), "ToString")] public static SqlExpression DateOnlyToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) { - throw new NotImplementedException(); var stringValue = value as SqlLiteral; - if (stringValue == null) - throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + if (stringValue == null || !stringValue.Value.Equals("o", StringComparison.OrdinalIgnoreCase)) + throw new NotSupportedException(Strings.ExTranslationOfDateOnlyToStringWithArbitraryArgumentIsNotSupported); - if (!stringValue.Value.Equals("s")) - throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); - - return SqlDml.DateTimeToStringIso(_this); + return SqlDml.DateToString(_this); } } #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs index 0f1c32659c..cadfe870f3 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/DateTimeCompilers.cs @@ -284,11 +284,8 @@ public static SqlExpression DateTimeToStringIso(SqlExpression _this, [Type(typeo { var stringValue = value as SqlLiteral; - if (stringValue == null) - throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); - - if (!stringValue.Value.Equals("s")) - throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + if (stringValue == null || !stringValue.Value.Equals("s")) + throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentIsNotSupported); return SqlDml.DateTimeToStringIso(_this); } diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index f700ba126f..59a9899d22 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -146,22 +146,18 @@ public static SqlExpression TimeOnlyAdd(SqlExpression _this, [Type(typeof(TimeSp [Compiler(typeof(TimeOnly), "ToString")] public static SqlExpression TimeOnlyToStringIso(SqlExpression _this) { - throw new NotSupportedException(Strings.ExDateTimeToStringMethodIsNotSupported); + throw new NotSupportedException(Strings.ExTimeOnlyToStringMethodIsNotSupported); } [Compiler(typeof(TimeOnly), "ToString")] public static SqlExpression TimeOnlyToStringIso(SqlExpression _this, [Type(typeof(string))] SqlExpression value) { - throw new NotImplementedException(); - //var stringValue = value as SqlLiteral; - - //if (stringValue == null) - // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + var stringValue = value as SqlLiteral; - //if (!stringValue.Value.Equals("s")) - // throw new NotSupportedException(Strings.ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported); + if (stringValue == null || !stringValue.Value.Equals("o", StringComparison.OrdinalIgnoreCase)) + throw new NotSupportedException(Strings.ExTranslationOfTimeOnlyToStringWithArbitraryArgumentIsNotSupported); - //return SqlDml.DateTimeToStringIso(_this); + return SqlDml.TimeToString(_this); } } #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeSpanCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeSpanCompilers.cs index 4b85992644..935c475289 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeSpanCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeSpanCompilers.cs @@ -5,6 +5,7 @@ // Created: 2009.02.24 using System; +using Xtensive.Reflection; using Xtensive.Sql; using Xtensive.Sql.Dml; using Operator = Xtensive.Reflection.WellKnown.Operator; @@ -31,8 +32,14 @@ internal static SqlExpression GenericIntervalConstruct( SqlExpression seconds, SqlExpression milliseconds) { + var context = ExpressionTranslationContext.Current; + var mapping = context.Driver.GetTypeMapping(typeof(long)); + var mappedType = mapping.MapType(); + var m = milliseconds + 1000L * (seconds + 60L * (minutes + 60L * (hours + 24L * days))); - var nanoseconds = NanosecondsPerMillisecond * SqlDml.Cast(m, SqlType.Int64); + var nanoseconds = (mappedType.Precision.HasValue) + ? NanosecondsPerMillisecond * SqlDml.Cast(m, mappedType.Type, (short) mappedType.Precision.Value, (short) mappedType.Scale.Value) + : NanosecondsPerMillisecond * SqlDml.Cast(m, mappedType.Type); return SqlDml.IntervalConstruct(nanoseconds); } diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index 5dee89f7b6..0acc37de23 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -68,8 +68,10 @@ public enum SqlFunctionType DateAddYears, DateAddMonths, DateAddDays, + DateToString, TimeAddHours, TimeAddMinutes, + TimeToString, #endif DateTimeTruncate, DateTimeToStringIso, diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 13875038a4..6db7933c0b 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -727,6 +727,18 @@ public static SqlFunctionCall TimeAddMinutes(SqlExpression source, SqlExpression ArgumentValidator.EnsureArgumentNotNull(minutes, "minutes"); return new SqlFunctionCall(SqlFunctionType.TimeAddMinutes, source, minutes); } + + public static SqlFunctionCall DateToString(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.DateToString, expression); + } + + public static SqlFunctionCall TimeToString(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.TimeToString, expression); + } #endif public static SqlFunctionCall DateTimeToStringIso(SqlExpression expression) diff --git a/Orm/Xtensive.Orm/Strings.Designer.cs b/Orm/Xtensive.Orm/Strings.Designer.cs index 7c2398672c..c0394b5596 100644 --- a/Orm/Xtensive.Orm/Strings.Designer.cs +++ b/Orm/Xtensive.Orm/Strings.Designer.cs @@ -1352,6 +1352,15 @@ internal static string ExDatabaseMappingRequiresMultidatabaseDomainConfiguration } } + /// + /// Looks up a localized string similar to DateOnly.ToString() method is not supported, use the DateOnly.ToString("o").. + /// + internal static string ExDateOnlyToStringMethodIsNotSupported { + get { + return ResourceManager.GetString("ExDateOnlyToStringMethodIsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to DateTime.ToString() method is not supported, use the DateTime.ToString("s").. /// @@ -4299,6 +4308,15 @@ internal static string ExThisStorageDoesNotSupportXValuesLessThanYSuppliedValueI } } + /// + /// Looks up a localized string similar to TimeOnly.ToString() method is not supported, use the TimeOnly.ToString("o").. + /// + internal static string ExTimeOnlyToStringMethodIsNotSupported { + get { + return ResourceManager.GetString("ExTimeOnlyToStringMethodIsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Transaction is not active.. /// @@ -4326,12 +4344,21 @@ internal static string ExTransactionShouldNotBeActive { } } + /// + /// Looks up a localized string similar to Translation of DateOnly.ToString(string) with arbitrary arguments is not supported. Use DateOnly.ToString("s").. + /// + internal static string ExTranslationOfDateOnlyToStringWithArbitraryArgumentIsNotSupported { + get { + return ResourceManager.GetString("ExTranslationOfDateOnlyToStringWithArbitraryArgumentIsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Translation of DateTime.ToString(string) with arbitrary arguments is not supported. Use DateTime.ToString("s").. /// - internal static string ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported { + internal static string ExTranslationOfDateTimeToStringWithArbitraryArgumentIsNotSupported { get { - return ResourceManager.GetString("ExTranslationOfDateTimeToStringWithArbitraryArgumentsIsNotSupported", resourceCulture); + return ResourceManager.GetString("ExTranslationOfDateTimeToStringWithArbitraryArgumentIsNotSupported", resourceCulture); } } @@ -4353,6 +4380,15 @@ internal static string ExTranslationOfLiteralOfTypeXIsNotSupported { } } + /// + /// Looks up a localized string similar to Translation of TimeOnly.ToString(string) with arbitrary arguments is not supported. Use TimeOnly.ToString("s").. + /// + internal static string ExTranslationOfTimeOnlyToStringWithArbitraryArgumentIsNotSupported { + get { + return ResourceManager.GetString("ExTranslationOfTimeOnlyToStringWithArbitraryArgumentIsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Translation of {0} method does not support any parameter type, but {1}.. /// diff --git a/Orm/Xtensive.Orm/Strings.resx b/Orm/Xtensive.Orm/Strings.resx index b1bc272b2a..b3c93464c1 100644 --- a/Orm/Xtensive.Orm/Strings.resx +++ b/Orm/Xtensive.Orm/Strings.resx @@ -2390,7 +2390,7 @@ Error: {1} DateTime.ToString() method is not supported, use the DateTime.ToString("s"). - + Translation of DateTime.ToString(string) with arbitrary arguments is not supported. Use DateTime.ToString("s"). @@ -2588,4 +2588,16 @@ Error: {1} Query was compiled with DomainConfiguration.ShareStorageSchemaOverNodes option set to true, it requires PostCompilerConfiguration.SchemaMapping and PostCompilerConfiguration.DatabaseMapping collections to be provided. + + DateOnly.ToString() method is not supported, use the DateOnly.ToString("o"). + + + TimeOnly.ToString() method is not supported, use the TimeOnly.ToString("o"). + + + Translation of DateOnly.ToString(string) with arbitrary arguments is not supported. Use DateOnly.ToString("s"). + + + Translation of TimeOnly.ToString(string) with arbitrary arguments is not supported. Use TimeOnly.ToString("s"). + \ No newline at end of file From 3deb380afe42b8ac6e2fadcce509e614f4a4db37 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 14 Feb 2023 15:22:51 +0500 Subject: [PATCH 53/86] DateOnly/TimeOnly-related tests for Linq improved --- ...oStringTest.cs => DateOnlyToStringTest.cs} | 8 ++-- .../TimeOnly/ComparisonTestTimeOnly.cs | 2 +- .../TimeOnly/OperationsTest.cs | 39 +++++++++++++------ ...OnlyToIsoString.cs => TimeOnlyToString.cs} | 8 ++-- 4 files changed, 37 insertions(+), 20 deletions(-) rename Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/{DateOnlyToIsoStringTest.cs => DateOnlyToStringTest.cs} (76%) rename Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/{TimeOnlyToIsoString.cs => TimeOnlyToString.cs} (76%) diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs similarity index 76% rename from Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs rename to Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs index 3f1d8a31a6..3ac2874baa 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToIsoStringTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs @@ -11,14 +11,14 @@ namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys { - public class DateOnlyToIsoStringTest : DateTimeBaseTest + public class DateOnlyToStringTest : DateTimeBaseTest { [Test] - public void ToIsoStringTest() + public void ToStringTest() { ExecuteInsideSession((s) => { - RunTest(s, c => c.DateOnly.ToString("s") == FirstDateOnly.ToString("s")); - RunWrongTest(s, c => c.DateOnly.ToString("s") == FirstDateOnly.AddDays(1).ToString("s")); + RunTest(s, c => c.DateOnly.ToString("o") == FirstDateOnly.ToString("o")); + RunWrongTest(s, c => c.DateOnly.ToString("o") == FirstDateOnly.AddDays(1).ToString("o")); }); } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs index 3804c72216..a98be05fe8 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs @@ -69,7 +69,7 @@ public void CompareMysqTest() RunWrongTest(s, c => c.TimeOnly > FirstTimeOnly); RunWrongTest(s, c => c.MillisecondTimeOnly > FirstMillisecondTimeOnly); - RunWrongTest(s, c => c.MillisecondTimeOnly < FirstMillisecondTimeOnly.AddMinutes(3)); + RunWrongTest(s, c => c.MillisecondTimeOnly < FirstMillisecondTimeOnly.AddMinutes(-3)); }); } } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs index 3943f7cdce..9d02d8a124 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs @@ -17,12 +17,16 @@ public void AddHoursTest() { ExecuteInsideSession((s) => { RunTest(s, c => c.TimeOnly.AddHours(1) == FirstTimeOnly.AddHours(1)); - RunTest(s, c => c.MillisecondTimeOnly.AddHours(-2) == FirstMillisecondTimeOnly.AddHours(-2)); RunTest(s, c => c.NullableTimeOnly.Value.AddHours(33) == NullableTimeOnly.AddHours(33)); RunWrongTest(s, c => c.TimeOnly.AddHours(1) == FirstTimeOnly.AddHours(2)); - RunWrongTest(s, c => c.MillisecondTimeOnly.AddHours(-1) == FirstMillisecondTimeOnly.AddHours(-2)); RunWrongTest(s, c => c.NullableTimeOnly.Value.AddHours(33) == NullableTimeOnly.AddHours(44)); + + if(StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.MySql) + || StorageProviderInfo.Instance.CheckProviderVersionIsAtLeast(new Version(5, 6))) { + RunTest(s, c => c.MillisecondTimeOnly.AddHours(-2) == FirstMillisecondTimeOnly.AddHours(-2)); + RunWrongTest(s, c => c.MillisecondTimeOnly.AddHours(-1) == FirstMillisecondTimeOnly.AddHours(-2)); + } }); } @@ -31,47 +35,60 @@ public void AddMinutesTest() { ExecuteInsideSession((s) => { RunTest(s, c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(1)); - RunTest(s, c => c.MillisecondTimeOnly.AddMinutes(-2) == FirstMillisecondTimeOnly.AddMinutes(-2)); RunTest(s, c => c.NullableTimeOnly.Value.AddMinutes(33) == NullableTimeOnly.AddMinutes(33)); RunWrongTest(s, c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(2)); - RunWrongTest(s, c => c.MillisecondTimeOnly.AddMinutes(-1) == FirstMillisecondTimeOnly.AddMinutes(-2)); RunWrongTest(s, c => c.NullableTimeOnly.Value.AddMinutes(33) == NullableTimeOnly.AddMinutes(44)); + + if (StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.MySql) + || StorageProviderInfo.Instance.CheckProviderVersionIsAtLeast(new Version(5, 6))) { + RunTest(s, c => c.MillisecondTimeOnly.AddMinutes(-2) == FirstMillisecondTimeOnly.AddMinutes(-2)); + RunWrongTest(s, c => c.MillisecondTimeOnly.AddMinutes(-1) == FirstMillisecondTimeOnly.AddMinutes(-2)); + } }); } - [Test, Ignore("Compiler's not implemented yet")] + [Test] public void AddTimeSpanTest() { ExecuteInsideSession((s) => { - RunTest(s, c => c.TimeOnly.Add(FirstOffset) == FirstTimeOnly.Add(FirstOffset)); RunTest(s, c => c.MillisecondTimeOnly.Add(SecondOffset) == FirstMillisecondTimeOnly.Add(SecondOffset)); + RunTest(s, c => c.TimeOnly.Add(FirstOffset) == FirstTimeOnly.Add(FirstOffset)); RunTest(s, c => c.NullableTimeOnly.Value.Add(FirstOffset) == NullableTimeOnly.Add(FirstOffset)); RunWrongTest(s, c => c.TimeOnly.Add(FirstOffset) == FirstTimeOnly.Add(WrongOffset)); - RunWrongTest(s, c => c.MillisecondTimeOnly.Add(SecondOffset) == FirstMillisecondTimeOnly.Add(WrongOffset)); RunWrongTest(s, c => c.NullableTimeOnly.Value.Add(FirstOffset) == NullableTimeOnly.Add(WrongOffset)); + + if (StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.MySql) + || StorageProviderInfo.Instance.CheckProviderVersionIsAtLeast(new Version(5, 6))) { + RunTest(s, c => c.MillisecondTimeOnly.Add(SecondOffset) == FirstMillisecondTimeOnly.Add(SecondOffset)); + RunWrongTest(s, c => c.MillisecondTimeOnly.Add(SecondOffset) == FirstMillisecondTimeOnly.Add(WrongOffset)); + } }); } [Test] public void MinusTimeOnlyTest() { - Require.ProviderIsNot(StorageProvider.MySql); + var inteval = FirstTimeOnly - SecondTimeOnly; + ExecuteInsideSession((s) => { RunTest(s, c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - SecondTimeOnly); - RunTest(s, c => c.MillisecondTimeOnly - SecondTimeOnly == FirstMillisecondTimeOnly - SecondTimeOnly); RunTest(s, c => c.NullableTimeOnly - SecondTimeOnly == NullableTimeOnly - SecondTimeOnly); RunWrongTest(s, c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - WrongTimeOnly); - RunWrongTest(s, c => c.MillisecondTimeOnly - SecondTimeOnly == FirstMillisecondTimeOnly - WrongTimeOnly); RunWrongTest(s, c => c.NullableTimeOnly - SecondTimeOnly == NullableTimeOnly - WrongTimeOnly); + if (StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.MySql) + || StorageProviderInfo.Instance.CheckProviderVersionIsAtLeast(new Version(5, 6))) { + RunTest(s, c => c.MillisecondTimeOnly - SecondTimeOnly == FirstMillisecondTimeOnly - SecondTimeOnly); + RunWrongTest(s, c => c.MillisecondTimeOnly - SecondTimeOnly == FirstMillisecondTimeOnly - WrongTimeOnly); + } }); } [Test] - public void MysqlMinisDateTimeTest() + public void MysqlMinisTimeOnlyTest() { Require.ProviderIs(StorageProvider.MySql); ExecuteInsideSession((s) => { diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs similarity index 76% rename from Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs rename to Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs index b9ab70a0ef..7b507e0c4c 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToIsoString.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs @@ -11,14 +11,14 @@ namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys { - public class TimeOnlyToIsoStringTest : DateTimeBaseTest + public class TimeOnlyToStringTest : DateTimeBaseTest { [Test] - public void ToIsoStringTest() + public void ToStringTest() { ExecuteInsideSession((s) => { - RunTest(s, c => c.TimeOnly.ToString("s") == FirstTimeOnly.ToString("s")); - RunWrongTest(s, c => c.TimeOnly.ToString("s") == FirstTimeOnly.AddHours(1).ToString("s")); + RunTest(s, c => c.TimeOnly.ToString("o") == FirstTimeOnly.ToString("o")); + RunWrongTest(s, c => c.TimeOnly.ToString("o") == FirstTimeOnly.AddHours(1).ToString("o")); }); } } From abca168a73f023f391b5a9df8cc8d2399d273759 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 17:06:57 +0500 Subject: [PATCH 54/86] Mysql: Fix time extraction --- Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Extractor.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 392699ae6e..5b086f09b6 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 @@ -653,9 +653,8 @@ private SqlValueType CreateValueType(IDataRecord row, return new SqlValueType(SqlType.Int64); } #if NET6_0_OR_GREATER //DO_DATEONLY - if (typeName.Equals("TIME", StringComparison.Ordinal)) { - var time_precision = (dataTypeName.Length > 4) ? int.Parse(dataTypeName[5].ToString()) : 0; - return new SqlValueType(SqlType.Time, precision); + if (typeName.Equals("TIME", StringComparison.Ordinal) || typeName.StartsWith("TIME(")) { + return new SqlValueType(SqlType.Time); } else if (typeName.StartsWith("TIME", StringComparison.Ordinal)) { // "timestamp precision" is saved as "scale", ignoring too From efe5a44a201826b5c56dbc4510e58d2993d9914c Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 18:47:56 +0500 Subject: [PATCH 55/86] Make server side timezone available for tests --- .../Interfaces/IStorageTimeZoneProvider.cs | 13 ++ .../StorageProviderInfo.cs | 112 ++++++++------- .../StorageTimeZoneProvider.cs | 131 ++++++++++++++++++ 3 files changed, 206 insertions(+), 50 deletions(-) create mode 100644 Orm/Xtensive.Orm.Tests.Framework/Interfaces/IStorageTimeZoneProvider.cs create mode 100644 Orm/Xtensive.Orm.Tests.Framework/StorageTimeZoneProvider.cs diff --git a/Orm/Xtensive.Orm.Tests.Framework/Interfaces/IStorageTimeZoneProvider.cs b/Orm/Xtensive.Orm.Tests.Framework/Interfaces/IStorageTimeZoneProvider.cs new file mode 100644 index 0000000000..c8ab060896 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests.Framework/Interfaces/IStorageTimeZoneProvider.cs @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; + +namespace Xtensive.Orm.Tests +{ + public interface IStorageTimeZoneProvider + { + public TimeSpan TimeZoneOffset { get; } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests.Framework/StorageProviderInfo.cs b/Orm/Xtensive.Orm.Tests.Framework/StorageProviderInfo.cs index b7910be38c..13d3a40222 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/StorageProviderInfo.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/StorageProviderInfo.cs @@ -1,11 +1,12 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2013-2023 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: 2013.07.23 using System; using Xtensive.Orm.Providers; +using Xtensive.Sql; namespace Xtensive.Orm.Tests { @@ -30,44 +31,37 @@ public static StorageProviderInfo Instance public ProviderInfo Info { get; private set; } - public bool CheckProviderIs(StorageProvider requiredProviders) - { - return (Provider & requiredProviders)!=0; - } + public IStorageTimeZoneProvider TimeZoneProvider { get; private set; } - public bool CheckProviderIsNot(StorageProvider disallowedProviders) - { - return (Provider & disallowedProviders)==0; - } + public bool CheckProviderIs(StorageProvider requiredProviders) => + (Provider & requiredProviders) != 0; - public bool CheckProviderVersionIsAtLeast(Version minimalVersion) - { - return Info.StorageVersion >= minimalVersion; - } + public bool CheckProviderIsNot(StorageProvider disallowedProviders) => + (Provider & disallowedProviders) == 0; - public bool CheckProviderVersionIsAtMost(Version maximalVersion) - { - return Info.StorageVersion <= maximalVersion; - } + public bool CheckProviderVersionIsAtLeast(Version minimalVersion) => + Info.StorageVersion >= minimalVersion; - public bool CheckAllFeaturesSupported(ProviderFeatures requiredFeatures) - { - return (Info.ProviderFeatures & requiredFeatures)==requiredFeatures; - } + public bool CheckProviderVersionIsAtMost(Version maximalVersion) => + Info.StorageVersion <= maximalVersion; - public bool CheckAllFeaturesNotSupported(ProviderFeatures disallowedFeatures) - { - return (Info.ProviderFeatures & disallowedFeatures)==0; - } + public bool CheckAllFeaturesSupported(ProviderFeatures requiredFeatures) => + (Info.ProviderFeatures & requiredFeatures) == requiredFeatures; - public bool CheckAnyFeatureSupported(ProviderFeatures requiredFeatures) - { - return (Info.ProviderFeatures & requiredFeatures)!=0; - } + public bool CheckAllFeaturesNotSupported(ProviderFeatures disallowedFeatures) => + (Info.ProviderFeatures & disallowedFeatures) == 0; + + public bool CheckAnyFeatureSupported(ProviderFeatures requiredFeatures) => + (Info.ProviderFeatures & requiredFeatures) != 0; + + public bool CheckAnyFeatureNotSupported(ProviderFeatures disallowedFeatures) => + (Info.ProviderFeatures & disallowedFeatures) != disallowedFeatures; - public bool CheckAnyFeatureNotSupported(ProviderFeatures disallowedFeatures) + public IDisposable ReplaceTimeZoneProvider(IStorageTimeZoneProvider newProvder) { - return (Info.ProviderFeatures & disallowedFeatures)!=disallowedFeatures; + var oldProvider = TimeZoneProvider; + TimeZoneProvider = newProvder; + return new Core.Disposable((b) => TimeZoneProvider = oldProvider); } private StorageProviderInfo() @@ -76,28 +70,46 @@ private StorageProviderInfo() var providerName = config.ConnectionInfo.Provider; Provider = ParseProvider(providerName); - Info = ProviderInfoBuilder.Build(providerName, TestSqlDriver.Create(config.ConnectionInfo)); + var sqlDriver = TestSqlDriver.Create(config.ConnectionInfo); + TimeZoneProvider = GetTimeZoneProvider(Provider, sqlDriver); + Info = ProviderInfoBuilder.Build(providerName, sqlDriver); } private static StorageProvider ParseProvider(string provider) { switch (provider) { - case WellKnown.Provider.SqlServer: - return StorageProvider.SqlServer; - case WellKnown.Provider.SqlServerCe: - return StorageProvider.SqlServerCe; - case WellKnown.Provider.PostgreSql: - return StorageProvider.PostgreSql; - case WellKnown.Provider.Oracle: - return StorageProvider.Oracle; - case WellKnown.Provider.MySql: - return StorageProvider.MySql; - case WellKnown.Provider.Firebird: - return StorageProvider.Firebird; - case WellKnown.Provider.Sqlite: - return StorageProvider.Sqlite; - default: - throw new ArgumentOutOfRangeException("provider"); + case WellKnown.Provider.SqlServer: + return StorageProvider.SqlServer; + case WellKnown.Provider.SqlServerCe: + return StorageProvider.SqlServerCe; + case WellKnown.Provider.PostgreSql: + return StorageProvider.PostgreSql; + case WellKnown.Provider.Oracle: + return StorageProvider.Oracle; + case WellKnown.Provider.MySql: + return StorageProvider.MySql; + case WellKnown.Provider.Firebird: + return StorageProvider.Firebird; + case WellKnown.Provider.Sqlite: + return StorageProvider.Sqlite; + default: + throw new ArgumentOutOfRangeException("provider"); + } + } + + private static IStorageTimeZoneProvider GetTimeZoneProvider(StorageProvider provider, SqlDriver sqlDriver) + { + switch (provider) { + case StorageProvider.SqlServer: + return new SqlServerTimeZoneProvider(sqlDriver); + case StorageProvider.PostgreSql: + return new PgSqlTimeZoneProvider(sqlDriver); + case StorageProvider.Oracle: + return new OracleTimeZoneProvider(sqlDriver); + case StorageProvider.Sqlite: + return new SqliteTimeZoneProvider(sqlDriver); + default: + return new LocalTimeZoneProvider(); } } } diff --git a/Orm/Xtensive.Orm.Tests.Framework/StorageTimeZoneProvider.cs b/Orm/Xtensive.Orm.Tests.Framework/StorageTimeZoneProvider.cs new file mode 100644 index 0000000000..c5c587c42d --- /dev/null +++ b/Orm/Xtensive.Orm.Tests.Framework/StorageTimeZoneProvider.cs @@ -0,0 +1,131 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using Xtensive.Sql; + +namespace Xtensive.Orm.Tests +{ + internal abstract class StorageTimeZoneProvider : IStorageTimeZoneProvider + { + public TimeSpan TimeZoneOffset { get; private set; } + + protected abstract TimeSpan GetServerTimezone(SqlDriver sqlDriver); + + public StorageTimeZoneProvider(SqlDriver sqlDriver) + { + TimeZoneOffset = GetServerTimezone(sqlDriver); + } + } + + internal sealed class LocalTimeZoneProvider : IStorageTimeZoneProvider + { + public TimeSpan TimeZoneOffset => TimeZoneInfo.Local.BaseUtcOffset; + } + + internal sealed class SqlServerTimeZoneProvider : StorageTimeZoneProvider + { + protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver) + { + using (var c = sqlDriver.CreateConnection()) { + c.Open(); + using (var cmd = c.CreateCommand("SELECT SYSDATETIMEOFFSET();")) { + var dateTimeOffset = (DateTimeOffset) cmd.ExecuteScalar(); + return dateTimeOffset.Offset; + } + } + } + + public SqlServerTimeZoneProvider(SqlDriver sqlDriver) + : base(sqlDriver) + { + } + } + + internal sealed class OracleTimeZoneProvider : StorageTimeZoneProvider + { + protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver) + { + var mappings = sqlDriver.TypeMappings[typeof(DateTimeOffset)]; + + TimeSpan value; + using (var connection = sqlDriver.CreateConnection()) { + connection.Open(); + var commandText = "select CAST(systimestamp AS timestamp with time zone) as \"local_time\" from dual"; + using (var cmd = connection.CreateCommand(commandText)) { + using (var reader = cmd.ExecuteReader()) { + _ = reader.Read(); + var dateTimeOffset = (DateTimeOffset) mappings.ReadValue(reader, 0); + value = dateTimeOffset.Offset; + } + } + connection.Close(); + } + return value; + } + + public OracleTimeZoneProvider(SqlDriver sqlDriver) + : base(sqlDriver) + { + } + } + + internal sealed class PgSqlTimeZoneProvider : StorageTimeZoneProvider + { + protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver) + { + var mappings = sqlDriver.TypeMappings[typeof(DateTimeOffset)]; + var referenceDate = new DateTime(2016, 10, 23); + TimeSpan value; + using (var connection = sqlDriver.CreateConnection()) { + connection.Open(); + var commandText = $"select CAST('{referenceDate.ToString("yyyy-MM-dd HH:mm:ss")}' AS timestamp with time zone) as \"local_time\""; + using (var cmd = connection.CreateCommand(commandText)) { + using (var reader = cmd.ExecuteReader()) { + _ = reader.Read(); + var dateTimeOffset = (DateTimeOffset) mappings.ReadValue(reader, 0); + + value = dateTimeOffset.Offset; + if (dateTimeOffset.DateTime != referenceDate) { + value = value.Add(referenceDate - dateTimeOffset.DateTime); + } + } + } + connection.Close(); + } + return value; + } + + public PgSqlTimeZoneProvider(SqlDriver sqlDriver) : base(sqlDriver) + { + } + } + + internal sealed class SqliteTimeZoneProvider : StorageTimeZoneProvider + { + protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver) + { + TimeSpan value; + using (var connection = sqlDriver.CreateConnection()) { + connection.Open(); + var commandText = "SELECT ((STRFTIME ('%s', '2016-01-01 12:00:00') - STRFTIME ('%s', '2016-01-01 12:00:00', 'UTC')) / 60)"; + using (var cmd = connection.CreateCommand(commandText)) { + using (var reader = cmd.ExecuteReader()) { + _ = reader.Read(); + var offsetInMinutes = reader.GetInt32(0); + + value = new TimeSpan(offsetInMinutes / 60, offsetInMinutes % 60, 0); + } + } + connection.Close(); + } + return value; + } + + public SqliteTimeZoneProvider(SqlDriver sqlDriver) + : base(sqlDriver) + { + } + } +} \ No newline at end of file From 2ff5b47a1bddd96587981194b72d572f82df3f83 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 18:50:25 +0500 Subject: [PATCH 56/86] Test for data conversion on Schema upgrade --- .../Upgrade/DateTimeDataConversions.cs | 533 ++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs diff --git a/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs b/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs new file mode 100644 index 0000000000..09d9ccaaf5 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs @@ -0,0 +1,533 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using NUnit.Framework; +using Xtensive.Orm.Configuration; +using Xtensive.Orm.Providers; +using Xtensive.Orm.Upgrade; +using Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel; + + +namespace Xtensive.Orm.Tests.Upgrade +{ + [TestFixture] + public class DateTimeTypesDataConversionTest + { + private readonly Dictionary upgradeHandlers = new(); + private readonly Dictionary initEntities = new(); + + private readonly TimeSpan ServerTimeZone = StorageProviderInfo.Instance.TimeZoneProvider.TimeZoneOffset; + private readonly bool RequreToServerOffset = + StorageProviderInfo.Instance.Provider is StorageProvider.PostgreSql; + + private readonly DateTimeOffset dateTimeOffsetInitValue = + new DateTimeOffset(new DateTime(2017, 10, 23, 22, 33, 44, 321), new TimeSpan(3, 45, 0)); + private readonly DateTime dateTimeInitValue = new DateTime(2017, 10, 23, 22,33,44, 321); +#if NET6_0_OR_GREATER + private readonly TimeOnly timeOnlyInitValue = TimeOnly.FromTimeSpan(new TimeSpan(22, 33, 44)); + private readonly DateOnly dateOnlyInitValue = DateOnly.FromDateTime(new DateTime(2017, 10, 23)); +#endif + + [OneTimeSetUp] + public void OneTimeSetup() + { + upgradeHandlers[nameof(DateTimeToDateTimeOffsetTest)] = typeof(DateTimeToDateTimeOffsetUpgradeHandler); + upgradeHandlers[nameof(DateTimeOffsetToDateTimeTest)] = typeof(DateTimeOffsetToDateTimeUpgradeHandler); +#if NET6_0_OR_GREATER + + upgradeHandlers[nameof(DateTimeToDateOnlyTest)] = typeof(DateTimeToDateOnlyUpgradeHandler); + upgradeHandlers[nameof(DateTimeToTimeOnlyTest)] = typeof(DateTimeToTimeOnlyUpgradeHandler); + + upgradeHandlers[nameof(DateTimeOffsetToDateOnlyTest)] = typeof(DateTimeOffsetToDateOnlyUpgradeHandler); + upgradeHandlers[nameof(DateTimeOffsetToTimeOnlyTest)] = typeof(DateTimeOffsetToTimeOnlyUpgradeHandler); + + upgradeHandlers[nameof(DateOnlyToDateTimeTest)] = typeof(DateOnlyToDateTimeUpgradeHandler); + upgradeHandlers[nameof(DateOnlyToDateTimeOffsetTest)] = typeof(DateOnlyToDateTimeOffsetUpgradeHandler); + upgradeHandlers[nameof(DateOnlyToTimeOnlyTest)] = typeof(DateOnlyToTimeOnlyUpgradeHandler); + + upgradeHandlers[nameof(TimeOnlyToDateTimeTest)] = typeof(TimeOnlyToDateTimeUpgradeHandler); + upgradeHandlers[nameof(TimeOnlyToDateTimeOffsetTest)] = typeof(TimeOnlyToDateTimeOffsetUpgradeHandler); + upgradeHandlers[nameof(TimeOnlyToDateOnlyTest)] = typeof(TimeOnlyToDateOnlyUpgradeHandler); +#endif + + initEntities[nameof(DateTimeToDateTimeOffsetTest)] = (typeof(DateTimeEntity), dateTimeInitValue); + initEntities[nameof(DateTimeOffsetToDateTimeTest)] = (typeof(DateTimeOffsetEntity), dateTimeOffsetInitValue); +#if NET6_0_OR_GREATER + + initEntities[nameof(DateTimeToDateOnlyTest)] = (typeof(DateTimeEntity), dateTimeInitValue); + initEntities[nameof(DateTimeToTimeOnlyTest)] = (typeof(DateTimeEntity), dateTimeInitValue); + + initEntities[nameof(DateTimeOffsetToDateOnlyTest)] = (typeof(DateTimeOffsetEntity), dateTimeOffsetInitValue); + initEntities[nameof(DateTimeOffsetToTimeOnlyTest)] = (typeof(DateTimeOffsetEntity), dateTimeOffsetInitValue); + + initEntities[nameof(DateOnlyToDateTimeTest)] = (typeof(DateOnlyEntity), dateOnlyInitValue); + initEntities[nameof(DateOnlyToDateTimeOffsetTest)] = (typeof(DateOnlyEntity), dateOnlyInitValue); + initEntities[nameof(DateOnlyToTimeOnlyTest)] = (typeof(DateOnlyEntity), dateOnlyInitValue); + + initEntities[nameof(TimeOnlyToDateTimeTest)] = (typeof(TimeOnlyEntity), timeOnlyInitValue); + initEntities[nameof(TimeOnlyToDateTimeOffsetTest)] = (typeof(TimeOnlyEntity), timeOnlyInitValue); + initEntities[nameof(TimeOnlyToDateOnlyTest)] = (typeof(TimeOnlyEntity), timeOnlyInitValue); +#endif + } + + [SetUp] + public void BeforeEveryTestMethodSetup() + { + var entityAndValue = GetInitialEntityType(); + if (entityAndValue.type.Name.Contains("Offset", StringComparison.Ordinal)) { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + } + + using (var domain = Domain.Build(BuildConfiguration(DomainUpgradeMode.Recreate, entityAndValue.type))) + using (var session = domain.OpenSession()) + using (var tx = session.OpenTransaction()) { + _ = entityAndValue.type + .GetMethod(nameof(DateTimeEntity.CreateTestInstance), BindingFlags.Public | BindingFlags.Static) + .Invoke(null,new[] { session, entityAndValue.initValue}); + + tx.Complete(); + } + } + + [Test] + public void DateTimeToDateTimeOffsetTest() + { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + + UpgradeAndTestData(new DateTimeOffset(dateTimeInitValue, ServerTimeZone)); + } + + [Test] + public void DateTimeOffsetToDateTimeTest() + { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + + var expectedValue = RequreToServerOffset + ? dateTimeOffsetInitValue.ToOffset(ServerTimeZone).DateTime + : dateTimeOffsetInitValue.DateTime; + + UpgradeAndTestData(expectedValue); + } + +#if NET6_0_OR_GREATER + [Test] + public void DateTimeToDateOnlyTest() + { + UpgradeAndTestData(DateOnly.FromDateTime(dateTimeInitValue)); + } + + [Test] + public void DateTimeToTimeOnlyTest() + { + if (StorageProviderInfo.Instance.Provider == StorageProvider.MySql) { + UpgradeAndTestData(timeOnlyInitValue);// datetime in mysql is without fractions + } + else { + UpgradeAndTestData(TimeOnly.FromDateTime(dateTimeInitValue)); + } + } + + [Test] + public void DateTimeOffsetToDateOnlyTest() + { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + + var expectedValue = RequreToServerOffset + ? DateOnly.FromDateTime(dateTimeOffsetInitValue.ToOffset(ServerTimeZone).DateTime) + : DateOnly.FromDateTime(dateTimeOffsetInitValue.DateTime); + + UpgradeAndTestData(expectedValue); + } + + [Test] + public void DateTimeOffsetToTimeOnlyTest() + { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + + var expectedValue = RequreToServerOffset + ? TimeOnly.FromDateTime(dateTimeOffsetInitValue.ToOffset(ServerTimeZone).DateTime) + : TimeOnly.FromDateTime(dateTimeOffsetInitValue.DateTime); + + UpgradeAndTestData(expectedValue); + } + + [Test] + public void DateOnlyToDateTimeTest() + { + UpgradeAndTestData(dateOnlyInitValue.ToDateTime(TimeOnly.MinValue)); + } + + [Test] + public void DateOnlyToDateTimeOffsetTest() + { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + + UpgradeAndTestData(new DateTimeOffset(dateOnlyInitValue.ToDateTime(TimeOnly.MinValue), ServerTimeZone)); + } + + [Test] + public void DateOnlyToTimeOnlyTest() + { + var provider = StorageProviderInfo.Instance.Provider; + + if (provider == StorageProvider.PostgreSql) { + UpgradeAndExpectException(); + } + else if (provider == StorageProvider.Oracle) { + UpgradeAndExpectException(); + } + else if (provider == StorageProvider.MySql) { + UpgradeAndExpectException(); + } + else if (provider == StorageProvider.Firebird) { + UpgradeAndExpectException(); + } + else if (provider == StorageProvider.SqlServer) { + UpgradeAndExpectException(); + } + } + + [Test] + public void TimeOnlyToDateTimeTest() + { + var provider = StorageProviderInfo.Instance.Provider; + + DateTime expectedValue; + if (provider == StorageProvider.Oracle) { + expectedValue = DateTime.MinValue.Add(timeOnlyInitValue.ToTimeSpan()); + } + else if (provider == StorageProvider.Firebird || provider == StorageProvider.MySql) { + expectedValue = DateTime.Now.Date.Add(timeOnlyInitValue.ToTimeSpan()); + } + else if(provider == StorageProvider.PostgreSql) { + expectedValue = new DateTime(1970, 1, 1).Add(timeOnlyInitValue.ToTimeSpan()); + } + else { + expectedValue = new DateTime(1900, 1, 1).Add(timeOnlyInitValue.ToTimeSpan()); + } + + UpgradeAndTestData(expectedValue); + } + + [Test] + public void TimeOnlyToDateTimeOffsetTest() + { + Require.AnyFeatureSupported(ProviderFeatures.DateTimeOffset | ProviderFeatures.DateTimeOffsetEmulation); + + var provider = StorageProviderInfo.Instance.Provider; + + DateTimeOffset expectedValue; + if (provider == StorageProvider.Oracle) { + expectedValue = new DateTimeOffset( + DateTime.MinValue.Add(timeOnlyInitValue.ToTimeSpan()), + TimeSpan.FromMinutes(60 * 4 + 2)); + } + else if (provider == StorageProvider.PostgreSql) { + expectedValue = new DateTimeOffset(new DateTime(1970, 1, 1)).Add(timeOnlyInitValue.ToTimeSpan()).ToOffset(ServerTimeZone); + } + else { + expectedValue = new DateTimeOffset(new DateTime(1900, 1, 1).Add(timeOnlyInitValue.ToTimeSpan()), ServerTimeZone); + } + + UpgradeAndTestData(expectedValue); + } + + [Test] + public void TimeOnlyToDateOnlyTest() + { + if (StorageProviderInfo.Instance.Provider == StorageProvider.PostgreSql) { + UpgradeAndExpectException(); + } + else { + UpgradeAndExpectException(); + } + } +#endif + + private void UpgradeAndTestData(TExpectedValue expectedValue) + where TEntity : Entity + { + var configuration = BuildConfiguration(DomainUpgradeMode.Perform, typeof(TEntity), GetUpgradeHandler()); + + using (var domain = Domain.Build(configuration)) + using (var session = domain.OpenSession()) + using (var tx = session.OpenTransaction()) { + var entity = session.Query.All().FirstOrDefault(); + Assert.That(entity["ConversionField"], Is.EqualTo(expectedValue)); + } + } + + private void UpgradeSafelyAndTestData(TExpectedValue expectedValue) + where TEntity : Entity + { + var configuration = BuildConfiguration(DomainUpgradeMode.Perform, typeof(TEntity), GetUpgradeHandler()); + + using (var domain = Domain.Build(configuration)) + using (var session = domain.OpenSession()) + using (var tx = session.OpenTransaction()) { + var entity = session.Query.All().FirstOrDefault(); + Assert.That(entity["ConversionField"], Is.EqualTo(expectedValue)); + } + } + + private void UpgradeAndExpectException() + where TEntity : Entity + where TExpectedException : Exception + { + var configuration = BuildConfiguration(DomainUpgradeMode.PerformSafely, typeof(TEntity), GetUpgradeHandler()); + _ = Assert.Throws(() => Domain.Build(configuration)); + } + + private DomainConfiguration BuildConfiguration(DomainUpgradeMode upgradeMode, Type entityType, Type upgraderType = null) + { + var domainConfiguration = DomainConfigurationFactory.Create(); + + domainConfiguration.UpgradeMode = upgradeMode; + domainConfiguration.Types.Register(entityType); + if (upgradeMode is DomainUpgradeMode.Perform or DomainUpgradeMode.PerformSafely + && upgraderType is not null) { + domainConfiguration.Types.Register(upgraderType); + } + return domainConfiguration; + } + + private Type GetUpgradeHandler() => + upgradeHandlers[TestContext.CurrentContext.Test.MethodName]; + + private (Type type, object initValue) GetInitialEntityType() => + initEntities[TestContext.CurrentContext.Test.MethodName]; + } +} + +namespace Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel +{ + [HierarchyRoot] + [TableMapping("TestEntity")] + public class DateTimeEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public DateTime ConversionField { get; set; } + + public static object CreateTestInstance(Session session, object value) + { + return new DateTimeEntity(session, (DateTime) value); + } + + public DateTimeEntity(Session session, DateTime value) + : base(session) + { + ConversionField = value; + } + } + + [HierarchyRoot] + [TableMapping("TestEntity")] + public class DateTimeOffsetEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public DateTimeOffset ConversionField { get; set; } + + public static object CreateTestInstance(Session session, object value) + { + return new DateTimeOffsetEntity(session, (DateTimeOffset) value); + } + + public DateTimeOffsetEntity(Session session, DateTimeOffset value) + : base(session) + { + ConversionField = value; + } + } +#if NET6_0_OR_GREATER + + [HierarchyRoot] + [TableMapping("TestEntity")] + public class DateOnlyEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public DateOnly ConversionField { get; set; } + + public static object CreateTestInstance(Session session, object value) + { + return new DateOnlyEntity(session, (DateOnly) value); + } + + public DateOnlyEntity(Session session, DateOnly value) + : base(session) + { + ConversionField = value; + } + } + + [HierarchyRoot] + [TableMapping("TestEntity")] + public class TimeOnlyEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public TimeOnly ConversionField { get; set; } + + public static object CreateTestInstance(Session session, object value) + { + return new TimeOnlyEntity(session, (TimeOnly) value); + } + + public TimeOnlyEntity(Session session, TimeOnly value) + : base(session) + { + ConversionField = value; + } + } +#endif + + public class DateTimeToDateTimeOffsetUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateTimeEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateTimeOffsetToDateTimeUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateTimeOffsetEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + +#if NET6_0_OR_GREATER + public class DateTimeToDateOnlyUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateTimeEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateTimeToTimeOnlyUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateTimeEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateTimeOffsetToDateOnlyUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateTimeOffsetEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateTimeOffsetToTimeOnlyUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateTimeOffsetEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateOnlyToDateTimeUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateOnlyEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateOnlyToDateTimeOffsetUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateOnlyEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class DateOnlyToTimeOnlyUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.DateOnlyEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class TimeOnlyToDateTimeUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.TimeOnlyEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class TimeOnlyToDateTimeOffsetUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.TimeOnlyEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } + + public class TimeOnlyToDateOnlyUpgradeHandler : UpgradeHandler + { + public override bool CanUpgradeFrom(string oldVersion) => true; + + protected override void AddUpgradeHints(ISet hints) + { + _ = hints.Add(RenameTypeHint.Create("Xtensive.Orm.Tests.Upgrade.DateTimeTypesDataConversionModel.TimeOnlyEntity")); + _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); + } + } +#endif +} + From edfa2e092683dd64aa583165d7508bd31ed182a0 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 18:54:01 +0500 Subject: [PATCH 57/86] ExpressionProcessor: additional cast to date/time in case of SQLite --- .../ExpressionProcessor.Helpers.cs | 8 ++++++++ .../Expressions/ExpressionProcessor.cs | 19 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs index 2f77209b25..4cbcd4a757 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs @@ -215,6 +215,14 @@ private static bool IsBooleanExpression(Expression expression) => private static bool IsDateTimeExpression(Expression expression) => IsExpressionOf(expression, WellKnownTypes.DateTime); +#if NET6_0_OR_GREATER //DO_DATEONLY + + private static bool IsDateOnlyExpression(Expression expression) => + IsExpressionOf(expression, WellKnownTypes.DateOnly); + + private static bool IsTimeOnlyExpression(Expression expression) => + IsExpressionOf(expression, WellKnownTypes.TimeOnly); +#endif private static bool IsDateTimeOffsetExpression(Expression expression) => IsExpressionOf(expression, WellKnownTypes.DateTimeOffset); diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs index c26ce5a3cf..0dd2a0bfe2 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.cs @@ -217,10 +217,21 @@ protected override SqlExpression VisitBinary(BinaryExpression expression) if (dateTimeEmulation && left.NodeType != SqlNodeType.Null && right.NodeType != SqlNodeType.Null - && IsComparisonExpression(expression) - && (IsDateTimeExpression(expression.Left) || IsDateTimeExpression(expression.Right))) { - left = SqlDml.Cast(left, SqlType.DateTime); - right = SqlDml.Cast(right, SqlType.DateTime); + && IsComparisonExpression(expression)) { + if (IsDateTimeExpression(expression.Left) || IsDateTimeExpression(expression.Right)) { + left = SqlDml.Cast(left, SqlType.DateTime); + right = SqlDml.Cast(right, SqlType.DateTime); + } +#if NET6_0_OR_GREATER + else if (IsDateOnlyExpression(expression.Left) || IsDateOnlyExpression(expression.Right)) { + left = SqlDml.Cast(left, SqlType.Date); + right = SqlDml.Cast(right, SqlType.Date); + } + else if (IsTimeOnlyExpression(expression.Left) || IsDateOnlyExpression(expression.Right)) { + left = SqlDml.Cast(left, SqlType.Time); + right = SqlDml.Cast(right, SqlType.Time); + } +#endif } //handle SQLite DateTimeOffset comparsion From 8d7b8c1a66937bb8cb2c9492398dedbcd77369e8 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 18:56:02 +0500 Subject: [PATCH 58/86] TimeOny/DateOnly parameters formatting for human readable string --- Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs b/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs index 4447c6af71..38c78f0255 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs @@ -36,6 +36,14 @@ public static string ToHumanReadableString(this DbCommand command) case DateTimeOffset dateTimeOffset: value = dateTimeOffset.ToString("yyyy-MM-dd HH:mm:ss.fffffffK"); break; +#if NET6_0_OR_GREATER //DO_DATEONLY + case DateOnly dateOnly: + value = dateOnly.ToString("yyyy-MM-dd"); + break; + case TimeOnly timeOnly: + value = timeOnly.ToString("HH:mm:ss.fffffff"); + break; +#endif } result.AppendFormat("{0}='{1}';", parameter.ParameterName, value); } From 047a76b2063372ea4a953714f6720e4846ac9d5f Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 18:57:27 +0500 Subject: [PATCH 59/86] Functions to convert betwee Date/Time/DateTime/DateTimeOffset --- Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 24 +++++--- Orm/Xtensive.Orm/Sql/SqlDml.cs | 64 +++++++++++++++++++-- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index 0acc37de23..5984b6bf6f 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -60,21 +60,31 @@ public enum SqlFunctionType // date time / interval functions // not ansi sql but our cross-server solution - - DateTimeConstruct, - DateTimeAddYears, - DateTimeAddMonths, #if NET6_0_OR_GREATER //DO_DATEONLY + DateConstruct, DateAddYears, DateAddMonths, DateAddDays, DateToString, + DateToDateTime, + DateToDateTimeOffset, + TimeConstruct, TimeAddHours, TimeAddMinutes, TimeToString, + TimeToDateTime, + TimeToDateTimeOffset, #endif + + DateTimeConstruct, + DateTimeAddYears, + DateTimeAddMonths, DateTimeTruncate, DateTimeToStringIso, +#if NET6_0_OR_GREATER //DO_DATEONLY + DateTimeToTime, + DateTimeToDate, +#endif IntervalConstruct, IntervalToMilliseconds, IntervalToNanoseconds, @@ -89,10 +99,10 @@ public enum SqlFunctionType DateTimeOffsetToLocalTime, DateTimeOffsetToUtcTime, DateTimeToDateTimeOffset, - + DateTimeOffsetToDateTime, #if NET6_0_OR_GREATER //DO_DATEONLY - DateConstruct, - TimeConstruct, + DateTimeOffsetToTime, + DateTimeOffsetToDate, #endif // .NET like rounding functions diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 6db7933c0b..cf5405d6d3 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -693,6 +693,12 @@ public static SqlFunctionCall DateTimeAddMonths(SqlExpression source, SqlExpress } #if NET6_0_OR_GREATER //DO_DATEONLY + public static SqlFunctionCall DateTimeToTime(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.DateTimeToTime, expression); + } + public static SqlFunctionCall DateAddYears(SqlExpression source, SqlExpression years) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); @@ -714,6 +720,30 @@ public static SqlFunctionCall DateAddDays(SqlExpression source, SqlExpression da return new SqlFunctionCall(SqlFunctionType.DateAddDays, source, days); } + public static SqlFunctionCall DateToString(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.DateToString, expression); + } + + public static SqlFunctionCall DateToDateTime(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.DateToDateTime, expression); + } + + public static SqlFunctionCall DateTimeToDate(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.DateTimeToDate, expression); + } + + public static SqlFunctionCall DateToDateTimeOffset(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.DateToDateTimeOffset, expression); + } + public static SqlFunctionCall TimeAddHours(SqlExpression source, SqlExpression hours) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); @@ -728,16 +758,22 @@ public static SqlFunctionCall TimeAddMinutes(SqlExpression source, SqlExpression return new SqlFunctionCall(SqlFunctionType.TimeAddMinutes, source, minutes); } - public static SqlFunctionCall DateToString(SqlExpression expression) + public static SqlFunctionCall TimeToString(SqlExpression expression) { ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); - return new SqlFunctionCall(SqlFunctionType.DateToString, expression); + return new SqlFunctionCall(SqlFunctionType.TimeToString, expression); } - public static SqlFunctionCall TimeToString(SqlExpression expression) + public static SqlFunctionCall TimeToDateTime(SqlExpression expression) { ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); - return new SqlFunctionCall(SqlFunctionType.TimeToString, expression); + return new SqlFunctionCall(SqlFunctionType.TimeToDateTime, expression); + } + + public static SqlFunctionCall TimeToDateTimeOffset(SqlExpression expression) + { + ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); + return new SqlFunctionCall(SqlFunctionType.TimeToDateTimeOffset, expression); } #endif @@ -868,6 +904,26 @@ public static SqlFunctionCall DateTimeToDateTimeOffset(SqlExpression dateTime) return new SqlFunctionCall(SqlFunctionType.DateTimeToDateTimeOffset, dateTime); } + public static SqlFunctionCall DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) + { + ArgumentValidator.EnsureArgumentNotNull(dateTimeOffset, nameof(dateTimeOffset)); + return new SqlFunctionCall(SqlFunctionType.DateTimeOffsetToDateTime, dateTimeOffset); + } + +#if NET6_0_OR_GREATER //DO_DATEONLY + public static SqlFunctionCall DateTimeOffsetToTime(SqlExpression dateTimeOffset) + { + ArgumentValidator.EnsureArgumentNotNull(dateTimeOffset, nameof(dateTimeOffset)); + return new SqlFunctionCall(SqlFunctionType.DateTimeOffsetToTime, dateTimeOffset); + } + + public static SqlFunctionCall DateTimeOffsetToDate(SqlExpression dateTimeOffset) + { + ArgumentValidator.EnsureArgumentNotNull(dateTimeOffset, nameof(dateTimeOffset)); + return new SqlFunctionCall(SqlFunctionType.DateTimeOffsetToDate, dateTimeOffset); + } +#endif + #endregion #region FunctionCall From 753d18acb67d12e637e32aaa8f89569db8857277 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Feb 2023 18:58:26 +0500 Subject: [PATCH 60/86] SqlActionTranslator uses new data conversion functions --- .../Upgrade/Internals/SqlActionTranslator.cs | 77 +++++++++++++++++-- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs b/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs index db1ed1b187..7a57ca3417 100644 --- a/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs +++ b/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs @@ -912,15 +912,16 @@ private void ChangeColumnType(PropertyChangeAction action) } else { var getValue = SqlDml.Case(); - getValue.Add(SqlDml.IsNull(tableRef[tempName]), GetDefaultValueExpression(targetColumn)); - - if (newSqlType.Type==SqlType.DateTimeOffset) - getValue.Add(SqlDml.IsNotNull(tableRef[tempName]), SqlDml.DateTimeToDateTimeOffset(tableRef[tempName])); - else if (newSqlType.Type==SqlType.DateTime && providerInfo.Supports(ProviderFeatures.DateTimeOffsetEmulation)) - getValue.Add(SqlDml.IsNotNull(tableRef[tempName]), SqlDml.Cast(SqlDml.Extract(SqlDateTimeOffsetPart.DateTime, tableRef[tempName]), newSqlType)); - else - getValue.Add(SqlDml.IsNotNull(tableRef[tempName]), SqlDml.Cast(tableRef[tempName], newSqlType)); + _ = getValue.Add(SqlDml.IsNull(tableRef[tempName]), GetDefaultValueExpression(targetColumn)); + SqlExpression convertExpression; + if (IsDateTimeType(newSqlType.Type) || IsDateTimeType(column.DataType.Type)) { + convertExpression = GetDataConversion(column.DataType, newSqlType, tableRef[tempName]); + } + else { + convertExpression = SqlDml.Cast(tableRef[tempName], newSqlType); + } + _ = getValue.Add(SqlDml.IsNotNull(tableRef[tempName]), convertExpression); copyValues.Values[tableRef[originalName]] = getValue; } upgradeOutput.BreakBatch(); @@ -931,6 +932,66 @@ private void ChangeColumnType(PropertyChangeAction action) DropColumn(table.TableColumns[tempName], cleanupOutput); } + private static SqlExpression GetDataConversion(SqlValueType oldType, SqlValueType newType, SqlTableColumn sqlTableColumn) + { + var oldSqlType = oldType.Type; + var newSqlType = newType.Type; + + if (oldSqlType == SqlType.DateTime && newSqlType == SqlType.DateTimeOffset) { + return SqlDml.DateTimeToDateTimeOffset(sqlTableColumn); + } + if (oldSqlType == SqlType.DateTimeOffset && newSqlType == SqlType.DateTime) { + return SqlDml.DateTimeOffsetToDateTime(sqlTableColumn); + } +#if NET6_0_OR_GREATER //DO_DATEONLY + if (oldSqlType == SqlType.DateTime && newSqlType == SqlType.Date) { + return SqlDml.DateTimeToDate(sqlTableColumn); + } + if (oldSqlType == SqlType.DateTime && newSqlType == SqlType.Time) { + return SqlDml.DateTimeToTime(sqlTableColumn); + } + if (oldSqlType == SqlType.DateTimeOffset && newSqlType == SqlType.Date) { + return SqlDml.DateTimeOffsetToDate(sqlTableColumn); + } + if (oldSqlType == SqlType.DateTimeOffset && newSqlType == SqlType.Time) { + return SqlDml.DateTimeOffsetToTime(sqlTableColumn); + } + if (oldSqlType == SqlType.Date && newSqlType == SqlType.DateTime) { + return SqlDml.DateToDateTime(sqlTableColumn); + } + if (oldSqlType == SqlType.Date && newSqlType == SqlType.DateTimeOffset) { + return SqlDml.DateToDateTimeOffset(sqlTableColumn); + } + if (oldSqlType == SqlType.Time && newSqlType == SqlType.DateTime) { + return SqlDml.TimeToDateTime(sqlTableColumn); + } + if (oldSqlType == SqlType.Time && newSqlType == SqlType.DateTimeOffset) { + return SqlDml.TimeToDateTimeOffset(sqlTableColumn); + } + //Date -> Time = invalid in most cases. + //Time -> Date = invalid in most cases. + //let storage throw exception on attempt +#endif + + return SqlDml.Cast(sqlTableColumn, newType); + } + +#if NET6_0_OR_GREATER //DO_DATEONLY + private static bool IsDateTimeType(in SqlType type) + { + return type == SqlType.DateTime + || type == SqlType.DateTimeOffset + || type == SqlType.Date + || type == SqlType.Time; + } +#else + private static bool IsDateTimeType(in SqlType type) + { + return type == SqlType.DateTime + || type == SqlType.DateTimeOffset; + } +#endif + private Table CreateTable(TableInfo tableInfo) { var node = resolver.Resolve(sqlModel, tableInfo.Name); From 519a8c1e38fcb513bd41bed84d7ae2162c112739 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 22 Feb 2023 16:14:51 +0500 Subject: [PATCH 61/86] Conversions of date/time/datetime/datetimeoffset values --- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 70 ++++++++++----- .../Sql.Drivers.MySql/v5_0/Compiler.cs | 39 +++++++- .../Sql.Drivers.MySql/v5_6/Compiler.cs | 3 + .../Sql.Drivers.Oracle/v09/Compiler.cs | 88 ++++++++++++++++++- .../Sql.Drivers.Oracle/v09/TypeMapper.cs | 3 +- .../Sql.Drivers.PostgreSql/v8_0/Compiler.cs | 66 +++++++++++++- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 24 +++++ .../Sql.Drivers.SqlServer/v10/Compiler.cs | 37 +++++++- .../Sql.Drivers.Sqlite/v3/Compiler.cs | 87 +++++++++++++++--- 9 files changed, 375 insertions(+), 42 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index b13ac7edc1..637b3677ab 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -205,47 +205,49 @@ public override void Visit(SqlBinary node) /// public override void Visit(SqlFunctionCall node) { + var arguments = node.Arguments; + switch (node.FunctionType) { case SqlFunctionType.Concat: - Visit(SqlDml.Concat(node.Arguments.ToArray(node.Arguments.Count))); + Visit(SqlDml.Concat(arguments.ToArray(node.Arguments.Count))); return; case SqlFunctionType.DateTimeTruncate: - Visit(SqlDml.Cast(node.Arguments[0], new SqlValueType("Date"))); + Visit(SqlDml.Cast(arguments[0], new SqlValueType("Date"))); return; case SqlFunctionType.IntervalToMilliseconds: - Visit(CastToLong(node.Arguments[0]) / NanosecondsPerMillisecond); + Visit(CastToLong(arguments[0]) / NanosecondsPerMillisecond); return; case SqlFunctionType.IntervalConstruct: case SqlFunctionType.IntervalToNanoseconds: - Visit(CastToLong(node.Arguments[0])); + Visit(CastToLong(arguments[0])); return; case SqlFunctionType.DateTimeAddMonths: - Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); + Visit(DateAddMonth(arguments[0], arguments[1])); return; case SqlFunctionType.DateTimeAddYears: - Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); + Visit(DateAddYear(arguments[0], arguments[1])); return; case SqlFunctionType.DateTimeConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime), - node.Arguments[0] - 2001), - node.Arguments[1] - 1), - node.Arguments[2] - 1)); + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1)); return; #if NET6_0_OR_GREATER //DO_DATEONLY case SqlFunctionType.DateAddYears: - Visit(DateAddYear(node.Arguments[0], node.Arguments[1])); + Visit(DateAddYear(arguments[0], arguments[1])); return; case SqlFunctionType.DateAddMonths: - Visit(DateAddMonth(node.Arguments[0], node.Arguments[1])); + Visit(DateAddMonth(arguments[0], arguments[1])); return; case SqlFunctionType.DateAddDays: - Visit(DateAddDay(node.Arguments[0], node.Arguments[1])); + Visit(DateAddDay(arguments[0], arguments[1])); return; case SqlFunctionType.DateConstruct: Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateOnly(2001, 1, 1)), SqlType.Date), - node.Arguments[0] - 2001), - node.Arguments[1] - 1), - node.Arguments[2] - 1)); + arguments[0] - 2001), + arguments[1] - 1), + arguments[2] - 1)); return; case SqlFunctionType.TimeAddHours: Visit(DateAddHour(node.Arguments[0], node.Arguments[1])); @@ -255,20 +257,32 @@ public override void Visit(SqlFunctionCall node) return; case SqlFunctionType.TimeConstruct: Visit(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Cast(SqlDml.Literal(new TimeOnly(0, 0, 0)), SqlType.Time), - node.Arguments[0]), - node.Arguments[1]), - node.Arguments[2]), - node.Arguments[3])); + arguments[0]), + arguments[1]), + arguments[2]), + arguments[3])); return; case SqlFunctionType.DateToString: - Visit(DateToString(node.Arguments[0])); + Visit(DateToString(arguments[0])); return; case SqlFunctionType.TimeToString: - Visit(TimeToString(node.Arguments[0])); + Visit(TimeToString(arguments[0])); + return; + case SqlFunctionType.DateToDateTime: + DateToDateTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToDate: + DateTimeToDate(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToTime: + DateTimeToTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTime: + TimeToDateTime(arguments[0]).AcceptVisitor(this); return; #endif case SqlFunctionType.DateTimeToStringIso: - Visit(DateTimeToStringIso(node.Arguments[0])); + Visit(DateTimeToStringIso(arguments[0])); return; } @@ -356,6 +370,18 @@ protected static SqlUserFunctionCall BitNot(SqlExpression operand) => SqlDml.FunctionCall("BIN_NOT", operand); #if NET6_0_OR_GREATER //DO_DATEONLY + protected static SqlExpression TimeToDateTime(SqlExpression time) => + SqlDml.Cast(time, SqlType.DateTime); + + protected static SqlExpression DateTimeToTime(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Time); + + protected static SqlExpression DateToDateTime(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTime); + + protected static SqlExpression DateTimeToDate(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Date); + protected static SqlFunctionCall DateToString(SqlExpression date) { return SqlDml.Substring(date, 0, 10);; diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs index aca48c3dd0..3c93e3f076 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs @@ -232,6 +232,18 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.TimeToString: Visit(TimeToString(arguments[0])); return; + case SqlFunctionType.DateTimeToDate: + DateTimeToDate(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTime: + DateToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToTime: + DateTimeToTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTime: + TimeToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; #endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(arguments[0])); @@ -244,7 +256,9 @@ public override void Visit(SqlFunctionCall node) #if NET6_0_OR_GREATER //DO_DATEONLY public override void Visit(SqlPlaceholder node) { - if (node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type == typeof(TimeOnly)) { + if (node.Id is Orm.Providers.QueryParameterBinding qpb + && qpb.BindingType == Orm.Providers.QueryParameterBindingType.Regular + && qpb.TypeMapping.Type == typeof(TimeOnly)) { _ = context.Output.Append("TIME("); base.Visit(node); _ = context.Output.Append(")"); @@ -310,7 +324,6 @@ protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpress NanosecondsPerDay); protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) => - SqlDml.FunctionCall("TIME", SqlDml.FunctionCall( "DATE_ADD", @@ -358,6 +371,7 @@ protected static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpressi protected static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) => SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(months, SqlDml.Native("MONTH")))); + protected static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) => SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(days, SqlDml.Native("DAY")))); @@ -378,6 +392,27 @@ protected static SqlUserFunctionCall DateToString(SqlExpression dateTime) => protected static SqlUserFunctionCall TimeToString(SqlExpression dateTime) => SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%H:%i:%s.%f0"); + + private static SqlExpression DateTimeToDate(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Date); + + private static SqlExpression DateToDateTime(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTime); + + private static SqlExpression DateTimeToTime(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Time); + + // can't convert via cast, because mysql shots to its head and creates + // value that it can't read later. This mimics conversion that occurs + // in newer versions (5.6+) and use current date as a source of year, + // month and day values :-) + private static SqlExpression TimeToDateTime(SqlExpression time) => + SqlDml.FunctionCall("DATE_ADD", + SqlDml.Literal(DateTime.Now.Date), + SqlDml.RawConcat( + SqlDml.RawConcat(SqlDml.Native("INTERVAL "), + SqlDml.FunctionCall("TIME_TO_SEC", time)), + SqlDml.Native("SECOND"))); #endif protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) => diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs index 5fa5e73e70..8ce6b46262 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs @@ -44,6 +44,9 @@ public override void Visit(SqlFunctionCall node) Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3])); return; } + case SqlFunctionType.TimeToDateTime: + Visit(SqlDml.Cast(arguments[0], SqlType.DateTime)); + return; default: base.Visit(node); return; diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index 57e770b420..d81a64d47f 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -37,7 +37,10 @@ internal class Compiler : SqlCompiler protected const string SecondIntervalPart = "SECOND"; protected const string ToCharFunctionName = "TO_CHAR"; - private const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL"; + protected const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL"; + protected const string ToDSIntervalFunctionName = "TO_DSINTERVAL"; + protected const string TimeFormat = "HH24:MI:SS.FF6"; + private static readonly SqlExpression SundayNumber = SqlDml.Native( "TO_NUMBER(TO_CHAR(TIMESTAMP '2009-07-26 00:00:00.000', 'D'))"); private static readonly SqlNative RefTimestamp = SqlDml.Native("timestamp '2009-01-01 00:00:00.000000'"); @@ -129,9 +132,38 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeToDateTimeOffset: DateTimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); return; + case SqlFunctionType.DateTimeOffsetToDateTime: + DateTimeOffsetToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; case SqlFunctionType.DateTimeOffsetToUtcTime: DateTimeOffsetToUtcTime(node.Arguments[0]).AcceptVisitor(this); return; +#if NET6_0_OR_GREATER + case SqlFunctionType.DateTimeToDate: + DateTimeToDate(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTime: + DateToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToTime: + DateTimeToTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTime: + TimeToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToDate: + DateTimeOffsetToDate(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToTime: + DateTimeOffsetToTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTimeOffset: + DateToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTimeOffset: + TimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; +#endif default: base.Visit(node); return; @@ -358,9 +390,9 @@ private static SqlExpression TimeAddInterval(SqlExpression time, SqlExpression i { var baseOp = (negateInterval) ? RefTimestamp + time - intervalToAdd : RefTimestamp + time + intervalToAdd; - var getTimeOnly = SqlDml.FunctionCall(ToCharFunctionName, baseOp, AnsiString("HH24:MI:SS.FF6")); + var getTimeOnly = SqlDml.FunctionCall(ToCharFunctionName, baseOp, AnsiString(TimeFormat)); var pretendZeroDays = (SqlExpression) SqlDml.Concat(AnsiString("0 "), getTimeOnly); - var dsInterval = SqlDml.FunctionCall("TO_DSINTERVAL", pretendZeroDays); + var dsInterval = SqlDml.FunctionCall(ToDSIntervalFunctionName, pretendZeroDays); var castToCorrectInterval = SqlDml.Cast(dsInterval, SqlType.Time); return castToCorrectInterval; } @@ -449,9 +481,59 @@ private static SqlExpression DateTimeOffsetToLocalTime(SqlExpression dateTimeOff private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) => SqlDml.Cast(dateTime, SqlType.DateTimeOffset); + private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.DateTime); + private static SqlExpression DateTimeOffsetToUtcTime(SqlExpression dateTimeOffset) => SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" at time zone 'UTC'")); + private static SqlExpression DateToDateTimeOffset(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTimeOffset); + +#if NET6_0_OR_GREATER //DO_DATEONLY + private static SqlExpression DateTimeToDate(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Date); + + private static SqlExpression DateToDateTime(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTime); + + private static SqlExpression DateTimeToTime(SqlExpression dateTime) => + SqlDml.Cast(SqlDml.FunctionCall(ToDSIntervalFunctionName, + (SqlExpression) SqlDml.Concat( + AnsiString("0 "), + SqlDml.FunctionCall(ToCharFunctionName, + dateTime, + AnsiString(TimeFormat) + ) + ) + ), SqlType.Time); + + private static SqlExpression TimeToDateTime(SqlExpression time) => + SqlDml.Cast( + SqlDml.Literal(DateTime.MinValue) + time, + SqlType.DateTime); + + private static SqlExpression DateTimeOffsetToDate(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.Date); + + private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(SqlDml.FunctionCall(ToDSIntervalFunctionName, + (SqlExpression)SqlDml.Concat( + AnsiString("0 "), + SqlDml.FunctionCall(ToCharFunctionName, + dateTimeOffset, + AnsiString(TimeFormat) + ) + ) + ), SqlType.Time); + //SqlDml.Cast(dateTimeOffset - SqlDml.Cast(dateTimeOffset, SqlType.Date), SqlType.Time); + + private static SqlExpression TimeToDateTimeOffset(SqlExpression time) => + SqlDml.Cast( + SqlDml.Literal(DateTime.MinValue) + time, + SqlType.DateTimeOffset); +#endif + private static SqlExpression AnsiString(string value) => SqlDml.Native("'" + value + "'"); // Constructors diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs index 79b55b8400..daeb325e76 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs @@ -212,7 +212,8 @@ public override object ReadDouble(DbDataReader reader, int index) public override object ReadDateTimeOffset(DbDataReader reader, int index) { var nativeReader = (OracleDataReader) reader; - return new DateTimeOffset(nativeReader.GetOracleTimeStampTZ(index).Value, nativeReader.GetOracleTimeStampTZ(index).GetTimeZoneOffset()); + var timeStampTZ = nativeReader.GetOracleTimeStampTZ(index); + return new DateTimeOffset(timeStampTZ.Value, timeStampTZ.GetTimeZoneOffset()); } public override object ReadTimeSpan(DbDataReader reader, int index) diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs index 8d2b6965b6..55d5db7920 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs @@ -19,7 +19,7 @@ internal class Compiler : SqlCompiler private const string TimeFormat = "HH24:MI:SS.US0"; #endif - private readonly static Type SqlPlaceholderType = typeof(SqlPlaceholder); + private static readonly Type SqlPlaceholderType = typeof(SqlPlaceholder); private static readonly SqlNative OneYearInterval = SqlDml.Native("interval '1 year'"); private static readonly SqlNative OneMonthInterval = SqlDml.Native("interval '1 month'"); @@ -30,6 +30,7 @@ internal class Compiler : SqlCompiler private static readonly SqlNative OneSecondInterval = SqlDml.Native("interval '1 second'"); private static readonly SqlLiteral ReferenceDateTimeLiteral = SqlDml.Literal(new DateTime(2001, 1, 1)); + private static readonly SqlLiteral EpochLiteral = SqlDml.Literal(new DateTime(1970, 1, 1)); #if NET6_0_OR_GREATER //DO_DATEONLY private static readonly SqlLiteral ReferenceDateLiteral = SqlDml.Literal(new DateOnly(2001, 1, 1)); private static readonly SqlLiteral ZeroTimeLiteral = SqlDml.Literal(new TimeOnly(0, 0, 0)); @@ -193,8 +194,37 @@ public override void Visit(SqlFunctionCall node) ConstructDateTimeOffset(node.Arguments[0], node.Arguments[1]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeToDateTimeOffset: - SqlDml.Cast(node.Arguments[0], SqlType.DateTimeOffset).AcceptVisitor(this); + DateTimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); return; + case SqlFunctionType.DateTimeOffsetToDateTime: + DateTimeOffsetToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateTimeToDate: + DateTimeToDate(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTime: + DateToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToTime: + DateTimeToTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTime: + TimeToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToDate: + DateTimeOffsetToDate(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTimeOffset: + DateToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToTime: + DateTimeOffsetToTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTimeOffset: + TimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; +#endif } base.Visit(node); } @@ -424,6 +454,38 @@ protected SqlExpression GetOffsetAsStringExpression(SqlExpression offsetInMinute return IntervalToIsoString(intervalExpression, true); } + private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.DateTimeOffset); + + private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.DateTime); +#if NET6_0_OR_GREATER + + private static SqlExpression DateTimeToDate(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Date); + + private static SqlExpression DateToDateTime(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTime); + + private static SqlExpression DateTimeToTime(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Time); + + private static SqlExpression TimeToDateTime(SqlExpression time) => + SqlDml.Cast(EpochLiteral + time, SqlType.DateTime); + + private static SqlExpression DateTimeOffsetToDate(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.Date); + + private static SqlExpression DateToDateTimeOffset(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTimeOffset); + + private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.Time); + + private static SqlExpression TimeToDateTimeOffset(SqlExpression time) => + SqlDml.Cast(EpochLiteral + time, SqlType.DateTimeOffset); +#endif + private string ZoneStringFromParts(int hours, int minutes) => $"{(hours < 0 ? "-" : "+")}{Math.Abs(hours):00}:{Math.Abs(minutes):00}"; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index bc1780aea1..38d7569eac 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -245,6 +245,18 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.TimeToString: Visit(TimeToString(arguments[0])); return; + case SqlFunctionType.DateTimeToDate: + DateTimeToDate(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTime: + DateToDateTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToTime: + DateTimeToTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTime: + TimeToDateTime(arguments[0]).AcceptVisitor(this); + return; #endif case SqlFunctionType.DateTimeToStringIso: Visit(DateTimeToStringIso(arguments[0])); @@ -588,6 +600,18 @@ protected static SqlUserFunctionCall TimeToString(SqlExpression time) => protected static SqlUserFunctionCall DateToString(SqlExpression time) => SqlDml.FunctionCall("CONVERT", SqlDml.Native("NVARCHAR(10)"), time, SqlDml.Native("23")); + + protected static SqlExpression DateTimeToDate(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Date); + + protected static SqlExpression DateToDateTime(SqlExpression date) => + SqlDml.Cast(date, SqlType.DateTime); + + protected static SqlExpression DateTimeToTime(SqlExpression dateTime) => + SqlDml.Cast(dateTime, SqlType.Time); + + protected static SqlExpression TimeToDateTime(SqlExpression time) => + SqlDml.Cast(time, SqlType.DateTime); #endif protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) => diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs index 8b484688fa..e599171baa 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs @@ -132,6 +132,23 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeToDateTimeOffset: DateTimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); return; + case SqlFunctionType.DateTimeOffsetToDateTime: + DateTimeOffsetToDateTime(node.Arguments[0]).AcceptVisitor(this); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateToDateTimeOffset: + DateToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTimeOffset: + TimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToTime: + DateTimeOffsetToTime(node.Arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToDate: + DateTimeOffsetToDate(node.Arguments[0]).AcceptVisitor(this); + return; +#endif } base.Visit(node); @@ -200,6 +217,22 @@ private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) => SqlDml.Native(OffsetPart), SqlDml.Native("SYSDATETIMEOFFSET()"))); + private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.DateTime); + +#if NET6_0_OR_GREATER //DO_DATEONLY + private static SqlExpression DateToDateTimeOffset(SqlExpression date) => + DateTimeToDateTimeOffset(SqlDml.Cast(date, SqlType.DateTime)); + + private static SqlExpression TimeToDateTimeOffset(SqlExpression time) => + DateTimeToDateTimeOffset(SqlDml.Cast(time, SqlType.DateTime)); + + private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset)=> + SqlDml.Cast(dateTimeOffset, SqlType.Time); + + private static SqlExpression DateTimeOffsetToDate(SqlExpression dateTimeOffset) => + SqlDml.Cast(dateTimeOffset, SqlType.Date); +#endif #endregion // Constructors @@ -209,4 +242,6 @@ public Compiler(SqlDriver driver) { } } -} \ No newline at end of file +} + + diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index 8ebe39cc2d..e4307257df 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -165,7 +165,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.Round: // Round should always be called with 2 arguments if (node.Arguments.Count == 1) { - Visit(SqlDml.FunctionCall(translator.TranslateToString(SqlFunctionType.Round), node.Arguments[0], SqlDml.Literal(0))); + Visit(SqlDml.FunctionCall(translator.TranslateToString(SqlFunctionType.Round), arguments[0], SqlDml.Literal(0))); return; } break; @@ -207,7 +207,7 @@ public override void Visit(SqlFunctionCall node) DateAddDay(arguments[0], arguments[1]).AcceptVisitor(this); return; case SqlFunctionType.DateToString: - DateToString(node.Arguments[0]).AcceptVisitor(this); + DateToString(arguments[0]).AcceptVisitor(this); return; case SqlFunctionType.DateConstruct: DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), @@ -228,36 +228,71 @@ public override void Visit(SqlFunctionCall node) TimeAddMinutes(arguments[0], arguments[1]).AcceptVisitor(this); return; case SqlFunctionType.TimeToString: - TimeToString(node.Arguments[0]).AcceptVisitor(this); + TimeToString(arguments[0]).AcceptVisitor(this); return; #endif case SqlFunctionType.DateTimeToStringIso: - DateTimeToStringIso(node.Arguments[0]).AcceptVisitor(this); + DateTimeToStringIso(arguments[0]).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetAddMonths: - SqlDml.Concat(DateTimeAddMonth(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), node.Arguments[1]), DateTimeOffsetExtractOffsetAsString(node.Arguments[0])).AcceptVisitor(this); + SqlDml.Concat(DateTimeAddMonth( + DateTimeOffsetExtractDateTimeAsString(arguments[0]), arguments[1]), + DateTimeOffsetExtractOffsetAsString(arguments[0])) + .AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetAddYears: - SqlDml.Concat(DateTimeAddYear(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), node.Arguments[1]), DateTimeOffsetExtractOffsetAsString(node.Arguments[0])).AcceptVisitor(this); + SqlDml.Concat( + DateTimeAddYear(DateTimeOffsetExtractDateTimeAsString(arguments[0]), arguments[1]), + DateTimeOffsetExtractOffsetAsString(arguments[0])) + .AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetConstruct: - SqlDml.Concat(node.Arguments[0], OffsetToOffsetAsString(node.Arguments[1])).AcceptVisitor(this); + SqlDml.Concat(arguments[0], OffsetToOffsetAsString(arguments[1])).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetToLocalTime: SqlDml.Concat(DateTimeOffsetToLocalDateTime(node.Arguments[0]), ServerOffsetAsString()).AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetToUtcTime: - SqlDml.Concat(DateTimeOffsetToUtcDateTime(node.Arguments[0]), "+00:00").AcceptVisitor(this); + SqlDml.Concat(DateTimeOffsetToUtcDateTime(arguments[0]), "+00:00").AcceptVisitor(this); return; case SqlFunctionType.DateTimeOffsetTimeOfDay: SqlDml.DateTimeMinusDateTime( - DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]), - DateTimeTruncate(DateTimeOffsetExtractDateTimeAsString(node.Arguments[0]))) + DateTimeOffsetExtractDateTimeAsString(arguments[0]), + DateTimeTruncate(DateTimeOffsetExtractDateTimeAsString(arguments[0]))) .AcceptVisitor(this); return; case SqlFunctionType.DateTimeToDateTimeOffset: - SqlDml.Concat(DateTime(node.Arguments[0]), ServerOffsetAsString()).AcceptVisitor(this); + DateTimeToDateTimeOffset(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToDateTime: + DateTimeOffsetToDateTime(arguments[0]).AcceptVisitor(this); + return; +#if NET6_0_OR_GREATER //DO_DATEONLY + case SqlFunctionType.DateTimeToDate: + DateTimeToDate(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateToDateTime: + DateToDateTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeToTime: + DateTimeToTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTime: + TimeToDateTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToTime: + DateTimeOffsetToTime(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.DateTimeOffsetToDate: + DateTimeOffsetToDate(arguments[0]).AcceptVisitor(this); + return; + case SqlFunctionType.TimeToDateTimeOffset: + TimeToDateTimeOffset(arguments[0]).AcceptVisitor(this); return; + case SqlFunctionType.DateToDateTimeOffset: + DateToDateTimeOffset(arguments[0]).AcceptVisitor(this); + return; +#endif } base.Visit(node); } @@ -472,6 +507,13 @@ private static SqlExpression DateTimeOffsetToUtcDateTime(SqlExpression dateTimeO private static SqlExpression DateTimeOffsetToLocalDateTime(SqlExpression dateTimeOffset) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, dateTimeOffset, "LOCALTIME"); + private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) => + SqlDml.Concat(DateTime(dateTime), ServerOffsetAsString()); + + private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) => + SqlDml.Cast(SqlDml.Extract(SqlDateTimeOffsetPart.DateTime, dateTimeOffset), SqlType.DateTime); + //SqlDml.Concat(DateTime(node.Arguments[0]), ServerOffsetAsString()); + private static SqlExpression DateTimeToStringIso(SqlExpression dateTime) => SqlDml.FunctionCall("STRFTIME", DateTimeIsoFormat, dateTime); @@ -533,6 +575,29 @@ private static SqlExpression DateToString(SqlExpression dateTime) => private static SqlExpression TimeToString(SqlExpression dateTime) => SqlDml.FunctionCall("STRFTIME", TimeToStringFormat, dateTime); + + private static SqlExpression DateTimeToDate(SqlExpression dateTime) => + SqlDml.FunctionCall("STRFTIME", DateFormat, dateTime); + + private static SqlExpression DateToDateTime(SqlExpression date) => + SqlDml.FunctionCall("STRFTIME", DateTimeFormat, SqlDml.Concat(date, " 00:00:00")); + + private static SqlExpression DateTimeToTime(SqlExpression dateTime) => + SqlDml.FunctionCall("STRFTIME", TimeFormat, dateTime); + private static SqlExpression TimeToDateTime(SqlExpression time) => + SqlDml.FunctionCall("STRFTIME", "1900-01-01 " + TimeFormat, time); + + private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset) => + DateTimeToTime(SqlDml.FunctionCall("STRFTIME", TimeFormat, DateTimeOffsetExtractDateTimeAsString(dateTimeOffset))); + + private static SqlExpression DateTimeOffsetToDate(SqlExpression dateTimeOffset) => + DateTimeToDate(SqlDml.FunctionCall("STRFTIME", DateFormat, DateTimeOffsetExtractDateTimeAsString(dateTimeOffset))); + + private static SqlExpression TimeToDateTimeOffset(SqlExpression time) => + SqlDml.Concat(SqlDml.FunctionCall("STRFTIME", DateTimeFormat, SqlDml.Concat("1900-01-01 ", time)), ServerOffsetAsString()); + + private static SqlExpression DateToDateTimeOffset(SqlExpression date) => + SqlDml.Concat(SqlDml.FunctionCall("STRFTIME", DateTimeFormat, SqlDml.Concat(date, " 00:00:00")), ServerOffsetAsString()); #endif private static SqlExpression DateOrTimeGetMilliseconds(SqlExpression date) => From a4b31f3d90aace772a99a636670e9e991509a8e3 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 22 Feb 2023 16:15:45 +0500 Subject: [PATCH 62/86] Fix DateTypeCollection.Add copy of fix in master --- Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs index 210b44e1bf..d9131da167 100644 --- a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs @@ -55,8 +55,10 @@ public DataTypeInfo this[SqlType sqlType] public void Add(SqlType sqlType, DataTypeInfo dataTypeInfo) { EnsureNotLocked(); - if (!IsLocked) - sqlTypes.Add(sqlType, dataTypeInfo); + sqlTypes.Add(sqlType, dataTypeInfo); + foreach(var nativeType in dataTypeInfo.NativeTypes) { + nativeTypes.Add(nativeType, dataTypeInfo); + } } /// @@ -144,8 +146,8 @@ public void Add(SqlType sqlType, DataTypeInfo dataTypeInfo) #if NET6_0_OR_GREATER //DO_DATEONLY /// - /// Date data from January 1,1 A.D. through December 31, 9999 A.D., - /// Storage size is 8 to 10 bytes. + /// Date data from January 1,1 A.D. through December 31, 9999 A.D. + /// Can have various ranges in different RDBMSs. /// public DataTypeInfo DateOnly { get; set; } From c296cca4aed62a7e34fa9cc815e6ea9f33ce1a6b Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 22 Feb 2023 16:37:48 +0500 Subject: [PATCH 63/86] Removed unused code --- Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index 4fae7afc2c..f9787818c4 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -61,15 +61,6 @@ protected override void TestFixtureTearDown() } base.TestFixtureTearDown(); - - string BuildCleanUpScrcipt() - { - var sb = new StringBuilder(cleanups.Select(s => s.Length).Sum()); - foreach (var s in cleanups) { - _ = sb.AppendLine(s); - } - return sb.ToString(); - } } [Test] @@ -78,11 +69,11 @@ public void SchemaExtractionTest() Assert.That(ExtractDefaultSchema(), Is.Not.Null); var catalog = ExtractCatalog(); - if (StorageProviderInfo.Instance.CheckAllFeaturesNotSupported(Providers.ProviderFeatures.Multischema)) + if (StorageProviderInfo.Instance.CheckAllFeaturesNotSupported(Providers.ProviderFeatures.Multischema)) { Assert.That(schemasToCheck.All(s => catalog.Schemas[s] != null), Is.True); + } } - protected abstract string GetTypesExtractionPrepareScript(string tableName); protected abstract string GetTypesExtractionCleanUpScript(string tableName); From ffd0e0dbb7c5f9910b61a6535f9e025f494723ff Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 25 Feb 2023 14:28:34 +0500 Subject: [PATCH 64/86] Ignore Sqlite in certain tests --- .../Upgrade/DateTimeDataConversions.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs b/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs index 09d9ccaaf5..e4abdaf027 100644 --- a/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs +++ b/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs @@ -173,21 +173,14 @@ public void DateOnlyToDateTimeOffsetTest() [Test] public void DateOnlyToTimeOnlyTest() { + Require.ProviderIsNot(StorageProvider.Sqlite, "No any error from storage on conversion"); + var provider = StorageProviderInfo.Instance.Provider; if (provider == StorageProvider.PostgreSql) { UpgradeAndExpectException(); } - else if (provider == StorageProvider.Oracle) { - UpgradeAndExpectException(); - } - else if (provider == StorageProvider.MySql) { - UpgradeAndExpectException(); - } - else if (provider == StorageProvider.Firebird) { - UpgradeAndExpectException(); - } - else if (provider == StorageProvider.SqlServer) { + else { UpgradeAndExpectException(); } } @@ -240,7 +233,11 @@ public void TimeOnlyToDateTimeOffsetTest() [Test] public void TimeOnlyToDateOnlyTest() { - if (StorageProviderInfo.Instance.Provider == StorageProvider.PostgreSql) { + Require.ProviderIsNot(StorageProvider.Sqlite, "No any error from storage on conversion"); + + var provider = StorageProviderInfo.Instance.Provider; + + if (provider == StorageProvider.PostgreSql) { UpgradeAndExpectException(); } else { From 5387951c0e4ea709d05aa7c69126528fcb1ef88b Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 25 Feb 2023 15:02:27 +0500 Subject: [PATCH 65/86] Sqlite: time resolution increased --- Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs | 2 +- Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs | 2 +- Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index e4307257df..9f6adf4f1f 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -20,7 +20,7 @@ internal class Compiler : SqlCompiler private const string DateWithZeroTimeFormat = "%Y-%m-%d 00:00:00.000"; #if NET6_0_OR_GREATER //DO_DATEONLY private const string DateFormat = "%Y-%m-%d"; - private const string TimeFormat = "%H:%M:%f"; + private const string TimeFormat = "%H:%M:%f0000"; private const string TimeToStringFormat = "%H:%M:%f0000"; #endif private const string DateTimeFormat = "%Y-%m-%d %H:%M:%f"; diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs index 3b630b0270..bd609f31c4 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs @@ -28,7 +28,7 @@ internal class Translator : SqlTranslator public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'"; /// - public override string TimeOnlyFormatString => @"\'HH\:mm\:ss.fff\'"; + public override string TimeOnlyFormatString => @"\'HH\:mm\:ss.fffffff\'"; #endif public virtual string DateTimeOffsetFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss.fffK\'"; diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs index 5bbe812276..0a491fa8dd 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs @@ -26,7 +26,7 @@ internal class TypeMapper : Sql.TypeMapper private const string DateTimeOffsetFormat = "yyyy-MM-dd HH:mm:ss.fffK"; private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fffffff"; private const string DateFormat = "yyyy-MM-dd"; - private const string TimeFormat = "HH:mm:ss.fff"; + private const string TimeFormat = "HH:mm:ss.fffffff"; public override object ReadBoolean(DbDataReader reader, int index) { From f76b4c7daca9b2eba7b94df44faae64b5e38edbf Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Sat, 25 Feb 2023 17:24:59 +0500 Subject: [PATCH 66/86] Remove TimeOnly/DateOnly reading from test The test is not suppose to be about DateOnly/TimeOnly Such tests will be created where other types are checked --- .../SimpleEntityManipulationTest.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs index ab320fd583..7b07cf93c7 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/SimpleEntityManipulationTest.cs @@ -10,7 +10,6 @@ using NUnit.Framework; using Xtensive.Core; using Xtensive.Orm.Configuration; -using Xtensive.Orm.Tests.Model.UselessTypeInTheMiddleTestModel; using model = Xtensive.Orm.Tests.Storage.SchemaSharing.EntityManipulation.Model; namespace Xtensive.Orm.Tests.Storage.SchemaSharing.EntityManipulation @@ -405,23 +404,11 @@ private void Update(Session session, Key[] createdKeys, int initialCountOfEntiti Assert.That(a.DatabaseName, Is.EqualTo(databaseAndSchema.First)); Assert.That(a.SchemaName, Is.EqualTo(databaseAndSchema.Second)); - var now = DateTime.UtcNow; - a.Text = updatedText; -#if NET6_0_OR_GREATER //DO_DATEONLY - var dateOnly = DateOnly.FromDateTime(now); - var timeOnly = TimeOnly.FromDateTime(now); - a.DateOnly = dateOnly; - a.TimeOnly = timeOnly; -#endif session.SaveChanges(); Assert.That(session.Query.All().Count(), Is.EqualTo(initialCountOfEntities + 1)); Assert.That(session.Query.All().FirstOrDefault(e => e.Text == updatedText), Is.Not.Null); -#if NET6_0_OR_GREATER //DO_DATEONLY - Assert.AreEqual(session.Query.All().FirstOrDefault(e => e.Text == updatedText).DateOnly, dateOnly); - Assert.AreEqual(session.Query.All().FirstOrDefault(e => e.Text == updatedText).TimeOnly, timeOnly); -#endif Assert.That(session.Query.All().FirstOrDefault(e => e.Text == text), Is.Null); Assert.That( session.Query.All() From 6a6ff055ff02817e68e0ee1306657b5d2e012dcf Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Sun, 26 Feb 2023 18:26:20 -0800 Subject: [PATCH 67/86] Upgrade to SqlClient 5.1.0 with DateOnly/TimeOnly support --- Extensions/TestCommon/TestCommon.csproj | 2 +- .../Xtensive.Orm.Reprocessing.csproj | 2 +- .../Xtensive.Orm.Security/Xtensive.Orm.Security.csproj | 2 +- .../Xtensive.Orm.Tracking/Xtensive.Orm.Tracking.csproj | 2 +- Extensions/Xtensive.Orm.Web/Xtensive.Orm.Web.csproj | 2 +- Orm/Xtensive.Orm.SqlServer/Xtensive.Orm.SqlServer.csproj | 2 +- Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj | 2 +- Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs | 8 ++++---- Orm/Xtensive.Orm/Xtensive.Orm.csproj | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Extensions/TestCommon/TestCommon.csproj b/Extensions/TestCommon/TestCommon.csproj index f19fa46dd6..e23f0c4aef 100644 --- a/Extensions/TestCommon/TestCommon.csproj +++ b/Extensions/TestCommon/TestCommon.csproj @@ -12,7 +12,7 @@ - + diff --git a/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj b/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj index ab1e779974..34ee5d125c 100644 --- a/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj +++ b/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj @@ -16,7 +16,7 @@ - + diff --git a/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj b/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj index 686f6bf4ac..8643bbdc08 100644 --- a/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj +++ b/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj @@ -17,7 +17,7 @@ - + diff --git a/Extensions/Xtensive.Orm.Tracking/Xtensive.Orm.Tracking.csproj b/Extensions/Xtensive.Orm.Tracking/Xtensive.Orm.Tracking.csproj index b69fff4349..3c18a0e846 100644 --- a/Extensions/Xtensive.Orm.Tracking/Xtensive.Orm.Tracking.csproj +++ b/Extensions/Xtensive.Orm.Tracking/Xtensive.Orm.Tracking.csproj @@ -16,7 +16,7 @@ - + diff --git a/Extensions/Xtensive.Orm.Web/Xtensive.Orm.Web.csproj b/Extensions/Xtensive.Orm.Web/Xtensive.Orm.Web.csproj index d4d5c4299e..a919cdb476 100644 --- a/Extensions/Xtensive.Orm.Web/Xtensive.Orm.Web.csproj +++ b/Extensions/Xtensive.Orm.Web/Xtensive.Orm.Web.csproj @@ -19,7 +19,7 @@ - + diff --git a/Orm/Xtensive.Orm.SqlServer/Xtensive.Orm.SqlServer.csproj b/Orm/Xtensive.Orm.SqlServer/Xtensive.Orm.SqlServer.csproj index cebd795bca..c9b18b906f 100644 --- a/Orm/Xtensive.Orm.SqlServer/Xtensive.Orm.SqlServer.csproj +++ b/Orm/Xtensive.Orm.SqlServer/Xtensive.Orm.SqlServer.csproj @@ -16,7 +16,7 @@ 2 - + diff --git a/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj b/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj index 2631363597..ed55956c3b 100644 --- a/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj +++ b/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs index 347e8facc4..46fe180517 100644 --- a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs +++ b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs @@ -136,13 +136,13 @@ public virtual void BindDateTime(DbParameter parameter, object value) public virtual void BindDateOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Date; - parameter.Value = value != null ? ((DateOnly) value).ToDateTime(TimeOnly.MinValue) : DBNull.Value; + parameter.Value = value != null ? (DateOnly) value : DBNull.Value; } public virtual void BindTimeOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Time; - parameter.Value = value != null ? ((TimeOnly) value).ToTimeSpan() : DBNull.Value; + parameter.Value = value != null ? (TimeOnly) value : DBNull.Value; } #endif @@ -226,10 +226,10 @@ public virtual object ReadDateTime(DbDataReader reader, int index) => #if NET6_0_OR_GREATER //DO_DATEONLY public virtual object ReadDateOnly(DbDataReader reader, int index) => - DateOnly.FromDateTime(reader.GetFieldValue(index)); + reader.GetFieldValue(index); public virtual object ReadTimeOnly(DbDataReader reader, int index) => - TimeOnly.FromTimeSpan(reader.GetFieldValue(index)); + reader.GetFieldValue(index); #endif public virtual object ReadDateTimeOffset(DbDataReader reader, int index) => diff --git a/Orm/Xtensive.Orm/Xtensive.Orm.csproj b/Orm/Xtensive.Orm/Xtensive.Orm.csproj index e71484bead..ce9d4f62f6 100644 --- a/Orm/Xtensive.Orm/Xtensive.Orm.csproj +++ b/Orm/Xtensive.Orm/Xtensive.Orm.csproj @@ -50,7 +50,7 @@ - + From 15e1d947c76912b0cdb58101d1af445a005102a6 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 10:58:54 +0500 Subject: [PATCH 68/86] TestHelper: DateTime precision workarouds for Oracle/Mysql --- Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs index 220d402fb2..d80501172a 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs @@ -70,6 +70,9 @@ public static DateTime FixDateTimeForProvider(this DateTime origin, StorageProvi case StorageProvider.PostgreSql: divider = 10; break; + case StorageProvider.Oracle: + divider = 10; + break; default: divider = null; break; @@ -79,6 +82,17 @@ public static DateTime FixDateTimeForProvider(this DateTime origin, StorageProvi return origin; } var ticks = origin.Ticks; + if (provider == StorageProvider.Oracle) { + var ticks1 = (ticks % divider) >=5 ? ticks - (ticks % divider.Value) + divider.Value + : ticks - (ticks % divider.Value); + return new DateTime(ticks1); + } + if (provider == StorageProvider.MySql) { + var abc = ((ticks % 10000000) / 1000000); + var ticks1 = ((ticks % 10000000) / 1000000) >= 5 ? ticks - (ticks % 10000000) + 10000000 + : ticks - (ticks % 10000000); + return new DateTime(ticks1); + } var newTicks = ticks - (ticks % divider.Value); return new DateTime(newTicks); } From 2a3f91d4581ab61f578d61e9a77914650a4fc690 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 10:59:36 +0500 Subject: [PATCH 69/86] Update StorageProviderVersion with new items --- Orm/Xtensive.Orm.Tests.Framework/StorageProviderVersion.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Orm/Xtensive.Orm.Tests.Framework/StorageProviderVersion.cs b/Orm/Xtensive.Orm.Tests.Framework/StorageProviderVersion.cs index 2d5202ad86..c45858ca46 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/StorageProviderVersion.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/StorageProviderVersion.cs @@ -33,8 +33,11 @@ public static class StorageProviderVersion public static Version PostgreSql92 = new Version(9, 2); public static Version PostgreSql100 = new Version(10, 0); public static Version PostgreSql110 = new Version(11, 0); + public static Version PostgreSql120 = new Version(12, 0); public static Version MySql55 = new Version(5, 5); public static Version MySql56 = new Version(5, 6); + public static Version MySql57 = new Version(5, 7); + public static Version Mysql80 = new Version(8, 0); } } \ No newline at end of file From 312f8d093c94c36b9e5354a261f6a07787f00f0a Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 11:58:44 +0500 Subject: [PATCH 70/86] Test for DateTime and TimeOnly values with microseconds --- .../Storage/DateTimeStoragePrecisionTest.cs | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs diff --git a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs new file mode 100644 index 0000000000..926a7e35a3 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs @@ -0,0 +1,97 @@ +// Copyright (C) 2023 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Linq; +using NUnit.Framework; +using Xtensive.Orm.Configuration; +using Xtensive.Orm.Tests.Storage.DateTimeStoragePrecisionTestModel; + +namespace Xtensive.Orm.Tests.Storage.DateTimeStoragePrecisionTestModel +{ + [HierarchyRoot] + [KeyGenerator(KeyGeneratorKind.None)] + public class TestEntity : Entity + { + [Field, Key] + public long Id { get; private set; } + + [Field] + public DateTime FDateTime { get; set; } +#if NET6_0_OR_GREATER //DO_DATEONLY + + [Field] + public TimeOnly FTimeOnly { get; set; } +#endif + + public TestEntity(Session session, long idValue) + : base(session, idValue) + { + var dateTime = new DateTime(idValue, DateTimeKind.Utc); + FDateTime = dateTime; +#if NET6_0_OR_GREATER + FTimeOnly = TimeOnly.FromDateTime(dateTime); +#endif + } + } +} + +namespace Xtensive.Orm.Tests.Storage +{ + // Each storage is capable of storing certain amount of fractions of second. + // This test writes a value with 7 fractional points to storage and, + // depeding on storage, expects value of precision the storage have. + public class DateTimeStoragePrecisionTest : AutoBuildTest + { + private const long TestDateTimeTicks = 638130658792224229L; //2023-02-27 03:37:59.2224229 UTC + + protected override DomainConfiguration BuildConfiguration() + { + var configuration = DomainConfigurationFactory.Create(); + configuration.UpgradeMode = DomainUpgradeMode.Recreate; + configuration.Types.Register(typeof(TestEntity)); + return configuration; + } + + protected override void PopulateData() + { + using (var session = Domain.OpenSession()) + using (var tx = session.OpenTransaction()) { + _ = new TestEntity(session, TestDateTimeTicks); + tx.Complete(); + } + } + + [Test] + public void DateTimeTest() + { + var dateTime = new DateTime(TestDateTimeTicks, DateTimeKind.Utc); + + using (var session = Domain.OpenSession()) + using (var tx = session.OpenTransaction()) { + var entity = session.Query.All().First(e => e.Id == TestDateTimeTicks); + Assert.That(entity.FDateTime, Is.EqualTo(GetExpectedValue(dateTime))); + } + } + +#if NET6_0_OR_GREATER //DO_DATEONLY + [Test] + public void TimeOnlyTest() + { + var dateTime = new DateTime(TestDateTimeTicks, DateTimeKind.Utc); + var timeOnly = TimeOnly.FromDateTime(dateTime); + + using (var session = Domain.OpenSession()) + using (var tx = session.OpenTransaction()) { + var entity = session.Query.All().First(e => e.Id == TestDateTimeTicks); + Assert.That(entity.FTimeOnly, Is.EqualTo(GetExpectedValue(timeOnly))); + } + } + + private static TimeOnly GetExpectedValue(in TimeOnly baseTimeOnly) => baseTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + +#endif + private static DateTime GetExpectedValue(in DateTime baseDateTime) => baseDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); + } +} \ No newline at end of file From 376d78a5fbdf5fa710ff0dfd567cc0d548b56093 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 12:06:36 +0500 Subject: [PATCH 71/86] Tests for default values and type compatibility --- .../Storage/FieldDefaultValueTest.cs | 28 ++++++++++++- .../Storage/TypeCompatibilityTest.cs | 41 ++++++++++++++++--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs b/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs index 233621bde6..2f906b3cac 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2021 Xtensive LLC. +// Copyright (C) 2008-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Dmitri Maximov @@ -146,6 +146,14 @@ public class X : Entity [Field(DefaultValue = "2012.12.12")] public DateTime FDateTime { get; set; } +#if NET6_0_OR_GREATER //DO_DATEONLY + [Field(DefaultValue = "2012.12.12")] + public DateOnly FDateOnly { get; set; } + + [Field(DefaultValue = "00:35:53.35")] + public TimeOnly FTimeOnly { get; set; } + +#endif [Field(DefaultValue = 1000)] public TimeSpan FTimeSpan { get; set; } @@ -223,6 +231,14 @@ public class X : Entity [Field(DefaultValue = "2012.12.12")] public DateTime? FNDateTime { get; set; } +#if NET6_0_OR_GREATER //DO_DATEONLY + [Field(DefaultValue = "2012.12.12")] + public DateOnly? FNDateOnly { get; set; } + + [Field(DefaultValue = "00:35:53.35")] + public TimeOnly? FNTimeOnly { get; set; } + +#endif [Field(DefaultValue = 1000)] public TimeSpan? FNTimeSpan { get; set; } @@ -331,6 +347,12 @@ public void DefaultValuesTest() Assert.AreEqual(true, x.FBool); Assert.AreEqual(byte.MaxValue, x.FByte); Assert.AreEqual(DateTime.Parse("2012.12.12"), x.FDateTime); + +#if NET6_0_OR_GREATER //DO_DATEONLY + Assert.AreEqual(DateOnly.Parse("2012.12.12"), x.FDateOnly); + Assert.AreEqual(TimeOnly.Parse("00:35:53.35"), x.FTimeOnly); +#endif + Assert.AreEqual(12.12M, x.FDecimal); Assert.AreEqual(float.MaxValue, x.FDouble); Assert.AreEqual(EByte.Max, x.FEByte); @@ -357,6 +379,10 @@ public void DefaultValuesTest() Assert.AreEqual(true, x.FNBool); Assert.AreEqual(byte.MaxValue, x.FNByte); Assert.AreEqual(DateTime.Parse("2012.12.12"), x.FNDateTime); +#if NET6_0_OR_GREATER //DO_DATEONLY + Assert.AreEqual(DateOnly.Parse("2012.12.12"), x.FNDateOnly); + Assert.AreEqual(TimeOnly.Parse("00:35:53.35"), x.FNTimeOnly); +#endif Assert.AreEqual(12.12M, x.FNDecimal); Assert.AreEqual(float.MaxValue, x.FNDouble); Assert.AreEqual(EByte.Max, x.FNEByte); diff --git a/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs b/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs index 27fba9a5bb..9146a690b8 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2021 Xtensive LLC. +// Copyright (C) 2008-2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Dmitri Maximov @@ -113,6 +113,14 @@ public class X : Entity [Field] public DateTime FDateTime { get; set; } +#if NET6_0_OR_GREATER //DO_DATEONLY + [Field] + public DateOnly FDateOnly { get; set; } + + [Field] + public TimeOnly FTimeOnly { get; set; } + +#endif [Field] public TimeSpan FTimeSpan { get; set; } @@ -198,6 +206,14 @@ public class X : Entity [Field] public DateTime? FNDateTime { get; set; } +#if NET6_0_OR_GREATER //DO_DATEONLY + [Field] + public DateOnly? FNDateOnly { get; set; } + + [Field] + public TimeOnly? FNTimeOnly { get; set; } + +#endif [Field] public TimeSpan? FNTimeSpan { get; set; } @@ -274,17 +290,28 @@ public void DefaultValuesTest() t.Complete(); } - var field = typeof (StorageDriver).GetField("underlyingDriver", BindingFlags.Instance | BindingFlags.NonPublic); - var sqlDriver = (SqlDriver) field.GetValue(Domain.Handlers.StorageDriver); + var sqlDriver = TestSqlDriver.Create(session.Domain.Configuration.ConnectionInfo); var dataTypeInfo = sqlDriver.ServerInfo.DataTypes.DateTime; - var minValue = ((ValueRange) dataTypeInfo.ValueRange).MinValue; + var dateTimeMinValue = ((ValueRange) dataTypeInfo.ValueRange).MinValue; + +#if NET6_0_OR_GREATER //DO_DATEONLY + dataTypeInfo = sqlDriver.ServerInfo.DataTypes.DateOnly; + var dateOnlyMinValue = ((ValueRange) dataTypeInfo.ValueRange).MinValue; + + dataTypeInfo = sqlDriver.ServerInfo.DataTypes.TimeOnly; + var timeOnlyMinValue = ((ValueRange) dataTypeInfo.ValueRange).MinValue; +#endif using (var t = session.OpenTransaction()) { X x = session.Query.SingleOrDefault(key); Assert.AreEqual(false, x.FBool); Assert.AreEqual(0, x.FByte); Assert.AreEqual(null, x.FByteArray); - Assert.AreEqual(minValue, x.FDateTime); + Assert.AreEqual(dateTimeMinValue, x.FDateTime); +#if NET6_0_OR_GREATER //DO_DATEONLY + Assert.AreEqual(dateOnlyMinValue, x.FDateOnly); + Assert.AreEqual(timeOnlyMinValue, x.FTimeOnly); +#endif Assert.AreEqual(0, x.FDecimal); Assert.AreEqual(0, x.FDouble); Assert.AreEqual(EByte.Default, x.FEByte); @@ -312,6 +339,10 @@ public void DefaultValuesTest() Assert.AreEqual(null, x.FNBool); Assert.AreEqual(null, x.FNByte); Assert.AreEqual(null, x.FNDateTime); +#if NET6_0_OR_GREATER //DO_DATEONLY + Assert.AreEqual(null, x.FNDateOnly); + Assert.AreEqual(null, x.FNTimeOnly); +#endif Assert.AreEqual(null, x.FNDecimal); Assert.AreEqual(null, x.FNDouble); Assert.AreEqual(null, x.FNEByte); From fd88cce22f0b1ad8f926118eb745822c1be5053f Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 13:06:00 +0500 Subject: [PATCH 72/86] Field default values of DateOnly/TimeOnly support --- .../Orm/Building/Builders/ValueTypeBuilder.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs index 21f55d1cad..b8b232fc83 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.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: Denis Krjuchkov @@ -58,6 +58,22 @@ private static object AdjustValue(string fieldName, Type fieldType, object value timespan = TimeSpan.FromTicks(ticks); return timespan; } +#if NET6_0_OR_GREATER //DO_DATEONLY + + if (valueType == WellKnownTypes.TimeOnly) { + if (value is string timeOnlyString && !TimeOnly.TryParse(timeOnlyString, out var timeOnly)) { + throw FailToParseValue(fieldName, timeOnlyString); + } + return timeOnly; + } + + if (valueType == WellKnownTypes.DateOnly) { + if (value is string dateOnlyString && !DateOnly.TryParse(dateOnlyString, out var dateOnly)) { + throw FailToParseValue(fieldName, dateOnlyString); + } + return dateOnly; + } +#endif return Convert.ChangeType(value, valueType); } From 9f8453e61115c335a47224d2bd4e5ac2c73395cf Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 13:15:21 +0500 Subject: [PATCH 73/86] Oracle: Fix wrong interval usage --- Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index d81a64d47f..6dfa18c293 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -38,6 +38,8 @@ internal class Compiler : SqlCompiler protected const string ToCharFunctionName = "TO_CHAR"; protected const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL"; + protected const string NumToYMIntervalFunctionName = "NUMTOYMINTERVAL"; + protected const string ToDSIntervalFunctionName = "TO_DSINTERVAL"; protected const string TimeFormat = "HH24:MI:SS.FF6"; @@ -357,7 +359,7 @@ public override void Visit(SqlBinary node) } private static SqlExpression DateTimeAddYMInterval(SqlExpression dateTime, SqlExpression units, in string component) => - dateTime + SqlDml.FunctionCall(NumToDSIntervalFunctionName, units, AnsiString(component)); + dateTime + SqlDml.FunctionCall(NumToYMIntervalFunctionName, units, AnsiString(component)); private static SqlExpression DateTimeAddDSInterval(SqlExpression dateTime, SqlExpression units, in string component) => dateTime + SqlDml.FunctionCall(NumToDSIntervalFunctionName, units, AnsiString(component)); From 059d798561eca9455f4f921625db3d3e24e63e43 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 13:19:26 +0500 Subject: [PATCH 74/86] Increased precision of time for Firebird and Oracle --- Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs | 2 +- Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs | 4 ++-- .../Sql.Drivers.Oracle/v09/ServerInfoProvider.cs | 2 +- Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs | 4 ++-- Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v11/Translator.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs index e8e976c3c1..e5d30d15d7 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs @@ -16,7 +16,7 @@ internal static class Constants public const string DateTimeFormatString = @"''\'yyyy\.MM\.dd HH\:mm\:ss\.fff\'''"; #if NET6_0_OR_GREATER //DO_DATEONLY public const string DateFormatString = @"''\'yyyy\.MM\.dd\'''"; - public const string TimeFormatString = @"''\'HH\:mm\:ss\.fff\'''"; + public const string TimeFormatString = @"''\'HH\:mm\:ss\.ffff\'''"; #endif } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index 6dfa18c293..1138091969 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -41,11 +41,11 @@ internal class Compiler : SqlCompiler protected const string NumToYMIntervalFunctionName = "NUMTOYMINTERVAL"; protected const string ToDSIntervalFunctionName = "TO_DSINTERVAL"; - protected const string TimeFormat = "HH24:MI:SS.FF6"; + protected const string TimeFormat = "HH24:MI:SS.FF7"; private static readonly SqlExpression SundayNumber = SqlDml.Native( "TO_NUMBER(TO_CHAR(TIMESTAMP '2009-07-26 00:00:00.000', 'D'))"); - private static readonly SqlNative RefTimestamp = SqlDml.Native("timestamp '2009-01-01 00:00:00.000000'"); + private static readonly SqlNative RefTimestamp = SqlDml.Native("timestamp '2009-01-01 00:00:00.0000000'"); public override void Visit(SqlFunctionCall node) { diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs index cdb9d403c9..5eab3ba4a4 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs @@ -232,7 +232,7 @@ public override DataTypeCollection GetDataTypesInfo() types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, ValueRange.DateOnly, "DATE"); types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, - ValueRange.TimeOnly, "interval day(0) to second(6)"); + ValueRange.TimeOnly, "interval day(0) to second(7)"); #endif types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index, ValueRange.DateTimeOffset, "TIMESTAMP WITH TIME ZONE"); diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index b88fbf7dc7..d3ffad5353 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -35,7 +35,7 @@ internal class Translator : SqlTranslator public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)"; /// - public override string TimeOnlyFormatString => @"'(INTERVAL '\'0 HH\:mm\:ss\.ffffff\'\ DAY(0) TO SECOND(6))"; + public override string TimeOnlyFormatString => @"'(INTERVAL '\'0 HH\:mm\:ss\.fffffff\'\ DAY(0) TO SECOND(7))"; #endif /// @@ -340,7 +340,7 @@ public override string Translate(SqlValueType type) return type.Type == SqlType.Interval ? "INTERVAL DAY(6) TO SECOND(3)" : type.Type == SqlType.Time - ? "INTERVAL DAY(0) TO SECOND(6)" + ? "INTERVAL DAY(0) TO SECOND(7)" : base.Translate(type); #else // we need to explicitly specify maximum interval precision diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v11/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v11/Translator.cs index e9e61349eb..67c9a80be7 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v11/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v11/Translator.cs @@ -26,7 +26,7 @@ public override string Translate(SqlValueType type) { // we need to explicitly specify maximum interval precision return type.Type == SqlType.Interval - ? "INTERVAL DAY(6) TO SECOND(6)" + ? "INTERVAL DAY(6) TO SECOND(7)" : base.Translate(type); } From c1c1d14b32b9494093f545b7113bd1f584da74b3 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 27 Feb 2023 13:22:49 +0500 Subject: [PATCH 75/86] Mysql: Fix wrong type of value range for DateOnly --- .../Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs index dd71caeba6..da09c28bd2 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs @@ -266,7 +266,7 @@ public override DataTypeCollection GetDataTypesInfo() new ValueRange(new DateTime(1000, 1, 1), new DateTime(9999, 12, 31)), "datetime"); types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, - new ValueRange(new DateTime(1000, 1, 1), new DateTime(9999, 12, 31)), + new ValueRange(new DateOnly(1000, 1, 1), new DateOnly(9999, 12, 31)), "date"); types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, ValueRange.TimeOnly, "time"); #else From fa3651af528f3bd500913be5e214f555a94d8344 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 28 Feb 2023 13:54:30 +0500 Subject: [PATCH 76/86] Mysql: Change time parameters usage in queries --- Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs index 3c93e3f076..de24cd7313 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs @@ -256,9 +256,8 @@ public override void Visit(SqlFunctionCall node) #if NET6_0_OR_GREATER //DO_DATEONLY public override void Visit(SqlPlaceholder node) { - if (node.Id is Orm.Providers.QueryParameterBinding qpb - && qpb.BindingType == Orm.Providers.QueryParameterBindingType.Regular - && qpb.TypeMapping.Type == typeof(TimeOnly)) { + if (node.Id is Orm.Providers.ParameterBinding qpb + && qpb.TypeMapping?.Type == typeof(TimeOnly)) { _ = context.Output.Append("TIME("); base.Visit(node); _ = context.Output.Append(")"); From faf05fa22ed2bc3f554d834a79b7c5f0d08bbfb3 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 28 Feb 2023 19:52:37 +0500 Subject: [PATCH 77/86] Fixed some sql tests for PostgreSQL --- .../PostgreSql/ExtractorTest.cs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs index e662f81b24..227fcca185 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs @@ -1,24 +1,20 @@ -// Copyright (C) 2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2010-2023 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 Gamzov // Created: 2010.01.23 -using System; -using System.Diagnostics; using System.Text; using NUnit.Framework; using Xtensive.Sql; using Xtensive.Sql.Dml; using Xtensive.Sql.Drivers.PostgreSql.v8_0; -using Xtensive.Sql.Model; namespace Xtensive.Orm.Tests.Sql.PostgreSql { public class ExtractorTest: ExtractorTestBase { protected override bool CheckContstraintExtracted => true; - protected override bool SeqStartEqualsToMin => true; protected override void CheckRequirements() => Require.ProviderIs(StorageProvider.PostgreSql); @@ -89,12 +85,12 @@ protected override string GetForeignKeyExtractionPrepareScript() " REFERENCES \"B3\" (\"b_id_1\", \"b_id_2\", \"b_id_3\") ON DELETE NO ACTION ON UPDATE CASCADE);"; } protected override string GetForeignKeyExtractionCleanUpScript() => - "drop table \"A1\"" + - "\n drop table \"A2\"" + - "\n drop table \"A3\"" + - "\n drop table \"B1\"" + - "\n drop table \"B2\"" + - "\n drop table \"B3\""; + "drop table \"A1\";" + + "\n drop table \"A2\";" + + "\n drop table \"A3\";" + + "\n drop table \"B1\";" + + "\n drop table \"B2\";" + + "\n drop table \"B3\";"; protected override string GetIndexExtractionPrepareScript(string tableName) { @@ -172,25 +168,33 @@ public void ExtractDateTimeOffsetFields() var testTable = defaultSchema.Tables["InteractionLog"]; var tableColumn = testTable.TableColumns["DateTimeOffset0"]; Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); - Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00+00:00'::timestamp(0) with time zone"))); + Assert.That(tableColumn.DefaultValue, Is.InstanceOf()); + var defaultExpression = (SqlNative) tableColumn.DefaultValue; + Assert.That(defaultExpression.Value, Is.EqualTo("'0001-01-01 04:02:33+04:02:33'::timestamp(0) with time zone")); tableColumn = testTable.TableColumns["DateTimeOffset1"]; Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); - Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00.0+00:00'::timestamp(1) with time zone"))); + Assert.That(tableColumn.DefaultValue, Is.InstanceOf()); + defaultExpression = (SqlNative) tableColumn.DefaultValue; + Assert.That(defaultExpression.Value, Is.EqualTo("'0001-01-01 04:02:33+04:02:33'::timestamp(1) with time zone")); tableColumn = testTable.TableColumns["DateTimeOffset2"]; Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); - Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00.00+00:00'::timestamp(2) with time zone"))); + Assert.That(tableColumn.DefaultValue, Is.InstanceOf()); + defaultExpression = (SqlNative) tableColumn.DefaultValue; + Assert.That(defaultExpression.Value, Is.EqualTo("'0001-01-01 04:02:33+04:02:33'::timestamp(2) with time zone")); tableColumn = testTable.TableColumns["DateTimeOffset3"]; Assert.That(tableColumn.DataType.Type, Is.EqualTo(SqlType.DateTimeOffset)); - Assert.That(tableColumn.DefaultValue, Is.EqualTo(new SqlNative("'0001-01-01 00:00:00.000+00:00'::timestamp(3) with time zone"))); + Assert.That(tableColumn.DefaultValue, Is.InstanceOf()); + defaultExpression = (SqlNative) tableColumn.DefaultValue; + Assert.That(defaultExpression.Value, Is.EqualTo("'0001-01-01 04:02:33+04:02:33'::timestamp(3) with time zone")); } protected virtual string GetExpressionIndexExtractorPrepareScript(string tableName) { - return $"CREATE TABLE \" {tableName} \"(col1 text, col2 text);" + + return $"CREATE TABLE \"{tableName}\"(col1 text, col2 text);" + $"CREATE INDEX \"{tableName}_indx\" ON \"" + tableName + "\"(col1,col2,(col1||col2));"; } From 11857bb29ad978574614d31a703c91b8283f2123 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 1 Mar 2023 13:07:35 +0500 Subject: [PATCH 78/86] PgSQL: Make_timestamp with integer hour and minute --- .../Sql.Drivers.PostgreSql/v10_0/Compiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs index 3c4dc5de19..46248d3651 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs @@ -32,7 +32,7 @@ public override void Visit(SqlFunctionCall node) } protected static SqlUserFunctionCall MakeDateTime(SqlExpression year, SqlExpression month, SqlExpression day) => - SqlDml.FunctionCall("MAKE_TIMESTAMP", year, month, day, SqlDml.Literal(0.0), SqlDml.Literal(0.0), SqlDml.Literal(0.0)); + SqlDml.FunctionCall("MAKE_TIMESTAMP", year, month, day, SqlDml.Literal(0), SqlDml.Literal(0), SqlDml.Literal(0.0)); #if NET6_0_OR_GREATER //DO_DATEONLY protected static SqlUserFunctionCall MakeDate(SqlExpression year, SqlExpression month, SqlExpression day) => From 578b3fa575efad2641126da11ca3cab01e6e5d70 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 1 Mar 2023 20:48:36 +0500 Subject: [PATCH 79/86] DateTime/TimeOnly values adjusting for test purposes changed --- .../TestHelper.cs | 101 ++++++++++++------ ...ueJira0593_AggregateForSingleColumnTest.cs | 8 +- .../DateTime/OperationsTest.cs | 8 +- .../DateTime/PartsExtractionTest.cs | 2 +- .../TimeOnly/OperationsTest.cs | 8 +- .../TimeOnly/PartsExtractionTest.cs | 2 +- .../Storage/DateTimeStoragePrecisionTest.cs | 7 +- 7 files changed, 88 insertions(+), 48 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs index d80501172a..4026bc80da 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs @@ -50,61 +50,98 @@ public static System.Configuration.Configuration GetConfigurationForAssembly(thi return instanceOfTypeFromAssembly.GetType().Assembly.GetAssemblyConfiguration(); } + /// + /// Cuts down resolution of value if needed, according to current . + /// + /// The value to adjust. + /// New value with less resolution if the provider requires it, otherwise, untouched . + public static DateTime AdjustDateTimeForCurrentProvider(this DateTime origin) + { + var provider = StorageProviderInfo.Instance; + return AdjustDateTimeForProvider(origin, provider); + } + /// /// Cuts down resolution of value if needed. /// - /// The value to fix. - /// Type of provider. - /// New value with less resolution if requires it or untouched if the provider doesn't - public static DateTime FixDateTimeForProvider(this DateTime origin, StorageProviderInfo providerInfo) + /// The value to adjust. + /// Type of provider. + /// New value with less resolution if the provider requires it, otherwise, untouched . + private static DateTime AdjustDateTimeForProvider(this DateTime origin, StorageProviderInfo providerInfo) { - long? divider; + //long? divider; var provider = providerInfo.Provider; switch (provider) { case StorageProvider.MySql: - divider = providerInfo.Info.StorageVersion < StorageProviderVersion.MySql56 ? 10000000 : 10; - break; + return providerInfo.Info.StorageVersion < StorageProviderVersion.MySql56 + ? AdjustDateTime(origin, 0) + : AdjustDateTime(origin, 6); case StorageProvider.Firebird: - divider = 1000; - break; + return AdjustDateTime(origin, 4); case StorageProvider.PostgreSql: - divider = 10; - break; + return AdjustDateTime(origin, 6); case StorageProvider.Oracle: - divider = 10; - break; + return AdjustDateTime(origin, 6, true); default: - divider = null; - break; + return origin; } + } - if (!divider.HasValue) { + /// + /// Cuts down fractions of value (nanoseconds, milliseconds, etc) to desired value. + /// + /// + /// Number of fractional points to keep. + /// Indicates whether value should be rounded after cutting off. + /// Result value. + /// Valid fractions should be between 0 and 7 (7 included). + public static DateTime AdjustDateTime(this DateTime origin, byte desiredFractions = 7, bool requireRound = false) + { + if (desiredFractions == 7) { return origin; } + + const int baseDivider = 10_000_000; // no fractions + var currentDivider = baseDivider / (desiredFractions switch { + 0 => 1, + 1 => 10, + 2 => 100, + 3 => 1000, + 4 => 10000, + 5 => 100000, + 6 => 1000000, + _ => throw new ArgumentOutOfRangeException(nameof(desiredFractions)) + }); + var ticks = origin.Ticks; - if (provider == StorageProvider.Oracle) { - var ticks1 = (ticks % divider) >=5 ? ticks - (ticks % divider.Value) + divider.Value - : ticks - (ticks % divider.Value); - return new DateTime(ticks1); - } - if (provider == StorageProvider.MySql) { - var abc = ((ticks % 10000000) / 1000000); - var ticks1 = ((ticks % 10000000) / 1000000) >= 5 ? ticks - (ticks % 10000000) + 10000000 - : ticks - (ticks % 10000000); - return new DateTime(ticks1); - } - var newTicks = ticks - (ticks % divider.Value); + + var newTicks = requireRound + ? ((ticks % currentDivider) / (currentDivider / 10)) >= 5 + ? ticks - (ticks % currentDivider) + currentDivider + : ticks - (ticks % currentDivider) + : ticks - (ticks % currentDivider); return new DateTime(newTicks); } #if NET6_0_OR_GREATER //DO_DATEONLY + /// + /// Cuts down resolution of value if needed, according to current . + /// + /// The value to adjust. + /// New value with less resolution if the provider requires it, otherwise, untouched . + public static TimeOnly AdjustTimeOnlyForCurrentProvider(this TimeOnly origin) + { + var provider = StorageProviderInfo.Instance; + return AdjustTimeOnlyForProvider(origin, provider); + } + /// /// Cuts down resolution of value if needed. /// - /// The value to fix. - /// Type of provider. - /// New value with less resolution if requires it or untouched if the provider doesn't - public static TimeOnly FixTimeOnlyForProvider(this TimeOnly origin, StorageProviderInfo providerInfo) + /// The value to adjust. + /// Type of provider. + /// New value with less resolution if the provider requires it, otherwise, untouched . + public static TimeOnly AdjustTimeOnlyForProvider(this TimeOnly origin, StorageProviderInfo providerInfo) { long? divider; var provider = providerInfo.Provider; diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueJira0593_AggregateForSingleColumnTest.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueJira0593_AggregateForSingleColumnTest.cs index ab8b162010..4a194139c4 100644 --- a/Orm/Xtensive.Orm.Tests/Issues/IssueJira0593_AggregateForSingleColumnTest.cs +++ b/Orm/Xtensive.Orm.Tests/Issues/IssueJira0593_AggregateForSingleColumnTest.cs @@ -700,10 +700,10 @@ private static void CheckQueryable(IQueryable query) private static void CheckQueryable(IQueryable query) { var localArray = query.ToArray(); - Assert.AreEqual(localArray.Min().FixDateTimeForProvider(StorageProviderInfo.Instance), query.Min(c => c)); - Assert.AreEqual(localArray.Min().FixDateTimeForProvider(StorageProviderInfo.Instance), query.Min()); - Assert.AreEqual(localArray.Max().FixDateTimeForProvider(StorageProviderInfo.Instance), query.Max(c => c)); - Assert.AreEqual(localArray.Max().FixDateTimeForProvider(StorageProviderInfo.Instance), query.Max()); + Assert.AreEqual(localArray.Min().AdjustDateTimeForCurrentProvider(), query.Min(c => c)); + Assert.AreEqual(localArray.Min().AdjustDateTimeForCurrentProvider(), query.Min()); + Assert.AreEqual(localArray.Max().AdjustDateTimeForCurrentProvider(), query.Max(c => c)); + Assert.AreEqual(localArray.Max().AdjustDateTimeForCurrentProvider(), query.Max()); Assert.AreEqual(localArray.Count(), query.Count()); } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs index b0b2bb46d7..cc3d5cf6b8 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs @@ -196,10 +196,10 @@ public void MysqlMinisDateTimeTest() { Require.ProviderIs(StorageProvider.MySql); ExecuteInsideSession(() => { - var firstDateTime = FirstDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); - var firstMillisecondDateTime = FirstMillisecondDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); - var secondDateTime = SecondDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); - var nullableDateTime = NullableDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); + var firstDateTime = FirstDateTime.AdjustDateTimeForCurrentProvider(); + var firstMillisecondDateTime = FirstMillisecondDateTime.AdjustDateTimeForCurrentProvider(); + var secondDateTime = SecondDateTime.AdjustDateTimeForCurrentProvider(); + var nullableDateTime = NullableDateTime.AdjustDateTimeForCurrentProvider(); RunTest(c => c.DateTime - secondDateTime == firstDateTime - secondDateTime); RunTest(c => c.MillisecondDateTime - secondDateTime == firstMillisecondDateTime - secondDateTime); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs index 93966431c0..13cf6cd7d1 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs @@ -111,7 +111,7 @@ public void MysqlExtractMillisecondTest() { Require.ProviderIs(StorageProvider.MySql); ExecuteInsideSession((s) => { - var firstMillisecondDateTime = FirstMillisecondDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); + var firstMillisecondDateTime = FirstMillisecondDateTime.AdjustDateTimeForCurrentProvider(); RunTest(s, c => c.MillisecondDateTime.Millisecond == firstMillisecondDateTime.Millisecond); RunWrongTest(s, c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Millisecond); }); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs index 9d02d8a124..baaecc212a 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs @@ -92,10 +92,10 @@ public void MysqlMinisTimeOnlyTest() { Require.ProviderIs(StorageProvider.MySql); ExecuteInsideSession((s) => { - var firstTimeOnly = FirstTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); - var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); - var secondTimeOnly = SecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); - var nullableTimeOnly = NullableTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + var firstTimeOnly = FirstTimeOnly.AdjustTimeOnlyForCurrentProvider(); + var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.AdjustTimeOnlyForCurrentProvider(); + var secondTimeOnly = SecondTimeOnly.AdjustTimeOnlyForCurrentProvider(); + var nullableTimeOnly = NullableTimeOnly.AdjustTimeOnlyForCurrentProvider(); RunTest(s, c => c.TimeOnly - secondTimeOnly == firstTimeOnly - secondTimeOnly); RunTest(s, c => c.MillisecondTimeOnly - secondTimeOnly == firstMillisecondTimeOnly - secondTimeOnly); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs index fcd65dca3a..5c1d96db34 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs @@ -70,7 +70,7 @@ public void MysqlExtractMillisecondTest() Require.ProviderIs(StorageProvider.MySql); Require.ProviderVersionAtLeast(new Version(5, 6));// no support for fractions below 5.6 ExecuteInsideSession((s) => { - var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + var firstMillisecondTimeOnly = FirstMillisecondTimeOnly.AdjustTimeOnlyForCurrentProvider(); RunTest(s, c => c.MillisecondTimeOnly.Millisecond == firstMillisecondTimeOnly.Millisecond); RunWrongTest(s, c => c.MillisecondTimeOnly.Second == WrongMillisecondTimeOnly.Millisecond); }); diff --git a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs index 926a7e35a3..11911c2b88 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs @@ -89,9 +89,12 @@ public void TimeOnlyTest() } } - private static TimeOnly GetExpectedValue(in TimeOnly baseTimeOnly) => baseTimeOnly.FixTimeOnlyForProvider(StorageProviderInfo.Instance); + private static TimeOnly GetExpectedValue(in TimeOnly baseTimeOnly) => baseTimeOnly.AdjustTimeOnlyForCurrentProvider(); #endif - private static DateTime GetExpectedValue(in DateTime baseDateTime) => baseDateTime.FixDateTimeForProvider(StorageProviderInfo.Instance); + private static DateTime GetExpectedValue(in DateTime baseDateTime) => + StorageProviderInfo.Instance.Provider == StorageProvider.MySql + ? baseDateTime.AdjustDateTime(0, true) + : baseDateTime.AdjustDateTimeForCurrentProvider(); } } \ No newline at end of file From c70c497ea3b3f047343ee1f74fab385691550a89 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 2 Mar 2023 17:56:03 +0500 Subject: [PATCH 80/86] Some tricky operations with datetime fractions changed Only tests affected --- Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs | 9 ++++----- .../DateTime/OperationsTest.cs | 10 +++++----- .../DateTime/PartsExtractionTest.cs | 2 +- .../Storage/DateTimeStoragePrecisionTest.cs | 11 ++++++++--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs index 4026bc80da..08e74502a9 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs @@ -67,9 +67,8 @@ public static DateTime AdjustDateTimeForCurrentProvider(this DateTime origin) /// The value to adjust. /// Type of provider. /// New value with less resolution if the provider requires it, otherwise, untouched . - private static DateTime AdjustDateTimeForProvider(this DateTime origin, StorageProviderInfo providerInfo) + public static DateTime AdjustDateTimeForProvider(this DateTime origin, StorageProviderInfo providerInfo) { - //long? divider; var provider = providerInfo.Provider; switch (provider) { case StorageProvider.MySql: @@ -81,7 +80,7 @@ private static DateTime AdjustDateTimeForProvider(this DateTime origin, StorageP case StorageProvider.PostgreSql: return AdjustDateTime(origin, 6); case StorageProvider.Oracle: - return AdjustDateTime(origin, 6, true); + return AdjustDateTime(origin, 7); default: return origin; } @@ -91,11 +90,11 @@ private static DateTime AdjustDateTimeForProvider(this DateTime origin, StorageP /// Cuts down fractions of value (nanoseconds, milliseconds, etc) to desired value. /// /// - /// Number of fractional points to keep. + /// Number of fractional points to keep (from 0 to 7). /// Indicates whether value should be rounded after cutting off. /// Result value. /// Valid fractions should be between 0 and 7 (7 included). - public static DateTime AdjustDateTime(this DateTime origin, byte desiredFractions = 7, bool requireRound = false) + public static DateTime AdjustDateTime(this DateTime origin, byte desiredFractions, bool requireRound = false) { if (desiredFractions == 7) { return origin; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs index cc3d5cf6b8..3b73872219 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/OperationsTest.cs @@ -192,14 +192,14 @@ public void MinusDateTimeTest() } [Test] - public void MysqlMinisDateTimeTest() + public void MysqlMinusDateTimeTest() { Require.ProviderIs(StorageProvider.MySql); ExecuteInsideSession(() => { - var firstDateTime = FirstDateTime.AdjustDateTimeForCurrentProvider(); - var firstMillisecondDateTime = FirstMillisecondDateTime.AdjustDateTimeForCurrentProvider(); - var secondDateTime = SecondDateTime.AdjustDateTimeForCurrentProvider(); - var nullableDateTime = NullableDateTime.AdjustDateTimeForCurrentProvider(); + var firstDateTime = FirstDateTime.AdjustDateTime(0); + var firstMillisecondDateTime = FirstMillisecondDateTime.AdjustDateTime(0); + var secondDateTime = SecondDateTime.AdjustDateTime(0); + var nullableDateTime = NullableDateTime.AdjustDateTime(0); RunTest(c => c.DateTime - secondDateTime == firstDateTime - secondDateTime); RunTest(c => c.MillisecondDateTime - secondDateTime == firstMillisecondDateTime - secondDateTime); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs index 13cf6cd7d1..1d1b2a574e 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/PartsExtractionTest.cs @@ -111,7 +111,7 @@ public void MysqlExtractMillisecondTest() { Require.ProviderIs(StorageProvider.MySql); ExecuteInsideSession((s) => { - var firstMillisecondDateTime = FirstMillisecondDateTime.AdjustDateTimeForCurrentProvider(); + var firstMillisecondDateTime = FirstMillisecondDateTime.AdjustDateTime(0); RunTest(s, c => c.MillisecondDateTime.Millisecond == firstMillisecondDateTime.Millisecond); RunWrongTest(s, c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Millisecond); }); diff --git a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs index 11911c2b88..4b14b36447 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs @@ -92,9 +92,14 @@ public void TimeOnlyTest() private static TimeOnly GetExpectedValue(in TimeOnly baseTimeOnly) => baseTimeOnly.AdjustTimeOnlyForCurrentProvider(); #endif - private static DateTime GetExpectedValue(in DateTime baseDateTime) => - StorageProviderInfo.Instance.Provider == StorageProvider.MySql + private static DateTime GetExpectedValue(in DateTime baseDateTime) + { + var provider = StorageProviderInfo.Instance.Provider; + return provider == StorageProvider.MySql ? baseDateTime.AdjustDateTime(0, true) - : baseDateTime.AdjustDateTimeForCurrentProvider(); + : provider == StorageProvider.Oracle + ? baseDateTime.AdjustDateTime(6, true) + : baseDateTime.AdjustDateTimeForCurrentProvider(); + } } } \ No newline at end of file From ef5331574c62ac5bf5fe32b0b3e11fb852d533d7 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 3 Mar 2023 20:10:00 +0500 Subject: [PATCH 81/86] Remove DO_DATEONLY mark and other minor changes --- .../Sql.Drivers.Firebird/Constants.cs | 2 +- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 22 +++++-------- .../v2_5/ServerInfoProvider.cs | 2 +- .../Sql.Drivers.Firebird/v2_5/Translator.cs | 7 ++-- .../Sql.Drivers.MySql/v5_0/Compiler.cs | 12 +++---- .../Sql.Drivers.MySql/v5_0/Extractor.cs | 2 +- .../v5_0/ServiceInfoProvider.cs | 2 -- .../Sql.Drivers.MySql/v5_0/Translator.cs | 13 ++++---- .../Sql.Drivers.MySql/v5_0/TypeMapper.cs | 4 +-- .../Sql.Drivers.MySql/v5_6/Compiler.cs | 6 ++-- .../Sql.Drivers.Oracle/v09/Compiler.cs | 32 +++++++----------- .../v09/ServerInfoProvider.cs | 2 +- .../Sql.Drivers.Oracle/v09/Translator.cs | 6 ++-- .../Sql.Drivers.Oracle/v09/TypeMapper.cs | 2 +- .../Sql.Drivers.PostgreSql/v10_0/Compiler.cs | 4 +-- .../Sql.Drivers.PostgreSql/v8_0/Compiler.cs | 15 ++++----- .../v8_0/ServerInfoProvider.cs | 2 +- .../Sql.Drivers.PostgreSql/v8_0/Translator.cs | 8 ++--- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 22 +++++-------- .../v09/ServerInfoProvider.cs | 3 +- .../Sql.Drivers.SqlServer/v09/Translator.cs | 4 +-- .../Sql.Drivers.SqlServer/v10/Compiler.cs | 5 +-- .../v10/ServerInfoProvider.cs | 3 +- .../Sql.Drivers.SqlServer/v10/Translator.cs | 2 +- .../Sql.Drivers.SqlServer/v11/Compiler.cs | 14 -------- .../Sql.Drivers.SqlServer/v13/Compiler.cs | 6 ++-- .../Sql.Drivers.Sqlite/v3/Compiler.cs | 17 +++++----- .../v3/ServerInfoProvider.cs | 3 +- .../Sql.Drivers.Sqlite/v3/Translator.cs | 4 +-- .../Sql.Drivers.Sqlite/v3/TypeMapper.cs | 4 +-- .../TestHelper.cs | 2 +- .../DateTimeIntervalTest.cs | 4 +-- .../ExtractorTestBase.cs | 2 +- .../Firebird/ExtractorTest.cs | 2 +- .../MySQL/ExtractorTest.cs | 2 +- .../Oracle/ExtractorTest.cs | 2 +- .../PostgreSql/ExtractorTest.cs | 2 +- .../SqlDateTimePartsTest.cs | 2 +- .../SqlServer/MSSQLExtractorTests.cs | 2 +- .../Sqlite/ExtractorTest.cs | 2 +- Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs | 2 +- .../DateOnly/ComparisonTestDateOnly.cs | 2 +- .../DateOnly/ConstructorTest.cs | 2 +- .../DateOnly/DateOnlyToStringTest.cs | 4 +-- .../DateOnly/DistinctTest.cs | 2 +- .../DateOnly/GroupByTest.cs | 2 +- .../DateOnly/JoinTest.cs | 2 +- .../DateOnly/MinMaxTest.cs | 2 +- .../DateOnly/OperationsTest.cs | 2 +- .../DateOnly/OrderByTest.cs | 2 +- .../DateOnly/PartsExtractionTest.cs | 6 ++-- .../DateOnly/WhereTest.cs | 3 +- .../DateTimeBaseTest.cs | 5 ++- .../Linq/DateTimeAndDateTimeOffset/Model.cs | 3 +- .../TimeOnly/ComparisonTestTimeOnly.cs | 3 +- .../TimeOnly/ConstructorTest.cs | 2 +- .../TimeOnly/DistinctTest.cs | 2 +- .../TimeOnly/GroupByTest.cs | 2 +- .../TimeOnly/JoinTest.cs | 2 +- .../TimeOnly/MinMaxTest.cs | 2 +- .../TimeOnly/OperationsTest.cs | 2 +- .../TimeOnly/OrderByTest.cs | 2 +- .../TimeOnly/PartsExtractionTest.cs | 2 +- .../TimeOnly/TimeOnlyToString.cs | 2 +- .../TimeOnly/WhereTest.cs | 2 +- .../Storage/DateTimeStoragePrecisionTest.cs | 6 ++-- .../Storage/FieldDefaultValueTest.cs | 10 +++--- .../SchemaSharing/EntityManipulation/Model.cs | 8 ----- .../Storage/SetFieldTest.cs | 11 +++++-- .../Storage/TypeCompatibilityTest.cs | 10 +++--- .../Upgrade/DateTimeDataConversions.cs | 7 ++-- Orm/Xtensive.Orm/Linq/ExpressionWriter.cs | 2 +- .../Orm/Building/Builders/ValueTypeBuilder.cs | 2 +- .../Orm/Linq/ExpressionExtensions.cs | 2 +- .../Orm/Linq/ItemToTupleConverter{TItem}.cs | 2 +- .../Orm/Providers/DbCommandExtensions.cs | 2 +- .../Orm/Providers/DomainHandler.cs | 2 +- .../ExpressionProcessor.Helpers.cs | 2 +- .../ExpressionTranslationHelpers.cs | 2 +- .../Orm/Providers/SqlCompiler.Helpers.cs | 4 +-- .../Upgrade/Internals/SqlActionTranslator.cs | 4 +-- Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs | 3 +- .../Sql/Compiler/SqlTranslator.cs | 2 +- .../Sql/Dml/Expressions/SqlExpression.cs | 2 +- .../Sql/Dml/Expressions/SqlExtract.cs | 12 +++---- Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs | 6 ++-- .../Sql/Info/DataTypeCollection.cs | 4 +-- Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs | 2 +- .../Sql/Internals/SqlValidator.cs | 2 +- Orm/Xtensive.Orm/Sql/SqlDml.cs | 33 ++++--------------- Orm/Xtensive.Orm/Sql/SqlDriver.cs | 4 +-- Orm/Xtensive.Orm/Sql/SqlNodeType.cs | 2 +- Orm/Xtensive.Orm/Sql/SqlType.cs | 2 +- .../Sql/ValueTypeMapping/TypeMapper.cs | 6 ++-- .../Tuples/Packed/PackedFieldAccessor.cs | 2 +- Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs | 6 ++-- 96 files changed, 206 insertions(+), 272 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs index e5d30d15d7..700a2187fa 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs @@ -14,7 +14,7 @@ internal static class Constants // cannot use "FFF" cause it may lead to empty string for fractions part. public const string DateTimeFormatString = @"''\'yyyy\.MM\.dd HH\:mm\:ss\.fff\'''"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER public const string DateFormatString = @"''\'yyyy\.MM\.dd\'''"; public const string TimeFormatString = @"''\'HH\:mm\:ss\.ffff\'''"; #endif diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index 637b3677ab..1c00fdfaea 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs @@ -176,7 +176,7 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeMinusDateTime: DateTimeSubtractDateTime(node.Left, node.Right).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); return; @@ -233,7 +233,7 @@ public override void Visit(SqlFunctionCall node) arguments[1] - 1), arguments[2] - 1)); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateAddYears: Visit(DateAddYear(arguments[0], arguments[1])); return; @@ -308,8 +308,8 @@ protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, Sql (CastToLong(DateDiffMillisecond(DateAddDay(date2, DateDiffDay(date2, date1)), date1)) * NanosecondsPerMillisecond); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected static SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) { return SqlDml.Modulo( @@ -368,8 +368,8 @@ protected static SqlUserFunctionCall BitXor(SqlExpression left, SqlExpression ri protected static SqlUserFunctionCall BitNot(SqlExpression operand) => SqlDml.FunctionCall("BIN_NOT", operand); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected static SqlExpression TimeToDateTime(SqlExpression time) => SqlDml.Cast(time, SqlType.DateTime); @@ -382,15 +382,11 @@ protected static SqlExpression DateToDateTime(SqlExpression date) => protected static SqlExpression DateTimeToDate(SqlExpression dateTime) => SqlDml.Cast(dateTime, SqlType.Date); - protected static SqlFunctionCall DateToString(SqlExpression date) - { - return SqlDml.Substring(date, 0, 10);; - } + protected static SqlFunctionCall DateToString(SqlExpression date) => + SqlDml.Substring(date, 0, 10); - protected static SqlConcat TimeToString(SqlExpression time) - { - return SqlDml.Concat(SqlDml.Substring(time, 0, 12), SqlDml.Literal("0000")); - } + protected static SqlConcat TimeToString(SqlExpression time) => + SqlDml.Concat(SqlDml.Substring(time, 0, 12), SqlDml.Literal("0000")); #endif protected static SqlConcat DateTimeToStringIso(SqlExpression dateTime) @@ -400,7 +396,7 @@ protected static SqlConcat DateTimeToStringIso(SqlExpression dateTime) return SqlDml.Concat(date, SqlDml.Literal("T"), time); } - + #endregion protected internal Compiler(SqlDriver driver) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs index a7b0e1493a..bb32bdb1dd 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/ServerInfoProvider.cs @@ -237,8 +237,8 @@ public override DataTypeCollection GetDataTypesInfo() dtc.DateTime = DataTypeInfo.Range(SqlType.DateTime, commonFeatures, ValueRange.DateTime, "timestamp"); - #if NET6_0_OR_GREATER + dtc.DateOnly = DataTypeInfo.Range(SqlType.Date, commonFeatures, ValueRange.DateOnly, "date"); dtc.TimeOnly = DataTypeInfo.Range(SqlType.Time, commonFeatures, ValueRange.TimeOnly, "time"); #endif diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs index 3e2358a5a2..b60a32f15c 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs @@ -21,8 +21,9 @@ internal class Translator : SqlTranslator { public override string DateTimeFormatString => Constants.DateTimeFormatString; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER public override string DateOnlyFormatString => Constants.DateFormatString; + public override string TimeOnlyFormatString => Constants.TimeFormatString; #endif @@ -122,7 +123,7 @@ public override void Translate(IOutput output, SqlNodeType type) case SqlNodeType.Modulo: _ = output.Append("MOD"); break; case SqlNodeType.DateTimeMinusDateTime: -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimeMinusTime: #endif _ = output.Append("-"); break; @@ -223,8 +224,8 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) default: base.Translate(output, dateTimePart); break; } } - #if NET6_0_OR_GREATER //DO_DATEONLY + /// public override void Translate(IOutput output, SqlDatePart datePart) { diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs index de24cd7313..ae20d304fa 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs @@ -94,7 +94,7 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeMinusInterval: DateTimeAddInterval(node.Left, -node.Right).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); return; @@ -187,7 +187,7 @@ public override void Visit(SqlFunctionCall node) arguments[1] - 1), arguments[2] - 1)); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateAddYears: Visit(DateAddYear(arguments[0], arguments[1])); return; @@ -252,8 +252,8 @@ public override void Visit(SqlFunctionCall node) base.Visit(node); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public override void Visit(SqlPlaceholder node) { if (node.Id is Orm.Providers.ParameterBinding qpb @@ -292,7 +292,7 @@ public override void Visit(SqlExtract node) Visit(SqlDml.FunctionCall(node.DateTimePart.ToString(), node.Operand)); return; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER if (node.DatePart == SqlDatePart.DayOfWeek || node.DatePart == SqlDatePart.DayOfYear) { Visit(SqlDml.FunctionCall(node.DatePart.ToString(), node.Operand)); return; @@ -315,8 +315,8 @@ protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpre DateTimeAddDay(date, ((interval - (interval % NanosecondsPerDay)) + ((interval % NanosecondsPerDay) > (NanosecondsPerDay / 2) ? 0 : 1)) / NanosecondsPerDay), (interval / NanosecondsPerMillisecond * NanosecondsPerMicrosecond) % (MillisecondsPerDay * NanosecondsPerMicrosecond)); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) => SqlDml.Modulo( NanosecondsPerDay + CastToDecimal(SqlDml.FunctionCall("TIME_TO_SEC", time1) - SqlDml.FunctionCall("TIME_TO_SEC", time2), 18, 0) * NanosecondsPerSecond, @@ -363,8 +363,8 @@ protected static SqlUserFunctionCall DateTimeAddHour(SqlExpression datetime, Sql protected static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) => SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, datetime); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) => SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(years, SqlDml.Native("YEAR")))); 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 5b086f09b6..5e9415786e 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 @@ -652,7 +652,7 @@ private SqlValueType CreateValueType(IDataRecord row, // although they can be read as "scale" and "precision" return new SqlValueType(SqlType.Int64); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER if (typeName.Equals("TIME", StringComparison.Ordinal) || typeName.StartsWith("TIME(")) { return new SqlValueType(SqlType.Time); } diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs index da09c28bd2..349ab79a14 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/ServiceInfoProvider.cs @@ -275,8 +275,6 @@ public override DataTypeCollection GetDataTypesInfo() "datetime", "time"); #endif - - types.Char = DataTypeInfo.Stream(SqlType.Char, common | index, 255, "char"); types.VarChar = DataTypeInfo.Stream(SqlType.VarChar, common | index, 4000, "varchar"); diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs index 69e7c5190b..ddd294a19d 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs @@ -22,8 +22,10 @@ namespace Xtensive.Sql.Drivers.MySql.v5_0 internal class Translator : SqlTranslator { public override string DateTimeFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss\.ffffff\'"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER + public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'"; + public override string TimeOnlyFormatString => @"\'HH\:mm\:ss\.ffffff\'"; #endif @@ -110,7 +112,7 @@ public override void Translate(IOutput output, SqlFunctionType type) case SqlFunctionType.DateTimeAddYears: case SqlFunctionType.DateTimeAddMonths: case SqlFunctionType.DateTimeConstruct: -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateAddYears: case SqlFunctionType.DateAddMonths: case SqlFunctionType.DateAddDays: @@ -170,14 +172,14 @@ public override void Translate(IOutput output, SqlNodeType type) switch (type) { case SqlNodeType.Concat: _ = output.Append(","); break; case SqlNodeType.DateTimePlusInterval: -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: #endif _ = output.Append("+"); break; case SqlNodeType.DateTimeMinusInterval: case SqlNodeType.DateTimeMinusDateTime: -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimeMinusTime: #endif _ = output.Append("-"); break; @@ -527,8 +529,8 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) default: base.Translate(output, dateTimePart); break; } } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY /// public override void Translate(IOutput output, SqlDatePart datePart) { @@ -552,7 +554,6 @@ public override void Translate(IOutput output, SqlTimePart dateTimePart) } #endif - /// public override void Translate(IOutput output, SqlLockType lockType) { diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs index 35928be8d2..699acb9282 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs @@ -14,7 +14,7 @@ namespace Xtensive.Sql.Drivers.MySql.v5_0 { internal class TypeMapper : Sql.TypeMapper { -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private static readonly Type[] CastRequiredTypes = new[] { typeof(Guid), typeof(TimeSpan), typeof(byte[]), typeof (DateOnly), typeof(TimeOnly) }; #else private static readonly Type[] CastRequiredTypes = new[] { typeof(Guid), typeof(TimeSpan), typeof(byte[]) }; @@ -66,8 +66,8 @@ public override void BindGuid(DbParameter parameter, object value) parameter.DbType = DbType.String; parameter.Value = value==null ? (object) DBNull.Value : SqlHelper.GuidToString((Guid) value); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public override void BindDateOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Date; diff --git a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs index 8ce6b46262..debe6cd67f 100644 --- a/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs +++ b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs @@ -11,7 +11,7 @@ namespace Xtensive.Sql.Drivers.MySql.v5_6 { internal class Compiler : v5_5.Compiler { -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER public override void Visit(SqlBinary node) { if (node.NodeType == SqlNodeType.TimePlusInterval) { @@ -31,14 +31,14 @@ public override void Visit(SqlFunctionCall node) SqlDml.FunctionCall("TIME", SqlDml.FunctionCall("ADDTIME", SqlDml.Cast(arguments[0], SqlType.DateTime), - arguments[1] * 10000))); // 10000 = 1:00:00 :) + arguments[1] * 10000))); // 10000 = 1:00:00 return; case SqlFunctionType.TimeAddMinutes: Visit( SqlDml.FunctionCall("TIME", SqlDml.FunctionCall("ADDTIME", SqlDml.Cast(arguments[0], SqlType.DateTime), - arguments[1] * 100))); // 100 = 0:01:00 :) + arguments[1] * 100))); // 100 = 0:01:00 return; case SqlFunctionType.TimeConstruct: { Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3])); diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs index 1138091969..168462fb70 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -68,41 +68,34 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeConstruct: DateTimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateConstruct: DateConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2]).AcceptVisitor(this); return; case SqlFunctionType.TimeConstruct: TimeConstruct(node.Arguments[0], node.Arguments[1], node.Arguments[2], node.Arguments[3]).AcceptVisitor(this); return; - case SqlFunctionType.DateAddYears: { + case SqlFunctionType.DateAddYears: DateTimeAddYMInterval(node.Arguments[0], node.Arguments[1], YearIntervalPart).AcceptVisitor(this); return; - } - case SqlFunctionType.DateAddMonths: { + case SqlFunctionType.DateAddMonths: DateTimeAddYMInterval(node.Arguments[0], node.Arguments[1], MonthIntervalPart).AcceptVisitor(this); return; - } - case SqlFunctionType.DateAddDays: { + case SqlFunctionType.DateAddDays: DateTimeAddDSInterval(node.Arguments[0], node.Arguments[1], DayIntervalPart).AcceptVisitor(this); return; - } - case SqlFunctionType.TimeAddHours: { + case SqlFunctionType.TimeAddHours: TimeAddHourOrMinute(node.Arguments[0], node.Arguments[1], true).AcceptVisitor(this); return; - } - case SqlFunctionType.TimeAddMinutes: { + case SqlFunctionType.TimeAddMinutes: TimeAddHourOrMinute(node.Arguments[0], node.Arguments[1], false).AcceptVisitor(this); return; - } - case SqlFunctionType.DateToString: { + case SqlFunctionType.DateToString: DateToString(node.Arguments[0]).AcceptVisitor(this); return; - } - case SqlFunctionType.TimeToString: { + case SqlFunctionType.TimeToString: TimeToString(node.Arguments[0]).AcceptVisitor(this); return; - } #endif case SqlFunctionType.IntervalAbs: SqlHelper.IntervalAbs(node.Arguments[0]).AcceptVisitor(this); @@ -251,7 +244,7 @@ public override void Visit(SqlExtract node) DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this); return; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER switch (node.DatePart) { case SqlDatePart.DayOfYear: DateTimeExtractDayOfYear(node.Operand).AcceptVisitor(this); @@ -344,7 +337,7 @@ public override void Visit(SqlBinary node) case SqlNodeType.BitXor: BitXor(node.Left, node.Right).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); return; @@ -375,8 +368,8 @@ private static SqlExpression DateTimeConstruct(SqlExpression years, SqlExpressio SqlDml.FunctionCall("TO_TIMESTAMP", SqlDml.FunctionCall(ToCharFunctionName, ((years * 100) + months) * 100 + days), AnsiString("YYYYMMDD")); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY private static SqlExpression DateConstruct(SqlExpression years, SqlExpression months, SqlExpression days) => SqlDml.FunctionCall("TO_DATE", SqlDml.FunctionCall(ToCharFunctionName, ((years * 100) + months) * 100 + days), @@ -491,8 +484,8 @@ private static SqlExpression DateTimeOffsetToUtcTime(SqlExpression dateTimeOffse private static SqlExpression DateToDateTimeOffset(SqlExpression date) => SqlDml.Cast(date, SqlType.DateTimeOffset); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY private static SqlExpression DateTimeToDate(SqlExpression dateTime) => SqlDml.Cast(dateTime, SqlType.Date); @@ -528,7 +521,6 @@ private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset) ) ) ), SqlType.Time); - //SqlDml.Cast(dateTimeOffset - SqlDml.Cast(dateTimeOffset, SqlType.Date), SqlType.Time); private static SqlExpression TimeToDateTimeOffset(SqlExpression time) => SqlDml.Cast( diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs index 5eab3ba4a4..07e4c74a50 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs @@ -228,7 +228,7 @@ public override DataTypeCollection GetDataTypesInfo() types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, ValueRange.DateTime, "timestamp"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, ValueRange.DateOnly, "DATE"); types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index d3ffad5353..4fb59e564d 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -29,8 +29,8 @@ internal class Translator : SqlTranslator /// public override string DateTimeFormatString => @"'(TIMESTAMP '\'yyyy\-MM\-dd HH\:mm\:ss\.fff\'\)"; +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY /// public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)"; @@ -365,8 +365,8 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) break; } } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY /// public override void Translate(IOutput output, SqlDatePart datePart) { @@ -427,7 +427,7 @@ public override void Translate(IOutput output, SqlNodeType type) switch (type) { case SqlNodeType.DateTimeOffsetPlusInterval: case SqlNodeType.DateTimePlusInterval: -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: #endif _ = output.Append("+"); break; diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs index daeb325e76..a9e3149e13 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs @@ -87,8 +87,8 @@ public override void BindDouble(DbParameter parameter, object value) nativeParameter.OracleDbType = OracleDbType.Double; nativeParameter.Value = value ?? DBNull.Value; } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public override void BindDateOnly(DbParameter parameter, object value) { var nativeParameter = (OracleParameter) parameter; diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs index 46248d3651..3f3c550c59 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v10_0/Compiler.cs @@ -17,7 +17,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeConstruct: Visit(MakeDateTime(arguments[0], arguments[1], arguments[2])); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateConstruct: Visit(MakeDate(arguments[0], arguments[1], arguments[2])); return; @@ -33,8 +33,8 @@ public override void Visit(SqlFunctionCall node) protected static SqlUserFunctionCall MakeDateTime(SqlExpression year, SqlExpression month, SqlExpression day) => SqlDml.FunctionCall("MAKE_TIMESTAMP", year, month, day, SqlDml.Literal(0), SqlDml.Literal(0), SqlDml.Literal(0.0)); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected static SqlUserFunctionCall MakeDate(SqlExpression year, SqlExpression month, SqlExpression day) => SqlDml.FunctionCall("MAKE_DATE", year, month, day); diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs index 55d5db7920..24d0d626aa 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Compiler.cs @@ -14,7 +14,7 @@ namespace Xtensive.Sql.Drivers.PostgreSql.v8_0 internal class Compiler : SqlCompiler { private const string DateTimeIsoFormat = "YYYY-MM-DD\"T\"HH24:MI:SS"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private const string DateFormat = "YYYY-MM-DD"; private const string TimeFormat = "HH24:MI:SS.US0"; #endif @@ -31,7 +31,7 @@ internal class Compiler : SqlCompiler private static readonly SqlLiteral ReferenceDateTimeLiteral = SqlDml.Literal(new DateTime(2001, 1, 1)); private static readonly SqlLiteral EpochLiteral = SqlDml.Literal(new DateTime(1970, 1, 1)); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private static readonly SqlLiteral ReferenceDateLiteral = SqlDml.Literal(new DateOnly(2001, 1, 1)); private static readonly SqlLiteral ZeroTimeLiteral = SqlDml.Literal(new TimeOnly(0, 0, 0)); #endif @@ -81,7 +81,7 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeOffsetPlusInterval: (node.Left + node.Right).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimeMinusTime: SqlDml.Cast( SqlDml.Cast( @@ -131,20 +131,19 @@ public override void Visit(SqlFunctionCall node) + (OneDayInterval * (node.Arguments[2] - 1)); newNode.AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateConstruct: (ReferenceDateLiteral + (OneYearInterval * (node.Arguments[0] - 2001)) + (OneMonthInterval * (node.Arguments[1] - 1)) + (OneDayInterval * (node.Arguments[2] - 1))).AcceptVisitor(this); return; - case SqlFunctionType.TimeConstruct: { + case SqlFunctionType.TimeConstruct: ((ZeroTimeLiteral + (OneHourInterval * (node.Arguments[0])) + (OneMinuteInterval * (node.Arguments[1])) + (OneSecondInterval * (node.Arguments[2] + (SqlDml.Cast(node.Arguments[3], SqlType.Double) / 1000))))).AcceptVisitor(this); return; - } #endif case SqlFunctionType.DateTimeTruncate: (SqlDml.FunctionCall("date_trunc", "day", node.Arguments[0])).AcceptVisitor(this); @@ -155,7 +154,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: (node.Arguments[0] + node.Arguments[1] * OneYearInterval).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateAddYears: (node.Arguments[0] + node.Arguments[1] * OneYearInterval).AcceptVisitor(this); return; @@ -199,7 +198,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeOffsetToDateTime: DateTimeOffsetToDateTime(node.Arguments[0]).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateTimeToDate: DateTimeToDate(node.Arguments[0]).AcceptVisitor(this); return; diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs index 7a28bb5bcb..2ab4e9f93a 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs @@ -177,8 +177,8 @@ public override DataTypeCollection GetDataTypesInfo() dtc.Interval = DataTypeInfo.Range(SqlType.Interval, commonFeatures, ValueRange.TimeSpan, "interval"); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY dtc.DateOnly = DataTypeInfo.Range(SqlType.Date, commonFeatures, ValueRange.DateOnly, "date"); dtc.TimeOnly = DataTypeInfo.Range(SqlType.Time, commonFeatures, ValueRange.TimeOnly, "time"); #endif diff --git a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs index bbca9ec06c..b0d25c48c8 100644 --- a/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs +++ b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Translator.cs @@ -53,7 +53,7 @@ public SqlFunctionTypeTranslations(int count) /// public override string DateTimeFormatString => @"\'yyyyMMdd HHmmss.ffffff\''::timestamp(6)'"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER /// public override string DateOnlyFormatString => @"\'yyyyMMdd\''::date'"; @@ -205,12 +205,12 @@ public override void Translate(IOutput output, SqlNodeType type) case SqlNodeType.Modulo: _ = output.Append("%"); break; case SqlNodeType.Overlaps: _ = output.Append("OVERLAPS"); break; case SqlNodeType.DateTimePlusInterval: _ = output.Append("+"); break; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: _ = output.Append("+"); break; #endif case SqlNodeType.DateTimeMinusInterval: case SqlNodeType.DateTimeMinusDateTime: -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimeMinusTime: #endif _ = output.Append("-"); break; @@ -858,8 +858,8 @@ public override void Translate(IOutput output, SqlDateTimeOffsetPart part) default: base.Translate(output, part); break; } } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY /// public override void Translate(IOutput output, SqlDatePart part) { diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs index 38d7569eac..fa4798592b 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -199,7 +199,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeAddYears: Visit(DateAddYear(arguments[0], arguments[1])); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateAddYears: Visit(DateAddYear(arguments[0], arguments[1])); return; @@ -330,7 +330,7 @@ public override void Visit(SqlBinary node) case SqlNodeType.DateTimeMinusInterval: DateTimeAddInterval(node.Left, -node.Right).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); return; @@ -481,32 +481,28 @@ protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpre DateAddDay(date, interval / NanosecondsPerDay), (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); } - #if NET6_0_OR_GREATER + /// /// Creates expression that represents addition to the given . /// /// Time expression. /// Interval expression to add. /// - protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) - { - return DateAddMillisecond(time, (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); - } + protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) => + DateAddMillisecond(time, (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay)); /// - /// Creates expression that represents subtraction of two expressions. + /// Creates expression that represents subtraction of two expressions. /// /// First expression. /// Second expression. /// Result expression. /// - protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) - { - return SqlDml.Modulo( + protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) => + SqlDml.Modulo( NanosecondsPerDay + CastToDecimal(DateDiffMillisecond(time2, time1), 18,0) * NanosecondsPerMillisecond, NanosecondsPerDay); - } #endif private SqlExpression GenericPad(SqlFunctionCall node) @@ -593,8 +589,8 @@ protected static SqlUserFunctionCall DateAddSecond(SqlExpression date, SqlExpres protected static SqlUserFunctionCall DateAddMillisecond(SqlExpression date, SqlExpression milliseconds) => SqlDml.FunctionCall("DATEADD", SqlDml.Native(MillisecondPart), milliseconds, date); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected static SqlUserFunctionCall TimeToString(SqlExpression time) => SqlDml.FunctionCall("CONVERT", SqlDml.Native("NVARCHAR(16)"), time, SqlDml.Native("114")); diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs index ca3f1e5cc2..325da57b5a 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs @@ -254,8 +254,7 @@ public override DataTypeCollection GetDataTypesInfo() types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, new ValueRange(new DateTime(1753, 1, 1), new DateTime(9999, 12, 31)), "datetime", "smalldatetime"); - -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, new ValueRange(new DateOnly(1, 1, 1), new DateOnly(9999, 12, 31)), "date"); types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, new ValueRange(TimeOnly.MinValue, TimeOnly.MaxValue), "time"); #endif diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs index 620b68881b..534562c877 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Translator.cs @@ -20,7 +20,7 @@ namespace Xtensive.Sql.Drivers.SqlServer.v09 internal class Translator : SqlTranslator { public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fff\'' as datetime)'"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER public override string DateOnlyFormatString => @"'cast ('\'yyyy\-MM\-dd\'' as date)'"; public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fff\'' as time)'"; #endif @@ -571,7 +571,7 @@ public override void Translate(SqlCompilerContext context, object literalValue) case long v: _ = output.Append($"CAST({v} as BIGINT)"); break; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case DateOnly dateOnly: var dateOnlyRange = (ValueRange) Driver.ServerInfo.DataTypes.DateTime.ValueRange; var newDateOnly = ValueRangeValidator.Correct(dateOnly.ToDateTime(TimeOnly.MinValue), dateOnlyRange).Date; diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs index e599171baa..9b167b4277 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs @@ -135,7 +135,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeOffsetToDateTime: DateTimeOffsetToDateTime(node.Arguments[0]).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateToDateTimeOffset: DateToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this); return; @@ -219,8 +219,8 @@ private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) => private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) => SqlDml.Cast(dateTimeOffset, SqlType.DateTime); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY private static SqlExpression DateToDateTimeOffset(SqlExpression date) => DateTimeToDateTimeOffset(SqlDml.Cast(date, SqlType.DateTime)); @@ -233,6 +233,7 @@ private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset)= private static SqlExpression DateTimeOffsetToDate(SqlExpression dateTimeOffset) => SqlDml.Cast(dateTimeOffset, SqlType.Date); #endif + #endregion // Constructors diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs index 560a77e3ed..47ff925a90 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs @@ -34,8 +34,7 @@ public override DataTypeCollection GetDataTypesInfo() new ValueRange(new DateTimeOffset(1, 1, 1, 0, 0, 0, 0, new TimeSpan(0)), new DateTimeOffset(9999, 12, 31, 0, 0, 0, 0, new TimeSpan(0))), "datetimeoffset"); - -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)), "datetime2", "datetime", "smalldatetime"); diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs index 14b0eb1507..43acc916e7 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Translator.cs @@ -17,7 +17,7 @@ internal class Translator : v09.Translator /// public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fffffff\'' as datetime2)'"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER /// public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fffffff\'' as time)'"; #endif diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs index 351dfe4c2b..be5c543cd3 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs @@ -50,19 +50,10 @@ public override void Visit(SqlFunctionCall node) switch (node.FunctionType) { case SqlFunctionType.DateTimeConstruct: Visit(SqlDml.FunctionCall("DATETIME2FROMPARTS", arguments[0], arguments[1], arguments[2], 0, 0, 0, 0, 7)); - //Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)), - // arguments[0] - 2001), - // arguments[1] - 1), - // arguments[2] - 1)); return; #if NET6_0_OR_GREATER case SqlFunctionType.DateConstruct: { Visit(SqlDml.FunctionCall("DATEFROMPARTS", arguments[0], arguments[1], arguments[2])); - - //Visit(SqlDml.Cast(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)), - // arguments[0] - 2001), - // arguments[1] - 1), - // arguments[2] - 1), SqlType.Date)); return; } case SqlFunctionType.TimeConstruct: { @@ -70,11 +61,6 @@ public override void Visit(SqlFunctionCall node) // default precision of time is 7, and if we use raw argument[3] value the result will be .0000xxx, // to prevent this and make fractions part valid .xxx0000 we multiply Visit(SqlDml.FunctionCall("TIMEFROMPARTS", arguments[0], arguments[1], arguments[2], arguments[3] * 10000, 7)); - //Visit(SqlDml.Cast(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Literal(new TimeOnly(0, 0, 0)), - // arguments[0]), - // arguments[1]), - // arguments[2]), - // arguments[3]), SqlType.Time)); return; } #endif diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs index f706181aca..938e0d4a82 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v13/Compiler.cs @@ -39,11 +39,13 @@ public override void Visit(SqlFunctionCall node) // so we should handle it by this big formula Visit(CastToLong(DateTimeSubtractDateTimeExpensive(binary.Left, binary.Right))); } - else if (binary.NodeType is SqlNodeType.TimeMinusTime) + else if (binary.NodeType is SqlNodeType.TimeMinusTime) { //but for time it is OK Visit(DateDiffBigMicrosecond(binary.Right, binary.Left)); - else + } + else { base.Visit(node); + } } else { base.Visit(node); diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index 9f6adf4f1f..b00bcffaf5 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -18,7 +18,7 @@ internal class Compiler : SqlCompiler { private const long NanosecondsPerMillisecond = 1000000L; private const string DateWithZeroTimeFormat = "%Y-%m-%d 00:00:00.000"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private const string DateFormat = "%Y-%m-%d"; private const string TimeFormat = "%H:%M:%f0000"; private const string TimeToStringFormat = "%H:%M:%f0000"; @@ -78,7 +78,7 @@ public override void Visit(SqlBinary node) DateTimeOffsetExtractOffsetAsString(node.Left)) .AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlNodeType.TimePlusInterval: TimeAddInterval(node.Left, node.Right).AcceptVisitor(this); return; @@ -135,8 +135,8 @@ public override void Visit(SqlExtract node) VisitDateTimeOffset(node); return; } -#if NET6_0_OR_GREATER //DO_DATEONLY - if(node.IsTimePart) { +#if NET6_0_OR_GREATER + if (node.IsTimePart) { VisitTime(node); return; } @@ -196,7 +196,7 @@ public override void Visit(SqlFunctionCall node) arguments[1] - 1), arguments[2] - 1).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateAddYears: DateAddYear(arguments[0], arguments[1]).AcceptVisitor(this); return; @@ -267,7 +267,7 @@ public override void Visit(SqlFunctionCall node) case SqlFunctionType.DateTimeOffsetToDateTime: DateTimeOffsetToDateTime(arguments[0]).AcceptVisitor(this); return; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case SqlFunctionType.DateTimeToDate: DateTimeToDate(arguments[0]).AcceptVisitor(this); return; @@ -412,8 +412,8 @@ private void VisitDateTime(SqlExtract node) } base.Visit(node); } - #if NET6_0_OR_GREATER //DO_DATEONLY + private void VisitTime(SqlExtract node) { if (node.IsMillisecondExtraction) { @@ -528,8 +528,8 @@ private static SqlExpression DateTimeAddDay(SqlExpression date, SqlExpression da private static SqlExpression DateTimeAddSeconds(SqlExpression date, SqlExpression seconds) => SqlDml.FunctionCall("STRFTIME", DateTimeFormat, date, SqlDml.Concat(seconds, " ", "SECONDS")); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER // DO_DATEONLY private static SqlExpression DateAddYear(SqlExpression date, SqlExpression years) => SqlDml.FunctionCall("STRFTIME", DateFormat, date, SqlDml.Concat(years, " ", "YEARS")); @@ -610,7 +610,6 @@ private static SqlExpression DateOrTimeGetTotalSeconds(SqlExpression date) => private static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2) => (((DateOrTimeGetTotalSeconds(date1) - DateOrTimeGetTotalSeconds(date2)) * MillisecondsPerSecond) + DateOrTimeGetMilliseconds(date1) - DateOrTimeGetMilliseconds(date2)) * NanosecondsPerMillisecond; - private static SqlExpression ServerOffsetAsString() { diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs index 523e03f644..7db0490b0c 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/ServerInfoProvider.cs @@ -228,9 +228,10 @@ public override DataTypeCollection GetDataTypesInfo() types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index, ValueRange.DateTime, "datetime"); types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index, ValueRange.DateTimeOffset, "datetimeoffset"); - #if NET6_0_OR_GREATER + types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, ValueRange.DateOnly, "date"); + types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, ValueRange.TimeOnly, "time"); #endif diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs index bd609f31c4..c59e56208a 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Translator.cs @@ -22,7 +22,7 @@ internal class Translator : SqlTranslator /// public override string DateTimeFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss.fff\'"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER /// public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'"; @@ -346,8 +346,8 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart) default: base.Translate(output, dateTimePart); break; } } - #if NET6_0_OR_GREATER + /// public override void Translate(IOutput output, SqlDatePart dateTimePart) { diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs index 0a491fa8dd..ef72270a5c 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/TypeMapper.cs @@ -33,8 +33,8 @@ public override object ReadBoolean(DbDataReader reader, int index) var value = reader.GetDecimal(index); return SQLiteConvert.ToBoolean(value); } - #if NET6_0_OR_GREATER + public override object ReadDateOnly(DbDataReader reader, int index) { var value = reader.GetString(index); @@ -88,8 +88,8 @@ public override void BindDateTime(DbParameter parameter, object value) var correctValue = ValueRangeValidator.Correct((DateTime) value, dateTimeRange); parameter.Value = correctValue.ToString(DateTimeFormat, CultureInfo.InvariantCulture); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public override void BindDateOnly(DbParameter parameter, object value) { parameter.DbType = DbType.String; diff --git a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs index 08e74502a9..8ac5e363c5 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs +++ b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs @@ -122,7 +122,7 @@ public static DateTime AdjustDateTime(this DateTime origin, byte desiredFraction return new DateTime(newTicks); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER /// /// Cuts down resolution of value if needed, according to current . /// diff --git a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs index 4b46ee311a..5b6ed42d5b 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs @@ -15,8 +15,8 @@ public abstract class DateTimeIntervalTest : SqlTest { protected static readonly DateTime DefaultDateTime = new DateTime(2001, 2, 3, 4, 5, 6, 334); protected static readonly DateTime SecondDateTime = new DateTime(2000, 12, 11, 10, 9, 8, 765); - #if NET6_0_OR_GREATER + protected static readonly DateOnly DefaultDateOnly = new DateOnly(2001, 2, 3); protected static readonly DateOnly SecondDateOnly = new DateOnly(2000, 12, 11); @@ -155,8 +155,8 @@ public virtual void DateTimeExtractDayOfYearTest() SqlDml.Extract(SqlDateTimePart.DayOfYear, DefaultDateTime), DefaultDateTime.DayOfYear); } - #if NET6_0_OR_GREATER + [Test] public virtual void DateOnlyAddYearsTest() { diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs index f9787818c4..cf9b41ccc8 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs @@ -439,7 +439,7 @@ private void PopulateTypeToColumnName() TypeToColumnName[SqlType.Interval] = "interval_column"; TypeToColumnName[SqlType.DateTime] = "datetime_column"; TypeToColumnName[SqlType.DateTimeOffset] = "datetimeoffset_column"; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER TypeToColumnName[SqlType.Date] = "date_column"; TypeToColumnName[SqlType.Time] = "time_column"; #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs index 9024f5258c..3d164fb888 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs @@ -31,7 +31,7 @@ protected override string GetTypesExtractionPrepareScript(string tableName) sb.AppendLine($"{TypeToColumnName[SqlType.Double]} double precision,"); sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} timestamp,"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER sb.AppendLine($"{TypeToColumnName[SqlType.Date]} date,"); sb.AppendLine($"{TypeToColumnName[SqlType.Time]} time,"); #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs index a6c8cef1ce..c4d01ff879 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs @@ -33,7 +33,7 @@ protected override string GetTypesExtractionPrepareScript(string tableName) sb.AppendLine($"{TypeToColumnName[SqlType.Double]} double precision NULL,"); sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} datetime NULL,"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER sb.AppendLine($"{TypeToColumnName[SqlType.Date]} date NULL,"); sb.AppendLine($"{TypeToColumnName[SqlType.Time]} time NULL,"); #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs index 241c3abc9e..326c474fe9 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs @@ -35,7 +35,7 @@ protected override string GetTypesExtractionPrepareScript(string tableName) sb.AppendLine($"\"{TypeToColumnName[SqlType.Interval]}\" interval day to second NULL,"); sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTime]}\" timestamp NULL,"); sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTimeOffset]}\" TIMESTAMP WITH TIME ZONE NULL,"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER sb.AppendLine($"\"{TypeToColumnName[SqlType.Date]}\" date NULL,"); sb.AppendLine($"\"{TypeToColumnName[SqlType.Time]}\" interval day(0) to second NULL,"); #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs index 227fcca185..c6540638e9 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs @@ -46,7 +46,7 @@ protected override string GetTypesExtractionPrepareScript(string tableName) sb.AppendLine($"\"{TypeToColumnName[SqlType.Interval]}\" interval NULL,"); sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTime]}\" timestamp NULL,"); sb.AppendLine($"\"{TypeToColumnName[SqlType.DateTimeOffset]}\" timestamptz NULL,"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER sb.AppendLine($"\"{TypeToColumnName[SqlType.Date]}\" date NULL,"); sb.AppendLine($"\"{TypeToColumnName[SqlType.Time]}\" time NULL,"); #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs index b0008246e3..be0bc899cb 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs @@ -96,8 +96,8 @@ public void DateTimePartsValueTest() Assert.That((int) SqlDateTimePart.DayOfWeek, Is.EqualTo(11)); Assert.That((int) SqlDateTimePart.Nothing, Is.EqualTo(25)); } - #if NET6_0_OR_GREATER + [Test] public void DatePartsValueTest() { diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs index 8a2ac63af2..1924be18a7 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs @@ -36,7 +36,7 @@ protected override string GetTypesExtractionPrepareScript(string tableName) else sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} [datetime] NULL,"); sb.AppendLine($"{TypeToColumnName[SqlType.DateTimeOffset]} [datetimeoffset] NULL,"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER sb.AppendLine($"{TypeToColumnName[SqlType.Date]} [date] NULL,"); sb.AppendLine($"{TypeToColumnName[SqlType.Time]} [time] NULL,"); #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs index d422b3fcd2..4e914df9b2 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs @@ -37,7 +37,7 @@ protected override string GetTypesExtractionPrepareScript(string tableName) sb.AppendLine($"{TypeToColumnName[SqlType.DateTime]} [datetime] NULL,"); sb.AppendLine($"{TypeToColumnName[SqlType.DateTimeOffset]} [datetimeoffset] NULL,"); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER sb.AppendLine($"{TypeToColumnName[SqlType.Date]} [date] NULL,"); sb.AppendLine($"{TypeToColumnName[SqlType.Time]} [time] NULL,"); #endif diff --git a/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs b/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs index a999810dd9..f278df1a8b 100644 --- a/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs +++ b/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs @@ -258,7 +258,7 @@ private static object[] GetTestValues(Type type) new DateTimeOffset(2001, 1, 1, 1, 1, 1, 1, new TimeSpan(3, 10, 0)), null }; -#if NET6_0_OR_GREATER // DO_DATEONLY +#if NET6_0_OR_GREATER if (type == typeof(DateOnly)) { return new object[] { new DateOnly(2005, 5, 5), diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs index a8f26954f1..af6e8e5340 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ComparisonTestDateOnly.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using NUnit.Framework; using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs index d2524233f8..d26665b263 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs index 3ac2874baa..67265a1d02 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs @@ -2,10 +2,8 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER -using System; -using System.Linq.Expressions; using NUnit.Framework; using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs index 1648faad35..deee0f4fe4 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DistinctTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs index b0ab715b8b..5c34ce186f 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/GroupByTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs index 4696440319..3730f82f61 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/JoinTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs index 9ce9613ac9..c16b7406d6 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/MinMaxTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs index 58f3f52145..3e7820bae5 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OperationsTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using NUnit.Framework; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs index b567a9c6bb..070b4e7051 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/OrderByTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs index 2a16963d09..2a2031a374 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.cs @@ -1,10 +1,8 @@ -// Copyright (C) 2016-2022 Xtensive LLC. +// Copyright (C) 2023 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -// Created by: Alex Groznov -// Created: 2016.08.01 -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using NUnit.Framework; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs index 96570cfc42..30115b9c0c 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.cs @@ -2,14 +2,13 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; using System.Linq.Expressions; using NUnit.Framework; using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; -using Xtensive.Orm.Tests.ObjectModel.Interfaces.Alphabet; namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys { diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs index d95fe15ff0..66d42a5ce1 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTimeBaseTest.cs @@ -24,8 +24,8 @@ public abstract class DateTimeBaseTest : BaseTest protected static readonly DateTime FirstMillisecondDateTime = FirstDateTime.AddMilliseconds(321); protected static readonly DateTime SecondMillisecondDateTime = SecondDateTime.AddMilliseconds(987); protected static readonly DateTime WrongMillisecondDateTime = WrongDateTime.AddMilliseconds(654); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY protected static readonly DateOnly FirstDateOnly = DateOnly.FromDateTime(FirstDateTime); protected static readonly DateOnly SecondDateOnly = DateOnly.FromDateTime(SecondDateTime); protected static readonly DateOnly NullableDateOnly = DateOnly.FromDateTime(SecondDateTime); @@ -63,8 +63,8 @@ protected override void PopulateEntities(Session session) MillisecondDateTime = FirstMillisecondDateTime, NullableDateTime = NullableDateTime }; - #if NET6_0_OR_GREATER + _ = new SingleDateOnlyEntity(session) { DateOnly = FirstDateOnly, NullableDateOnly = NullableDateOnly, @@ -149,7 +149,6 @@ protected override void PopulateEntities(Session session) MillisecondTimeOnly = FirstMillisecondTimeOnly.Add(new TimeSpan(0, 0,-5)), NullableTimeOnly = NullableTimeOnly.Add(new TimeSpan(0, 0,-5)), }; - #endif _ = new DateTimeEntity(session, FirstDateTime); diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs index 1ad9737841..40cea806fd 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/Model.cs @@ -243,8 +243,8 @@ private AllPossiblePartsEntity(Session session) { } } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY [HierarchyRoot] public class DateOnlyEntity : Entity { @@ -300,6 +300,5 @@ public SingleTimeOnlyEntity(Session session) { } } - #endif } diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs index a98be05fe8..1b10a3a4d7 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ComparisonTestTimeOnly.cs @@ -2,12 +2,11 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using NUnit.Framework; using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model; -using Xtensive.Orm.Tests.ObjectModel.Interfaces.Alphabet; namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.TimeOnlys { diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs index 2828f5f68e..3c9f91931c 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/ConstructorTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs index da16f8cedf..5e93720af5 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/DistinctTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs index 7743a2e6ca..95a39c580d 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/GroupByTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs index bb6f1227c3..56b718d852 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/JoinTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs index 56efa319a4..d57c7cfe9b 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/MinMaxTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs index baaecc212a..b2d503d660 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OperationsTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using NUnit.Framework; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs index 39b74d5697..0cc6f27a47 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/OrderByTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs index 5c1d96db34..082bc272d5 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/PartsExtractionTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using NUnit.Framework; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs index 7b507e0c4c..b204eeabcb 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/TimeOnlyToString.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq.Expressions; diff --git a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs index b6e3c91112..203811c1b2 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/TimeOnly/WhereTest.cs @@ -2,7 +2,7 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER using System; using System.Linq; diff --git a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs index 4b14b36447..196de31bca 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/DateTimeStoragePrecisionTest.cs @@ -19,7 +19,7 @@ public class TestEntity : Entity [Field] public DateTime FDateTime { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER [Field] public TimeOnly FTimeOnly { get; set; } @@ -74,8 +74,8 @@ public void DateTimeTest() Assert.That(entity.FDateTime, Is.EqualTo(GetExpectedValue(dateTime))); } } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY [Test] public void TimeOnlyTest() { @@ -90,8 +90,8 @@ public void TimeOnlyTest() } private static TimeOnly GetExpectedValue(in TimeOnly baseTimeOnly) => baseTimeOnly.AdjustTimeOnlyForCurrentProvider(); - #endif + private static DateTime GetExpectedValue(in DateTime baseDateTime) { var provider = StorageProviderInfo.Instance.Provider; diff --git a/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs b/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs index 2f906b3cac..e285c199b6 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/FieldDefaultValueTest.cs @@ -145,15 +145,15 @@ public class X : Entity [Field(DefaultValue = "2012.12.12")] public DateTime FDateTime { get; set; } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY [Field(DefaultValue = "2012.12.12")] public DateOnly FDateOnly { get; set; } [Field(DefaultValue = "00:35:53.35")] public TimeOnly FTimeOnly { get; set; } - #endif + [Field(DefaultValue = 1000)] public TimeSpan FTimeSpan { get; set; } @@ -231,7 +231,7 @@ public class X : Entity [Field(DefaultValue = "2012.12.12")] public DateTime? FNDateTime { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER [Field(DefaultValue = "2012.12.12")] public DateOnly? FNDateOnly { get; set; } @@ -348,11 +348,11 @@ public void DefaultValuesTest() Assert.AreEqual(byte.MaxValue, x.FByte); Assert.AreEqual(DateTime.Parse("2012.12.12"), x.FDateTime); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER Assert.AreEqual(DateOnly.Parse("2012.12.12"), x.FDateOnly); Assert.AreEqual(TimeOnly.Parse("00:35:53.35"), x.FTimeOnly); -#endif +#endif Assert.AreEqual(12.12M, x.FDecimal); Assert.AreEqual(float.MaxValue, x.FDouble); Assert.AreEqual(EByte.Max, x.FEByte); diff --git a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs index 83513a07b9..49ec9306aa 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SchemaSharing/EntityManipulation/Model.cs @@ -19,14 +19,6 @@ public class TestEntity1 : Entity [Field] public string Text { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY - [Field] - public DateOnly DateOnly { get; set; } - - [Field] - public TimeOnly TimeOnly { get; set; } -#endif - [Field] public string DatabaseName { get; set; } diff --git a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs index 82ea6a7cac..eaa4b371d8 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/SetFieldTest.cs @@ -25,11 +25,14 @@ public class Book : Entity [Field] public DateTime Date { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER [Field] public DateOnly DateOnly { get; set; } -#endif + [Field] + public TimeOnly TimeOnly { get; set; } + +#endif [Field] public Direction? NullableDirection { get; set; } @@ -66,10 +69,12 @@ public void CombinedTest() AssertIsNotCalled(() => { book.Title = "A"; }); AssertIsCalled(() => { book.Date = new DateTime(1, 2, 3); }); AssertIsNotCalled(() => { book.Date = new DateTime(1, 2, 3); }); - #if NET6_0_OR_GREATER //DO_DATEONLY + AssertIsCalled(() => { book.DateOnly = new DateOnly(1, 2, 3); }); AssertIsNotCalled(() => { book.DateOnly = new DateOnly(1, 2, 3); }); + AssertIsCalled(() => { book.TimeOnly = new TimeOnly(1, 2, 3); }); + AssertIsNotCalled(() => { book.TimeOnly = new TimeOnly(1, 2, 3); }); #endif var image = new byte[] { 1, 2, 3 }; diff --git a/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs b/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs index 9146a690b8..6eafe43715 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/TypeCompatibilityTest.cs @@ -113,7 +113,7 @@ public class X : Entity [Field] public DateTime FDateTime { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER [Field] public DateOnly FDateOnly { get; set; } @@ -206,7 +206,7 @@ public class X : Entity [Field] public DateTime? FNDateTime { get; set; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER [Field] public DateOnly? FNDateOnly { get; set; } @@ -294,7 +294,7 @@ public void DefaultValuesTest() var dataTypeInfo = sqlDriver.ServerInfo.DataTypes.DateTime; var dateTimeMinValue = ((ValueRange) dataTypeInfo.ValueRange).MinValue; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER dataTypeInfo = sqlDriver.ServerInfo.DataTypes.DateOnly; var dateOnlyMinValue = ((ValueRange) dataTypeInfo.ValueRange).MinValue; @@ -308,7 +308,7 @@ public void DefaultValuesTest() Assert.AreEqual(0, x.FByte); Assert.AreEqual(null, x.FByteArray); Assert.AreEqual(dateTimeMinValue, x.FDateTime); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER Assert.AreEqual(dateOnlyMinValue, x.FDateOnly); Assert.AreEqual(timeOnlyMinValue, x.FTimeOnly); #endif @@ -339,7 +339,7 @@ public void DefaultValuesTest() Assert.AreEqual(null, x.FNBool); Assert.AreEqual(null, x.FNByte); Assert.AreEqual(null, x.FNDateTime); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER Assert.AreEqual(null, x.FNDateOnly); Assert.AreEqual(null, x.FNTimeOnly); #endif diff --git a/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs b/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs index e4abdaf027..cf625c0a47 100644 --- a/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs +++ b/Orm/Xtensive.Orm.Tests/Upgrade/DateTimeDataConversions.cs @@ -113,8 +113,8 @@ public void DateTimeOffsetToDateTimeTest() UpgradeAndTestData(expectedValue); } - #if NET6_0_OR_GREATER + [Test] public void DateTimeToDateOnlyTest() { @@ -414,8 +414,8 @@ protected override void AddUpgradeHints(ISet hints) _ = hints.Add(ChangeFieldTypeHint.Create(e => e.ConversionField)); } } - #if NET6_0_OR_GREATER + public class DateTimeToDateOnlyUpgradeHandler : UpgradeHandler { public override bool CanUpgradeFrom(string oldVersion) => true; @@ -526,5 +526,4 @@ protected override void AddUpgradeHints(ISet hints) } } #endif -} - +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs b/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs index 6e58d162dd..a58655cf42 100644 --- a/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs +++ b/Orm/Xtensive.Orm/Linq/ExpressionWriter.cs @@ -361,7 +361,7 @@ protected override Expression VisitConstant(ConstantExpression c) Write(c.Value.ToString()); Write("\")"); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER else if (type == WellKnownTypes.DateOnly) { Write("DateOnly.Parse(\""); Write(c.Value.ToString()); diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs index b8b232fc83..a520be565c 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/ValueTypeBuilder.cs @@ -58,7 +58,7 @@ private static object AdjustValue(string fieldName, Type fieldType, object value timespan = TimeSpan.FromTicks(ticks); return timespan; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER if (valueType == WellKnownTypes.TimeOnly) { if (value is string timeOnlyString && !TimeOnly.TryParse(timeOnlyString, out var timeOnly)) { diff --git a/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs b/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs index 96f9d736e4..b33652de0e 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ExpressionExtensions.cs @@ -47,7 +47,7 @@ public static bool IsNewExpressionSupportedByStorage(this Expression expression) expression.NodeType == ExpressionType.New && expression.Type switch { var t => t == WellKnownTypes.TimeSpan || t == WellKnownTypes.DateTime || t == WellKnownTypes.DateTimeOffset -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER || t == WellKnownTypes.DateOnly || t == WellKnownTypes.TimeOnly #endif }; diff --git a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs index 03751f5eac..e6085a193e 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs @@ -102,7 +102,7 @@ private static bool TypeIsStorageMappable(Type type) type == WellKnownTypes.String || type == WellKnownTypes.DateTime || type == WellKnownTypes.DateTimeOffset || -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER type == WellKnownTypes.DateOnly || type == WellKnownTypes.TimeOnly || #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs b/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs index 38c78f0255..f60d262e03 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/DbCommandExtensions.cs @@ -36,7 +36,7 @@ public static string ToHumanReadableString(this DbCommand command) case DateTimeOffset dateTimeOffset: value = dateTimeOffset.ToString("yyyy-MM-dd HH:mm:ss.fffffffK"); break; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case DateOnly dateOnly: value = dateOnly.ToString("yyyy-MM-dd"); break; diff --git a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs index 04b2fbb53d..baad2dc491 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs @@ -128,7 +128,7 @@ protected virtual IEnumerable GetProviderCompilerContainers() typeof (StringCompilers), typeof (DateTimeCompilers), typeof (DateTimeOffsetCompilers), -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER typeof (DateOnlyCompilers), typeof (TimeOnlyCompilers), #endif diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs index 4cbcd4a757..9e68fa0fc1 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs @@ -215,7 +215,7 @@ private static bool IsBooleanExpression(Expression expression) => private static bool IsDateTimeExpression(Expression expression) => IsExpressionOf(expression, WellKnownTypes.DateTime); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private static bool IsDateOnlyExpression(Expression expression) => IsExpressionOf(expression, WellKnownTypes.DateOnly); diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs index 710578115d..13bc64c11a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionTranslationHelpers.cs @@ -45,8 +45,8 @@ public static SqlExpression ToChar(SqlExpression target) => public static SqlExpression ToDateTime(SqlExpression target) => Cast(target, WellKnownTypes.DateTime); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public static SqlExpression ToDate(SqlExpression target) => Cast(target, WellKnownTypes.DateOnly); diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs index 423fa550e8..fc622c27cc 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Helpers.cs @@ -307,7 +307,7 @@ private SqlExpression GetOrderByExpression(SqlExpression expression, SortProvide if (columnType == WellKnownTypes.DateTime) { return SqlDml.Cast(expression, SqlType.DateTime); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER if (columnType == WellKnownTypes.DateOnly) { return SqlDml.Cast(expression, SqlType.Date); } @@ -355,7 +355,7 @@ static SqlExpression CastToDateTimeVariantIfNeeded(SqlExpression expression,Type if (type == WellKnownTypes.DateTime) { sqlType = SqlType.DateTime; } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER else if (type == WellKnownTypes.DateOnly) { sqlType = SqlType.Date; } diff --git a/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs b/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs index 7a57ca3417..5c42204c5c 100644 --- a/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs +++ b/Orm/Xtensive.Orm/Orm/Upgrade/Internals/SqlActionTranslator.cs @@ -943,7 +943,7 @@ private static SqlExpression GetDataConversion(SqlValueType oldType, SqlValueTyp if (oldSqlType == SqlType.DateTimeOffset && newSqlType == SqlType.DateTime) { return SqlDml.DateTimeOffsetToDateTime(sqlTableColumn); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER if (oldSqlType == SqlType.DateTime && newSqlType == SqlType.Date) { return SqlDml.DateTimeToDate(sqlTableColumn); } @@ -976,7 +976,7 @@ private static SqlExpression GetDataConversion(SqlValueType oldType, SqlValueTyp return SqlDml.Cast(sqlTableColumn, newType); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private static bool IsDateTimeType(in SqlType type) { return type == SqlType.DateTime diff --git a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs index ee4471298d..92631fb1a5 100644 --- a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs +++ b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs @@ -57,12 +57,13 @@ internal static class WellKnownTypes public static readonly Type NullableDateTime = typeof(DateTime?); public static readonly Type DateTimeOffset = typeof(DateTimeOffset); public static readonly Type NullableDateTimeOffset = typeof(DateTimeOffset?); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER public static readonly Type DateOnly = typeof(DateOnly); public static readonly Type TimeOnly = typeof(TimeOnly); public static readonly Type NullableDateOnly = typeof(DateOnly?); public static readonly Type NullableTimeOnly = typeof(TimeOnly?); #endif + public static readonly Type Guid = typeof(Guid); public static readonly Type NullableGuid = typeof(Guid?); diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs index 80a51014be..a2e7ae8570 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs @@ -1533,7 +1533,7 @@ public virtual void Translate(SqlCompilerContext context, object literalValue) case Guid: case byte[]: throw new NotSupportedException(string.Format(Strings.ExTranslationOfLiteralOfTypeXIsNotSupported, literalType.GetShortName())); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER case DateOnly dateOnly: output.Append(dateOnly.ToString(DateOnlyFormatString, DateTimeFormat)); break; diff --git a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs index a988cf839a..3f48159789 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExpression.cs @@ -204,8 +204,8 @@ public static implicit operator SqlExpression(DateTime value) { return new SqlLiteral(value); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public static implicit operator SqlExpression(DateOnly value) { return new SqlLiteral(value); diff --git a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs index f886cb7776..972995fc38 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs @@ -25,15 +25,15 @@ public class SqlExtract : SqlExpression public SqlDateTimePart DateTimePart => typeMarker == DateTimeTypeId ? internalValue.ToDateTimePartFast() : SqlDateTimePart.Nothing; - #if NET6_0_OR_GREATER + public SqlDatePart DatePart => typeMarker == DateTypeId ? internalValue.ToDatePartFast() : SqlDatePart.Nothing; public SqlTimePart TimePart => typeMarker == TimeTypeId ? internalValue.ToTimePartFast() : SqlTimePart.Nothing; - #endif + public SqlDateTimeOffsetPart DateTimeOffsetPart => typeMarker == DateTimeOffsetTypeId ? internalValue : SqlDateTimeOffsetPart.Nothing; @@ -50,8 +50,8 @@ public class SqlExtract : SqlExpression public bool IsDateTimeOffsetPart => typeMarker == DateTimeOffsetTypeId; public bool IsDateTimePart => typeMarker == DateTimeTypeId; - #if NET6_0_OR_GREATER + public bool IsDatePart => typeMarker == DateTypeId; public bool IsTimePart => typeMarker == TimeTypeId; @@ -120,14 +120,10 @@ internal SqlExtract(SqlDateTimeOffsetPart dateTimeOffsetPart, SqlExpression oper internalValue = dateTimeOffsetPart; typeMarker = DateTimeOffsetTypeId; typeHasTime = true; - - //DateTimePart = SqlDateTimePart.Nothing; - //IntervalPart = SqlIntervalPart.Nothing; - //DateTimeOffsetPart = dateTimeOffsetPart; Operand = operand; } - #if NET6_0_OR_GREATER + internal SqlExtract(SqlDatePart datePart, SqlExpression operand) : base(SqlNodeType.Extract) { diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs index 5984b6bf6f..05a696c0d0 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlFunctionType.cs @@ -60,7 +60,7 @@ public enum SqlFunctionType // date time / interval functions // not ansi sql but our cross-server solution -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER DateConstruct, DateAddYears, DateAddMonths, @@ -81,7 +81,7 @@ public enum SqlFunctionType DateTimeAddMonths, DateTimeTruncate, DateTimeToStringIso, -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER DateTimeToTime, DateTimeToDate, #endif @@ -100,7 +100,7 @@ public enum SqlFunctionType DateTimeOffsetToUtcTime, DateTimeToDateTimeOffset, DateTimeOffsetToDateTime, -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER DateTimeOffsetToTime, DateTimeOffsetToDate, #endif diff --git a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs index d9131da167..fd5b4a3560 100644 --- a/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Info/DataTypeCollection.cs @@ -143,8 +143,8 @@ public void Add(SqlType sqlType, DataTypeInfo dataTypeInfo) /// A representation of the interval data type. /// public DataTypeInfo Interval { get; set; } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY /// /// Date data from January 1,1 A.D. through December 31, 9999 A.D. /// Can have various ranges in different RDBMSs. @@ -249,7 +249,7 @@ IEnumerator IEnumerable.GetEnumerator() yield return VarBinaryMax; yield return Guid; yield return Interval; -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER yield return DateOnly; yield return TimeOnly; #endif diff --git a/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs b/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs index d1c9e81916..7d0e896580 100644 --- a/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs +++ b/Orm/Xtensive.Orm/Sql/Info/ValueRange.Types.cs @@ -78,7 +78,7 @@ public partial class ValueRange /// public static readonly ValueRange TimeSpan = new ValueRange(System.TimeSpan.MinValue, System.TimeSpan.MaxValue); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER /// /// Standard value range for /// diff --git a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs index 499ca9f999..e22c0820d7 100644 --- a/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs +++ b/Orm/Xtensive.Orm/Sql/Internals/SqlValidator.cs @@ -29,7 +29,7 @@ internal static class SqlValidator WellKnownTypes.Double, WellKnownTypes.Decimal, WellKnownTypes.DateTime, -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER WellKnownTypes.DateOnly, WellKnownTypes.TimeOnly, #endif diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index cf5405d6d3..edd801eca3 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -544,13 +544,13 @@ public static SqlExtract Extract(SqlDateTimePart part, SqlExpression operand) } return new SqlExtract(part, operand); } - #if NET6_0_OR_GREATER + public static SqlExtract Extract(SqlDatePart part, SqlExpression operand) { ArgumentNullException.ThrowIfNull(operand, nameof(operand)); SqlValidator.EnsureIsArithmeticExpression(operand); - if(part==SqlDatePart.Nothing) { + if (part == SqlDatePart.Nothing) { throw new ArgumentException(string.Format("Unable to extract {0} part", SqlDatePart.Nothing.ToString())); } return new SqlExtract(part, operand); @@ -587,20 +587,7 @@ public static SqlFunctionCall DateTimeConstruct(SqlExpression year, SqlExpressio SqlValidator.EnsureIsArithmeticExpression(day); return new SqlFunctionCall(SqlFunctionType.DateTimeConstruct, year, month, day); } - -#if NET6_0_OR_GREATER //DO_DATEONLY - - // SQL Server - DATEFROMPARTS(y, m, d, fractions, precision) https://learn.microsoft.com/en-us/sql/t-sql/functions/datefromparts-transact-sql?view=sql-server-2016 - // Mysql - MAKEDATE(year, dayofyear), propably. https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html - // Oracle - if x is the number of seconds after midnight on January 1, 1970: TO_DATE( '01/01/1970', 'MM/DD/YYYY') + (x / (24 * 60 * 60)) - // In the examples above, x can be any number, not necessarily an integer. - //https://community.oracle.com/tech/developers/discussion/958492/how-to-convert-integer-to-date - // PgSql - some magic with string concatination or by using to_timestampt(ticks-in-some-form::numeric)::time https://stackoverflow.com/questions/26198358/postgresql-cast-numeric-to-date-and-format - // https://stackoverflow.com/questions/22835874/postgresql-convert-timestamp-to-time-or-retrieve-only-time-from-timestamp-col - // from 9.4 make_date(year, month, day) - - // firebird - dateadd(second, AmountOfSeconds, cast('00:00:00' as time)) - // sqlite - probably date(seconds-form-unixepoch, 'unixepoch') +#if NET6_0_OR_GREATER public static SqlFunctionCall DateConstruct(SqlExpression year, SqlExpression month, SqlExpression day) { @@ -613,14 +600,6 @@ public static SqlFunctionCall DateConstruct(SqlExpression year, SqlExpression mo return new SqlFunctionCall(SqlFunctionType.DateConstruct, year, month, day); } - // SQL Server - TIMEFROMPARTS(h, m, s, fractions, precision) https://learn.microsoft.com/en-us/sql/t-sql/functions/timefromparts-transact-sql?view=sql-server-2016 - // Mysql - addtime (MAKETIME(h, m, s), '00:00:00.), propably. https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html - // Oracle - numtodsinterval(value-in-seconds.milliseconds, 'second') //https://docs.oracle.com/database/121/SQLRF/functions129.htm#SQLRF00682 - // PgSql - see current impl of DateConstruct for inspiration :) - // from 9.4 there is make_time(h, m,s) - // firebird - dateadd(second, ColAmountOfSeconds, cast('00:00:00' as time)) - // sqlite - time('00:00:00', '+ NNN.NNNN seconds' //https://codernotes.ru/articles/bazy-dannyh-t-sql/funkcii-daty-i-vremeni-v-sqlite.html - public static SqlFunctionCall TimeConstruct(SqlExpression hour, SqlExpression minute, SqlExpression second, @@ -647,8 +626,8 @@ public static SqlBinary DateTimePlusInterval(SqlExpression left, SqlExpression r ArgumentValidator.EnsureArgumentNotNull(right, "right"); return new SqlBinary(SqlNodeType.DateTimePlusInterval, left, right); } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public static SqlBinary TimePlusInterval(SqlExpression left, SqlExpression right) { ArgumentValidator.EnsureArgumentNotNull(left, "left"); @@ -692,7 +671,7 @@ public static SqlFunctionCall DateTimeAddMonths(SqlExpression source, SqlExpress return new SqlFunctionCall(SqlFunctionType.DateTimeAddMonths, source, months); } -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER public static SqlFunctionCall DateTimeToTime(SqlExpression expression) { ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); @@ -909,8 +888,8 @@ public static SqlFunctionCall DateTimeOffsetToDateTime(SqlExpression dateTimeOff ArgumentValidator.EnsureArgumentNotNull(dateTimeOffset, nameof(dateTimeOffset)); return new SqlFunctionCall(SqlFunctionType.DateTimeOffsetToDateTime, dateTimeOffset); } - #if NET6_0_OR_GREATER //DO_DATEONLY + public static SqlFunctionCall DateTimeOffsetToTime(SqlExpression dateTimeOffset) { ArgumentValidator.EnsureArgumentNotNull(dateTimeOffset, nameof(dateTimeOffset)); diff --git a/Orm/Xtensive.Orm/Sql/SqlDriver.cs b/Orm/Xtensive.Orm/Sql/SqlDriver.cs index 8e9603009f..4417ba622b 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDriver.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDriver.cs @@ -421,7 +421,7 @@ private static void RegisterStandardMappings(TypeMappingRegistryBuilder builder) builder.Add(WellKnownTypes.TimeSpan, mapper.ReadTimeSpan, mapper.BindTimeSpan, mapper.MapTimeSpan); builder.Add(WellKnownTypes.Guid, mapper.ReadGuid, mapper.BindGuid, mapper.MapGuid); builder.Add(WellKnownTypes.ByteArray, mapper.ReadByteArray, mapper.BindByteArray, mapper.MapByteArray); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER builder.Add(WellKnownTypes.DateOnly, mapper.ReadDateOnly, mapper.BindDateOnly, mapper.MapDateOnly); builder.Add(WellKnownTypes.TimeOnly, mapper.ReadTimeOnly, mapper.BindTimeOnly, mapper.MapTimeOnly); #endif @@ -450,7 +450,7 @@ private static void RegisterStandardReverseMappings(TypeMappingRegistryBuilder b builder.AddReverse(SqlType.VarBinary, WellKnownTypes.ByteArray); builder.AddReverse(SqlType.VarBinaryMax, WellKnownTypes.ByteArray); builder.AddReverse(SqlType.Guid, WellKnownTypes.Guid); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER builder.AddReverse(SqlType.Date, WellKnownTypes.DateOnly); builder.AddReverse(SqlType.Time, WellKnownTypes.TimeOnly); #endif diff --git a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs index 4487571a47..02992c6537 100644 --- a/Orm/Xtensive.Orm/Sql/SqlNodeType.cs +++ b/Orm/Xtensive.Orm/Sql/SqlNodeType.cs @@ -47,7 +47,7 @@ public enum SqlNodeType DateTimeOffsetPlusInterval, DateTimeOffsetMinusInterval, DateTimeOffsetMinusDateTimeOffset, -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER TimePlusInterval, TimeMinusTime, #endif diff --git a/Orm/Xtensive.Orm/Sql/SqlType.cs b/Orm/Xtensive.Orm/Sql/SqlType.cs index cfe3f34ef4..965402e256 100644 --- a/Orm/Xtensive.Orm/Sql/SqlType.cs +++ b/Orm/Xtensive.Orm/Sql/SqlType.cs @@ -96,8 +96,8 @@ public struct SqlType : IEquatable /// Storage size is 8 to 10 bytes. /// public static readonly SqlType DateTimeOffset = new SqlType("DateTimeOffset"); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY /// /// Date from January 1, 1753 through December 31, 9999, /// Storage size is 3 bytes. diff --git a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs index 347e8facc4..51719dd012 100644 --- a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs +++ b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs @@ -131,8 +131,8 @@ public virtual void BindDateTime(DbParameter parameter, object value) parameter.DbType = DbType.DateTime; parameter.Value = value ?? DBNull.Value; } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public virtual void BindDateOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Date; @@ -223,8 +223,8 @@ public virtual object ReadDecimal(DbDataReader reader, int index) => public virtual object ReadDateTime(DbDataReader reader, int index) => reader.GetDateTime(index); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public virtual object ReadDateOnly(DbDataReader reader, int index) => DateOnly.FromDateTime(reader.GetFieldValue(index)); @@ -327,8 +327,8 @@ public virtual SqlValueType MapDecimal(int? length, int? precision, int? scale) public virtual SqlValueType MapDateTime(int? length, int? precision, int? scale) => new SqlValueType(SqlType.DateTime); +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY public virtual SqlValueType MapDateOnly(int? length, int? precision, int? scale) => new SqlValueType(SqlType.Date); diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs index d27a44d1c4..4a5974695e 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs @@ -587,8 +587,8 @@ public DateTimeOffsetFieldAccessor() : base(GetSize() * 8, 17) { } } +#if NET6_0_OR_GREATER -#if NET6_0_OR_GREATER //DO_DATEONLY internal sealed class DateOnlyFieldAccessor : ValueFieldAccessor { protected override DateOnly Decode(long value) => diff --git a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs index 0751bff4c4..c878cc729b 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs @@ -46,7 +46,7 @@ private static class ValueFieldAccessorResolver private static readonly ValueFieldAccessor SingleAccessor = new FloatFieldAccessor(); private static readonly ValueFieldAccessor DoubleAccessor = new DoubleFieldAccessor(); private static readonly ValueFieldAccessor DateTimeAccessor = new DateTimeFieldAccessor(); -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER private static readonly ValueFieldAccessor DateOnlyAccessor = new DateOnlyFieldAccessor(); private static readonly ValueFieldAccessor TimeOnlyAccessor = new TimeOnlyFieldAccessor(); #endif @@ -78,7 +78,7 @@ ValueFieldAccessor ResolveByType(Type type) ReferenceEquals(type, WellKnownTypes.Double) ? DoubleAccessor : ReferenceEquals(type, WellKnownTypes.DateTime) ? DateTimeAccessor : ReferenceEquals(type, WellKnownTypes.TimeSpan) ? TimeSpanAccessor : -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER ReferenceEquals(type, WellKnownTypes.DateOnly) ? DateOnlyAccessor : ReferenceEquals(type, WellKnownTypes.TimeOnly) ? TimeOnlyAccessor : #endif @@ -102,7 +102,7 @@ ValueFieldAccessor ResolveByNullableType(Type type) ReferenceEquals(type, WellKnownTypes.NullableDouble) ? DoubleAccessor : ReferenceEquals(type, WellKnownTypes.NullableDateTime) ? DateTimeAccessor : ReferenceEquals(type, WellKnownTypes.NullableTimeSpan) ? TimeSpanAccessor : -#if NET6_0_OR_GREATER //DO_DATEONLY +#if NET6_0_OR_GREATER ReferenceEquals(type, WellKnownTypes.NullableDateOnly) ? DateOnlyAccessor : ReferenceEquals(type, WellKnownTypes.NullableTimeOnly) ? TimeOnlyAccessor : #endif From fe72637b3fb0bf51d2ec692a170d4b334202389e Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 6 Mar 2023 12:33:51 +0500 Subject: [PATCH 82/86] TimeOnlyCompilers: No compiler for TimeOnly.Ticks --- .../Expressions/MemberCompilers/TimeOnlyCompilers.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs index 59a9899d22..50f38321fe 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Expressions/MemberCompilers/TimeOnlyCompilers.cs @@ -35,12 +35,6 @@ public static SqlExpression TimeOnlySecond(SqlExpression _this) => public static SqlExpression TimeOnlyMillisecond(SqlExpression _this) => ExpressionTranslationHelpers.ToInt(SqlDml.Extract(SqlTimePart.Millisecond, _this)); - [Compiler(typeof(TimeOnly), "Ticks", TargetKind.PropertyGet)] - public static SqlExpression TimeOnlyTicks(SqlExpression _this) - { - throw new NotImplementedException(); - } - #endregion #region Constructors From 921490a3c0c96a833f8f05b6c03aeab3b821f948 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 6 Mar 2023 21:09:19 -0800 Subject: [PATCH 83/86] Override Bind/Read in SqlServer's TypeMapper only --- .../Sql.Drivers.SqlServer/v09/TypeMapper.cs | 20 +++++++++++++++++++ .../Sql/ValueTypeMapping/TypeMapper.cs | 8 ++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs index b998c4dbe2..7163974dcf 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs @@ -80,6 +80,26 @@ public override void BindString(DbParameter parameter, object value) : NVarCharMaxLength; } +#if NET6_0_OR_GREATER + public override void BindDateOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.Date; + parameter.Value = value != null ? (DateOnly) value : DBNull.Value; + } + + public override void BindTimeOnly(DbParameter parameter, object value) + { + parameter.DbType = DbType.Time; + parameter.Value = value != null ? (TimeOnly) value : DBNull.Value; + } + + public override object ReadDateOnly(DbDataReader reader, int index) => + reader.GetFieldValue(index); + + public override object ReadTimeOnly(DbDataReader reader, int index) => + reader.GetFieldValue(index); +#endif + public override SqlValueType MapSByte(int? length, int? precision, int? scale) { return new SqlValueType(SqlType.Int16); diff --git a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs index c12df4a31e..12ba1545cc 100644 --- a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs +++ b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs @@ -136,13 +136,13 @@ public virtual void BindDateTime(DbParameter parameter, object value) public virtual void BindDateOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Date; - parameter.Value = value != null ? (DateOnly) value : DBNull.Value; + parameter.Value = value != null ? ((DateOnly) value).ToDateTime(TimeOnly.MinValue) : DBNull.Value; } public virtual void BindTimeOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Time; - parameter.Value = value != null ? (TimeOnly) value : DBNull.Value; + parameter.Value = value != null ? ((TimeOnly) value).ToTimeSpan() : DBNull.Value; } #endif @@ -226,10 +226,10 @@ public virtual object ReadDateTime(DbDataReader reader, int index) => #if NET6_0_OR_GREATER public virtual object ReadDateOnly(DbDataReader reader, int index) => - reader.GetFieldValue(index); + DateOnly.FromDateTime(reader.GetFieldValue(index)); public virtual object ReadTimeOnly(DbDataReader reader, int index) => - reader.GetFieldValue(index); + TimeOnly.FromTimeSpan(reader.GetFieldValue(index)); #endif public virtual object ReadDateTimeOffset(DbDataReader reader, int index) => From 0c90eed9ef459a391801c86facde402447ea260b Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 6 Mar 2023 21:11:41 -0800 Subject: [PATCH 84/86] Fix formatting --- .../Sql/ValueTypeMapping/TypeMapper.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs index 12ba1545cc..f95ea9b57c 100644 --- a/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs +++ b/Orm/Xtensive.Orm/Sql/ValueTypeMapping/TypeMapper.cs @@ -46,12 +46,12 @@ public virtual void BindBoolean(DbParameter parameter, object value) public virtual void BindChar(DbParameter parameter, object value) { parameter.DbType = DbType.String; - if (value==null) { + if (value == null) { parameter.Value = DBNull.Value; return; } var _char = (char) value; - parameter.Value = _char==default(char) ? string.Empty : _char.ToString(); + parameter.Value = _char == default(char) ? string.Empty : _char.ToString(); } public virtual void BindString(DbParameter parameter, object value) @@ -155,7 +155,7 @@ public virtual void BindDateTimeOffset(DbParameter parameter, object value) public virtual void BindTimeSpan(DbParameter parameter, object value) { parameter.DbType = DbType.Int64; - if (value!=null) { + if (value != null) { var timeSpan = ValueRangeValidator.Correct((TimeSpan) value, Int64TimeSpanRange); parameter.Value = timeSpan.Ticks * 100; } @@ -311,9 +311,9 @@ public virtual SqlValueType MapDouble(int? length, int? precision, int? scale) = public virtual SqlValueType MapDecimal(int? length, int? precision, int? scale) { - if (MaxDecimalPrecision==null) + if (MaxDecimalPrecision == null) return new SqlValueType(SqlType.Decimal); - if (precision==null) { + if (precision == null) { var resultPrecision = Math.Min(DecimalPrecisionLimit, MaxDecimalPrecision.Value); var resultScale = resultPrecision / 2; return new SqlValueType(SqlType.Decimal, resultPrecision, resultScale); @@ -352,9 +352,9 @@ public virtual SqlValueType MapByteArray(int? length, int? precision, int? scale protected static SqlValueType ChooseStreamType(SqlType varType, SqlType varMaxType, int? length, int? varTypeMaxLength) { - if (varTypeMaxLength==null) + if (varTypeMaxLength == null) return new SqlValueType(varMaxType); - if (length==null) + if (length == null) return new SqlValueType(varType, varTypeMaxLength.Value); if (length.Value > varTypeMaxLength.Value) return new SqlValueType(varMaxType); @@ -367,13 +367,13 @@ protected static SqlValueType ChooseStreamType(SqlType varType, SqlType varMaxTy public virtual void Initialize() { var varchar = Driver.ServerInfo.DataTypes.VarChar; - if (varchar!=null) + if (varchar != null) VarCharMaxLength = varchar.MaxLength; var varbinary = Driver.ServerInfo.DataTypes.VarBinary; - if (varbinary!=null) + if (varbinary != null) VarBinaryMaxLength = varbinary.MaxLength; var _decimal = Driver.ServerInfo.DataTypes.Decimal; - if (_decimal!=null) + if (_decimal != null) MaxDecimalPrecision = _decimal.MaxPrecision; } From 0732719c4706b8282397c3130fe3306cfefede40 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 7 Mar 2023 12:40:57 +0500 Subject: [PATCH 85/86] Improve changlog --- ChangeLog/7.1.0-RC-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog/7.1.0-RC-dev.txt b/ChangeLog/7.1.0-RC-dev.txt index 5abb980437..b250b7e6bb 100644 --- a/ChangeLog/7.1.0-RC-dev.txt +++ b/ChangeLog/7.1.0-RC-dev.txt @@ -1,2 +1,3 @@ +[main] Add support for new .NET 6 DateOnly and TimeOnly types [main] Updated BitFaster.Caching package reference to v2.0.0 [sqlserver] Updated Microsoft.Data.SqlClient package reference to v5.0.0 \ No newline at end of file From e19c329f019696d1a5d23ce8681117a9e8b8d160 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 7 Mar 2023 18:27:36 +0500 Subject: [PATCH 86/86] Put Read methods to correct place within class --- .../Sql.Drivers.SqlServer/v09/TypeMapper.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs index 7163974dcf..8e47a6cd7e 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/TypeMapper.cs @@ -79,8 +79,8 @@ public override void BindString(DbParameter parameter, object value) ? NVarCharLength : NVarCharMaxLength; } - #if NET6_0_OR_GREATER + public override void BindDateOnly(DbParameter parameter, object value) { parameter.DbType = DbType.Date; @@ -92,12 +92,6 @@ public override void BindTimeOnly(DbParameter parameter, object value) parameter.DbType = DbType.Time; parameter.Value = value != null ? (TimeOnly) value : DBNull.Value; } - - public override object ReadDateOnly(DbDataReader reader, int index) => - reader.GetFieldValue(index); - - public override object ReadTimeOnly(DbDataReader reader, int index) => - reader.GetFieldValue(index); #endif public override SqlValueType MapSByte(int? length, int? precision, int? scale) @@ -137,6 +131,14 @@ public override object ReadDateTimeOffset(DbDataReader reader, int index) { return ((SqlDataReader) reader).GetDateTimeOffset(index); } +#if NET6_0_OR_GREATER + + public override object ReadDateOnly(DbDataReader reader, int index) => + reader.GetFieldValue(index); + + public override object ReadTimeOnly(DbDataReader reader, int index) => + reader.GetFieldValue(index); +#endif public override void Initialize() {