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
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.Firebird/Sql.Drivers.Firebird/Constants.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Constants.cs
index d0c47590a5..700a2187fa 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
+ public const string DateFormatString = @"''\'yyyy\.MM\.dd\'''";
+ public const string TimeFormatString = @"''\'HH\:mm\:ss\.ffff\'''";
+#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 82a5c02c26..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
@@ -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
@@ -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);
}
@@ -149,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
+ 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;
@@ -170,34 +205,84 @@ 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
+ 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.Cast(SqlDml.Literal(new DateOnly(2001, 1, 1)), SqlType.Date),
+ arguments[0] - 2001),
+ arguments[1] - 1),
+ arguments[2] - 1));
+ return;
+ 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),
+ 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;
+ 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;
}
@@ -223,6 +308,15 @@ protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, Sql
(CastToLong(DateDiffMillisecond(DateAddDay(date2, DateDiffDay(date2, date1)), date1)) *
NanosecondsPerMillisecond);
}
+#if NET6_0_OR_GREATER
+
+ 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)
{
@@ -231,6 +325,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) =>
@@ -271,6 +368,26 @@ protected static SqlUserFunctionCall BitXor(SqlExpression left, SqlExpression ri
protected static SqlUserFunctionCall BitNot(SqlExpression operand) =>
SqlDml.FunctionCall("BIN_NOT", operand);
+#if NET6_0_OR_GREATER
+
+ 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) =>
+ SqlDml.Substring(date, 0, 10);
+
+ protected static SqlConcat TimeToString(SqlExpression time) =>
+ SqlDml.Concat(SqlDml.Substring(time, 0, 12), SqlDml.Literal("0000"));
+#endif
protected static SqlConcat DateTimeToStringIso(SqlExpression dateTime)
{
@@ -279,8 +396,8 @@ 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/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()
+ };
}
}
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..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,6 +237,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");
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..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
@@ -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
@@ -19,15 +19,15 @@ 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
+ 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;
@@ -123,6 +123,9 @@ public override void Translate(IOutput output, SqlNodeType type)
case SqlNodeType.Modulo:
_ = output.Append("MOD"); break;
case SqlNodeType.DateTimeMinusDateTime:
+#if NET6_0_OR_GREATER
+ case SqlNodeType.TimeMinusTime:
+#endif
_ = output.Append("-"); break;
case SqlNodeType.Except:
case SqlNodeType.Intersect:
@@ -221,6 +224,18 @@ 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)
+ {
+ 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 51afafdf9c..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
@@ -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
+ 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,34 +166,107 @@ 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(DateAddMonth(node.Arguments[0], node.Arguments[1]));
+ Visit(DateTimeAddMonth(arguments[0], arguments[1]));
return;
case SqlFunctionType.DateTimeAddYears:
- Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
+ Visit(DateTimeAddYear(arguments[0], arguments[1]));
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));
+ Visit(DateTimeAddDay(DateTimeAddMonth(DateTimeAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)),
+ arguments[0] - 2001),
+ arguments[1] - 1),
+ arguments[2] - 1));
+ return;
+#if NET6_0_OR_GREATER
+ 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)),
+ 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)),
+ 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;
+ 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(node.Arguments[0]));
+ Visit(DateTimeToStringIso(arguments[0]));
return;
}
base.Visit(node);
}
+#if NET6_0_OR_GREATER
+
+ public override void Visit(SqlPlaceholder node)
+ {
+ if (node.Id is Orm.Providers.ParameterBinding 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)
@@ -210,6 +292,13 @@ public override void Visit(SqlExtract node)
Visit(SqlDml.FunctionCall(node.DateTimePart.ToString(), node.Operand));
return;
}
+#if NET6_0_OR_GREATER
+ if (node.DatePart == SqlDatePart.DayOfWeek || node.DatePart == SqlDatePart.DayOfYear) {
+ Visit(SqlDml.FunctionCall(node.DatePart.ToString(), node.Operand));
+ return;
+ }
+#endif
+
base.Visit(node);
}
@@ -217,40 +306,113 @@ 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));
}
+#if NET6_0_OR_GREATER
+
+ 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 DateDiffMicrosecond(SqlExpression date1, SqlExpression date2) =>
- SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("MICROSECOND"), date1, date2);
+ protected static SqlUserFunctionCall DateTimeDiffMicrosecond(SqlExpression datetime1, SqlExpression datetime2) =>
+ SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("MICROSECOND"), datetime1, datetime2);
+
+ 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);
+
+ protected static SqlUserFunctionCall DateTimeAddMonth(SqlExpression datetime, SqlExpression months) =>
+ SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MONTH"), months, datetime);
+
+ protected static SqlUserFunctionCall DateTimeAddDay(SqlExpression datetime, SqlExpression days) =>
+ SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("DAY"), days, datetime);
+
+ protected static SqlUserFunctionCall DateTimeAddHour(SqlExpression datetime, SqlExpression days) =>
+ SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("HOUR"), days, datetime);
- private static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) =>
- SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("YEAR"), years, date);
+ protected static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) =>
+ SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, datetime);
+#if NET6_0_OR_GREATER
- private static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) =>
- SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MONTH"), months, date);
+ 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 DateAddDay(SqlExpression date, SqlExpression days) =>
- SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("DAY"), days, date);
+ 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 DateAddMicrosecond(SqlExpression date, SqlExpression microseconds) =>
- SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, date);
+ protected static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) =>
+ SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(days, SqlDml.Native("DAY"))));
+
+ protected static SqlUserFunctionCall TimeAddHour(SqlExpression time, SqlExpression hours) =>
+ SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(hours, SqlDml.Native("HOUR"))));
+
+ protected static SqlUserFunctionCall TimeAddMinute(SqlExpression time, SqlExpression minutes) =>
+ SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(minutes, SqlDml.Native("MINUTE"))));
+
+ protected static SqlUserFunctionCall TimeAddSecond(SqlExpression time, SqlExpression seconds) =>
+ SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(seconds, SqlDml.Native("SECOND"))));
+
+ 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");
+
+ 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) =>
SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%Y-%m-%dT%T");
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..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
@@ -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;
@@ -601,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);
@@ -617,69 +618,84 @@ private SqlValueType CreateValueType(IDataRecord row,
return new SqlValueType(SqlType.Double);
}
- if (columnName == "TINYINT(1)") {
+ if (dataTypeName.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
+ 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
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 +748,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()
+ };
}
}
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..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
@@ -261,9 +261,19 @@ 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 DateOnly(1000, 1, 1), new DateOnly(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.MySql/Sql.Drivers.MySql/v5_0/Translator.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Translator.cs
index c2576d2dd1..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
@@ -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,12 @@ 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
+
+ public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'";
+
+ public override string TimeOnlyFormatString => @"\'HH\:mm\:ss\.ffffff\'";
+#endif
public override string TimeSpanFormatString => string.Empty;
@@ -106,6 +112,13 @@ public override void Translate(IOutput output, SqlFunctionType type)
case SqlFunctionType.DateTimeAddYears:
case SqlFunctionType.DateTimeAddMonths:
case SqlFunctionType.DateTimeConstruct:
+#if NET6_0_OR_GREATER
+ case SqlFunctionType.DateAddYears:
+ case SqlFunctionType.DateAddMonths:
+ case SqlFunctionType.DateAddDays:
+ case SqlFunctionType.DateConstruct:
+ case SqlFunctionType.TimeConstruct:
+#endif
case SqlFunctionType.IntervalToMilliseconds:
return;
//string
@@ -158,10 +171,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
+ case SqlNodeType.TimePlusInterval:
+#endif
+ _ = output.Append("+");
break;
case SqlNodeType.DateTimeMinusInterval:
case SqlNodeType.DateTimeMinusDateTime:
+#if NET6_0_OR_GREATER
+ case SqlNodeType.TimeMinusTime:
+#endif
_ = output.Append("-"); break;
case SqlNodeType.Equals: _ = output.Append("="); break;
case SqlNodeType.NotEquals: _ = output.Append("<>"); break;
@@ -403,9 +423,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;
@@ -415,7 +434,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);
@@ -510,6 +529,30 @@ 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 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_0/TypeMapper.cs b/Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/TypeMapper.cs
index a3550e672e..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
@@ -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
+ 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)
@@ -62,6 +66,20 @@ 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
+
+ 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)
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..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
@@ -1,13 +1,75 @@
-// 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
+ 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)
+ {
+ 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;
+ }
+ case SqlFunctionType.TimeToDateTime:
+ Visit(SqlDml.Cast(arguments[0], SqlType.DateTime));
+ return;
+ default:
+ base.Visit(node);
+ return;
+ }
+ }
+
+ 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
public Compiler(SqlDriver driver)
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.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 @@
-
-
+
+
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..168462fb70 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
@@ -11,72 +11,157 @@
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";
+ protected const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL";
+ protected const string NumToYMIntervalFunctionName = "NUMTOYMINTERVAL";
+
+ protected const string ToDSIntervalFunctionName = "TO_DSINTERVAL";
+ 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.0000000'");
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.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.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
+ 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.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;
}
}
@@ -99,69 +184,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, DayPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Hour:
+ DateTimeOffsetExtractPart(node.Operand, HourPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Millisecond:
+ DateTimeOffsetExtractPart(node.Operand, MillisecondPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Nanosecond:
+ DateTimeOffsetExtractPart(node.Operand, NanosecondPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Minute:
+ DateTimeOffsetExtractPart(node.Operand, MinutePartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Month:
+ DateTimeOffsetExtractPart(node.Operand, MonthPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Second:
+ DateTimeOffsetExtractPart(node.Operand, SecondPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.Year:
+ DateTimeOffsetExtractPart(node.Operand, YearPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.TimeZoneHour:
+ DateTimeOffsetExtractPart(node.Operand, TimeZoneHourPartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.TimeZoneMinute:
+ DateTimeOffsetExtractPart(node.Operand, TimeZoneMinutePartFormat).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.DayOfWeek:
+ DateTimeExtractDayOfWeek(node.Operand).AcceptVisitor(this);
+ return;
+ case SqlDateTimeOffsetPart.DayOfYear:
+ DateTimeOffsetExtractPart(node.Operand, DayOfYearPartFormat).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
+ 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)
@@ -232,155 +325,210 @@ 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
+ 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(NumToYMIntervalFunctionName, 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
+
+ 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 TimeAddInterval(SqlExpression time, SqlExpression intervalToAdd, bool negateInterval = false)
+ {
+ var baseOp = (negateInterval) ? RefTimestamp + time - intervalToAdd
+ : RefTimestamp + time + intervalToAdd;
+ var getTimeOnly = SqlDml.FunctionCall(ToCharFunctionName, baseOp, AnsiString(TimeFormat));
+ var pretendZeroDays = (SqlExpression) SqlDml.Concat(AnsiString("0 "), getTimeOnly);
+ var dsInterval = SqlDml.FunctionCall(ToDSIntervalFunctionName, 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)
{
// TO_CHAR with 'D' returns values depending on NLS_TERRITORY setting,
// 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 DateTimeOffsetTruncateOffset(SqlExpression dateTimeOffset) =>
+ SqlDml.Cast(dateTimeOffset, SqlType.DateTime);
- private static SqlExpression DateTimeOffsetTruncate(SqlExpression dateTimeOffset)
- {
- return SqlDml.Truncate(dateTimeOffset);
- }
+ private static SqlExpression DateTimeOffsetTruncate(SqlExpression dateTimeOffset) =>
+ SqlDml.Truncate(dateTimeOffset);
- private static SqlExpression DateTimeOffsetToUtcDateTime(SqlExpression dateTimeOffset)
- {
- return SqlDml.FunctionCall("SYS_EXTRACT_UTC", dateTimeOffset);
- }
+ private static SqlExpression DateTimeOffsetToUtcDateTime(SqlExpression dateTimeOffset) =>
+ SqlDml.FunctionCall("SYS_EXTRACT_UTC", dateTimeOffset);
- private static SqlExpression DateTimeOffsetExtractPart(SqlExpression dateTimeOffset, string dateTimeOffsetPart)
- {
- return SqlDml.FunctionCall("TO_CHAR", dateTimeOffset, AnsiString(dateTimeOffsetPart));
- }
+ private static SqlExpression DateTimeOffsetExtractPart(SqlExpression dateTimeOffset, string dateTimeOffsetPart) =>
+ SqlDml.FunctionCall(ToCharFunctionName, dateTimeOffset, AnsiString(dateTimeOffsetPart));
- private static SqlExpression DateTimeOffsetToLocalDateTime(SqlExpression dateTimeOffset)
- {
- return SqlDml.Cast(DateTimeOffsetToLocalTime(dateTimeOffset), SqlType.DateTime);
- }
+ private static SqlExpression DateTimeOffsetToLocalDateTime(SqlExpression dateTimeOffset) =>
+ SqlDml.Cast(DateTimeOffsetToLocalTime(dateTimeOffset), SqlType.DateTime);
+ private static SqlExpression DateTimeOffsetToLocalTime(SqlExpression dateTimeOffset) =>
+ SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" AT LOCAL"));
- private static SqlExpression DateTimeOffsetToLocalTime(SqlExpression dateTimeOffset)
- {
- return SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" AT LOCAL"));
- }
+ private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) =>
+ SqlDml.Cast(dateTime, SqlType.DateTimeOffset);
- private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime)
- {
- return SqlDml.Cast(dateTime, SqlType.DateTimeOffset);
- }
+ private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) =>
+ SqlDml.Cast(dateTimeOffset, SqlType.DateTime);
- 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 DateToDateTimeOffset(SqlExpression date) =>
+ SqlDml.Cast(date, SqlType.DateTimeOffset);
+#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(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);
+
+ 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/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) {
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..07e4c74a50 100644
--- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs
+++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/ServerInfoProvider.cs
@@ -225,12 +225,19 @@ 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(7)");
+#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 5c3c92167c..4fb59e564d 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
@@ -29,6 +29,14 @@ internal class Translator : SqlTranslator
///
public override string DateTimeFormatString => @"'(TIMESTAMP '\'yyyy\-MM\-dd HH\:mm\:ss\.fff\'\)";
+#if NET6_0_OR_GREATER
+
+ ///
+ public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)";
+
+ ///
+ public override string TimeOnlyFormatString => @"'(INTERVAL '\'0 HH\:mm\:ss\.fffffff\'\ DAY(0) TO SECOND(7))";
+#endif
///
public override string TimeSpanFormatString => "(INTERVAL '{0}{1} {2}:{3}:{4}.{5:000}' DAY(6) TO SECOND(3))";
@@ -108,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(");
@@ -119,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(");
@@ -327,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(7)"
+ : 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
}
///
@@ -348,6 +365,32 @@ public override void Translate(IOutput output, SqlDateTimePart dateTimePart)
break;
}
}
+#if NET6_0_OR_GREATER
+
+ ///
+ 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)
@@ -358,7 +401,6 @@ public override void Translate(IOutput output, SqlIntervalPart part)
else {
base.Translate(output, part);
}
-
}
///
@@ -385,6 +427,9 @@ public override void Translate(IOutput output, SqlNodeType type)
switch (type) {
case SqlNodeType.DateTimeOffsetPlusInterval:
case SqlNodeType.DateTimePlusInterval:
+#if NET6_0_OR_GREATER
+ case SqlNodeType.TimePlusInterval:
+#endif
_ = output.Append("+"); break;
case SqlNodeType.DateTimeOffsetMinusDateTimeOffset:
case SqlNodeType.DateTimeOffsetMinusInterval:
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..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,6 +87,24 @@ public override void BindDouble(DbParameter parameter, object value)
nativeParameter.OracleDbType = OracleDbType.Double;
nativeParameter.Value = value ?? DBNull.Value;
}
+#if NET6_0_OR_GREATER
+
+ 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)
{
@@ -194,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.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);
}
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..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
@@ -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
+ 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), SqlDml.Literal(0), SqlDml.Literal(0.0));
+#if NET6_0_OR_GREATER
+
+ 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 4d278826b1..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
@@ -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,14 +13,29 @@ namespace Xtensive.Sql.Drivers.PostgreSql.v8_0
{
internal class Compiler : SqlCompiler
{
- private readonly static Type SqlPlaceholderType = typeof(SqlPlaceholder);
+ private const string DateTimeIsoFormat = "YYYY-MM-DD\"T\"HH24:MI:SS";
+#if NET6_0_OR_GREATER
+ private const string DateFormat = "YYYY-MM-DD";
+ private const string TimeFormat = "HH24:MI:SS.US0";
+#endif
+
+ 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'");
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'");
+ 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
+ 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)
{
@@ -54,20 +69,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
+ 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;
}
}
@@ -100,12 +125,26 @@ 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));
newNode.AcceptVisitor(this);
return;
+#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:
+ ((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);
return;
@@ -115,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
+ 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);
@@ -131,8 +193,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
+ 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);
}
@@ -206,9 +297,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)
{
@@ -283,24 +373,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);
}
@@ -361,6 +453,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.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs b/Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/ServerInfoProvider.cs
index 1a811ecf3c..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,6 +177,11 @@ public override DataTypeCollection GetDataTypesInfo()
dtc.Interval = DataTypeInfo.Range(SqlType.Interval, commonFeatures,
ValueRange.TimeSpan, "interval");
+#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, "character", "char", "bpchar");
dtc.VarChar = DataTypeInfo.Stream(SqlType.VarChar, commonFeatures, MaxCharLength, "character varying", "varchar");
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..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
@@ -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
+
+ ///
+ 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";
@@ -197,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
+ case SqlNodeType.TimePlusInterval: _ = output.Append("+"); break;
+#endif
case SqlNodeType.DateTimeMinusInterval:
case SqlNodeType.DateTimeMinusDateTime:
+#if NET6_0_OR_GREATER
+ case SqlNodeType.TimeMinusTime:
+#endif
_ = output.Append("-"); break;
default: base.Translate(output, type); break;
};
@@ -388,12 +402,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;
@@ -824,6 +834,7 @@ public override void Translate(SqlCompilerContext context, SqlCast node, NodeSec
}
}
+ ///
public override void Translate(IOutput output, SqlDateTimePart part)
{
switch (part) {
@@ -834,6 +845,7 @@ public override void Translate(IOutput output, SqlDateTimePart part)
}
}
+ ///
public override void Translate(IOutput output, SqlDateTimeOffsetPart part)
{
switch (part) {
@@ -846,6 +858,29 @@ public override void Translate(IOutput output, SqlDateTimeOffsetPart part)
default: base.Translate(output, part); break;
}
}
+#if NET6_0_OR_GREATER
+
+ ///
+ 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 6d1ce50ad8..fa4798592b 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
@@ -150,9 +150,10 @@ 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);
+ (SqlDml.FunctionCall("DATALENGTH", arguments) / 2).AcceptVisitor(this);
return;
case SqlFunctionType.PadLeft:
case SqlFunctionType.PadRight:
@@ -160,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;
}
@@ -173,42 +174,92 @@ 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) {
- SqlExpression len = SqlDml.CharLength(node.Arguments[0]);
- node = SqlDml.Substring(node.Arguments[0], node.Arguments[1], len);
+ if (arguments.Count == 2) {
+ SqlExpression len = SqlDml.CharLength(arguments[0]);
+ node = SqlDml.Substring(arguments[0], arguments[1], 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;
+#if NET6_0_OR_GREATER
+ 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.TimeAddHours:
+ Visit(DateAddHour(arguments[0], arguments[1]));
+ return;
+ case SqlFunctionType.TimeAddMinutes:
+ Visit(DateAddMinute(arguments[0], arguments[1]));
+ return;
+#endif
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;
+#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;
+ case SqlFunctionType.DateToString:
+ Visit(DateToString(arguments[0]));
+ return;
+ 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(node.Arguments[0]));
+ Visit(DateTimeToStringIso(arguments[0]));
return;
}
@@ -236,6 +287,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:
@@ -273,6 +330,14 @@ public override void Visit(SqlBinary node)
case SqlNodeType.DateTimeMinusInterval:
DateTimeAddInterval(node.Left, -node.Right).AcceptVisitor(this);
return;
+#if NET6_0_OR_GREATER
+ 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;
@@ -416,6 +481,29 @@ 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) =>
+ 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) =>
+ SqlDml.Modulo(
+ NanosecondsPerDay + CastToDecimal(DateDiffMillisecond(time2, time1), 18,0) * NanosecondsPerMillisecond,
+ NanosecondsPerDay);
+#endif
private SqlExpression GenericPad(SqlFunctionCall node)
{
@@ -475,6 +563,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);
@@ -498,6 +589,26 @@ 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
+
+ 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"));
+
+ 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) =>
SqlDml.FunctionCall("CONVERT", SqlDml.Native("NVARCHAR(19)"), dateTime, SqlDml.Native("126"));
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 c3368bf6f8..325da57b5a 100644
--- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs
+++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/ServerInfoProvider.cs
@@ -252,8 +252,12 @@ 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 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
types.Char = DataTypeInfo.Stream(SqlType.Char, common | index, 4000, "nchar", "char");
types.VarChar = DataTypeInfo.Stream(SqlType.VarChar, common | index, 4000, "nvarchar", "varchar");
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..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,6 +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
+ 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()
@@ -567,6 +571,13 @@ public override void Translate(SqlCompilerContext context, object literalValue)
case long v:
_ = output.Append($"CAST({v} as BIGINT)");
break;
+#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;
+ output.Append(newDateOnly.ToString(DateOnlyFormatString, DateTimeFormat));
+ break;
+#endif
default:
base.Translate(context, literalValue);
break;
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..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,6 +79,20 @@ 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;
+ 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;
+ }
+#endif
public override SqlValueType MapSByte(int? length, int? precision, int? scale)
{
@@ -117,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()
{
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..9b167b4277 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)
@@ -138,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
+ 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);
@@ -206,6 +217,23 @@ 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
+
+ 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
@@ -215,4 +243,6 @@ public Compiler(SqlDriver driver)
{
}
}
-}
\ No newline at end of file
+}
+
+
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..47ff925a90 100644
--- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs
+++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/ServerInfoProvider.cs
@@ -29,14 +29,20 @@ 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 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");
+#else
+ types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index,
+ new ValueRange(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)),
+ "datetime2", "datetime", "smalldatetime", "date", "time");
+#endif
types.VarBinaryMax = DataTypeInfo.Regular(SqlType.VarBinaryMax, common, "varbinary(max)", "image");
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..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,6 +17,11 @@ internal class Translator : v09.Translator
///
public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fffffff\'' as datetime2)'";
+#if NET6_0_OR_GREATER
+ ///
+ 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.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs
index 63736de380..be5c543cd3 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,31 @@ 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));
+ return;
+#if NET6_0_OR_GREATER
+ case SqlFunctionType.DateConstruct: {
+ Visit(SqlDml.FunctionCall("DATEFROMPARTS", arguments[0], arguments[1], arguments[2]));
+ 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));
+ return;
+ }
+#endif
+ }
+
+ base.Visit(node);
+ }
+
public Compiler(SqlDriver driver)
: base(driver)
{
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..938e0d4a82 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,50 @@ 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 +91,7 @@ public override void Visit(SqlFunctionCall node)
base.Visit(node); break;
}
}
+#endif
protected override SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2)
{
@@ -59,7 +104,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 +123,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.SqlServer/Xtensive.Orm.SqlServer.csproj b/Orm/Xtensive.Orm.SqlServer/Xtensive.Orm.SqlServer.csproj
index 1717bd3525..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.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs
index cf044ff98e..b00bcffaf5 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
@@ -16,18 +16,23 @@ namespace Xtensive.Sql.Drivers.Sqlite.v3
{
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
+ private const string DateFormat = "%Y-%m-%d";
+ 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";
+ 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('+');
- private const long NanosecondsPerMillisecond = 1000000L;
- private const string DateFormat = "%Y-%m-%d 00:00:00.000";
- 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";
-
protected override bool VisitCreateTableConstraints(SqlCreateTable node, IEnumerable constraints, bool hasItems)
{
// SQLite has special syntax for autoincrement primary keys
@@ -73,6 +78,14 @@ public override void Visit(SqlBinary node)
DateTimeOffsetExtractOffsetAsString(node.Left))
.AcceptVisitor(this);
return;
+#if NET6_0_OR_GREATER
+ 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;
@@ -110,18 +123,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
+ if (node.IsTimePart) {
+ VisitTime(node);
+ return;
+ }
+#endif
+
base.Visit(node);
}
@@ -131,77 +151,148 @@ 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
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;
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);
+ 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
+ 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(arguments[0]).AcceptVisitor(this);
return;
+ 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;
+ 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(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(DateAddMonth(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(DateAddYear(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
+ 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);
}
@@ -315,12 +406,23 @@ 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);
+ }
+#if NET6_0_OR_GREATER //DO_DATEONLY
+
+ private void VisitTime(SqlExtract node)
+ {
+ if (node.IsMillisecondExtraction) {
+ Visit(CastToLong(DateOrTimeGetMilliseconds(node.Operand)));
return;
}
base.Visit(node);
}
+#endif
private void VisitDateTimeOffset(SqlExtract node)
{
@@ -347,14 +449,17 @@ 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) =>
- 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);
@@ -402,35 +507,109 @@ 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);
- 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
+
+ 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 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 DateToString(SqlExpression dateTime) =>
+ SqlDml.FunctionCall("STRFTIME", DateFormat, 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 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) =>
+ private static SqlExpression DateOrTimeGetTotalSeconds(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;
-
+ (((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/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()
+ };
}
}
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..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,6 +228,12 @@ 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
types.VarCharMax = DataTypeInfo.Regular(SqlType.VarCharMax, common | index,
"varchar", "nvarchar", "nchar", "char", "text", "xml");
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..c59e56208a 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
+
+ ///
+ public override string DateOnlyFormatString => @"\'yyyy\-MM\-dd\'";
+
+ ///
+ public override string TimeOnlyFormatString => @"\'HH\:mm\:ss.fffffff\'";
+#endif
public virtual string DateTimeOffsetFormatString => @"\'yyyy\-MM\-dd HH\:mm\:ss.fffK\'";
@@ -338,6 +346,32 @@ 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)
+ {
+ 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());
@@ -482,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 900b64d8c9..ef72270a5c 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,23 +9,44 @@
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.fffffff";
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);
+ return DateOnly.ParseExact(value, DateFormat, CultureInfo.InvariantCulture);
+ }
+
+ public override object ReadTimeOnly(DbDataReader reader, int index)
+ {
+ var value = reader.GetString(index);
+ return TimeOnly.ParseExact(value, TimeFormat, CultureInfo.InvariantCulture);
+ }
+#endif
public override object ReadDateTimeOffset(DbDataReader reader, int index)
{
@@ -67,6 +88,30 @@ 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
+
+ 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)
{
@@ -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
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/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
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
diff --git a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs
index c4bc8884bf..8ac5e363c5 100644
--- a/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs
+++ b/Orm/Xtensive.Orm.Tests.Framework/TestHelper.cs
@@ -50,13 +50,97 @@ 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 .
+ public static DateTime AdjustDateTimeForProvider(this DateTime origin, StorageProviderInfo providerInfo)
+ {
+ var provider = providerInfo.Provider;
+ switch (provider) {
+ case StorageProvider.MySql:
+ return providerInfo.Info.StorageVersion < StorageProviderVersion.MySql56
+ ? AdjustDateTime(origin, 0)
+ : AdjustDateTime(origin, 6);
+ case StorageProvider.Firebird:
+ return AdjustDateTime(origin, 4);
+ case StorageProvider.PostgreSql:
+ return AdjustDateTime(origin, 6);
+ case StorageProvider.Oracle:
+ return AdjustDateTime(origin, 7);
+ default:
+ return origin;
+ }
+ }
+
+ ///
+ /// Cuts down fractions of value (nanoseconds, milliseconds, etc) to desired value.
+ ///
+ ///
+ /// 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, 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;
+
+ 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
+ ///
+ /// 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 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;
@@ -80,7 +164,8 @@ public static DateTime FixDateTimeForProvider(this DateTime origin, StorageProvi
}
var ticks = origin.Ticks;
var newTicks = ticks - (ticks % divider.Value);
- return new DateTime(newTicks);
+ return new TimeOnly(newTicks);
}
+#endif
}
}
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs b/Orm/Xtensive.Orm.Tests.Sql/DateTimeIntervalTest.cs
index abd968b9e2..5b6ed42d5b 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;
@@ -146,6 +155,120 @@ public virtual void DateTimeExtractDayOfYearTest()
SqlDml.Extract(SqlDateTimePart.DayOfYear, DefaultDateTime),
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 DateOnlyExtractYearTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlDatePart.Year, DefaultDateOnly),
+ DefaultDateOnly.Year);
+ }
+
+ [Test]
+ public virtual void DateOnlyExtractMonthTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlDatePart.Month, DefaultDateOnly),
+ DefaultDateOnly.Month);
+ }
+
+ [Test]
+ public virtual void DateOnlyExtractDayTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlDatePart.Day, DefaultDateOnly),
+ DefaultDateOnly.Day);
+ }
+
+ [Test]
+ public virtual void DateOnlyExtractDayOfWeekTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlDatePart.DayOfWeek, DefaultDateOnly),
+ (int) DefaultDateOnly.DayOfWeek);
+ }
+
+ [Test]
+ public virtual void DateOnlyExtractDayOfYearTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlDatePart.DayOfYear, DefaultDateOnly),
+ DefaultDateOnly.DayOfYear);
+ }
+
+ [Test]
+ public virtual void TimeOnlyExtractHourTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlTimePart.Hour, DefaultTimeOnly),
+ DefaultTimeOnly.Hour);
+ }
+
+ [Test]
+ public virtual void TimeOnlyExtractMinuteTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlTimePart.Minute, DefaultTimeOnly),
+ DefaultTimeOnly.Minute);
+ }
+
+ [Test]
+ public virtual void TimeOnlyExtractSecondTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlTimePart.Second, DefaultTimeOnly),
+ DefaultTimeOnly.Second);
+ }
+
+ [Test]
+ public virtual void TimeOnlyExtractMillisecondTest()
+ {
+ CheckEquality(
+ SqlDml.Extract(SqlTimePart.Millisecond, DefaultTimeOnly),
+ DefaultTimeOnly.Millisecond);
+ }
+
+ [Test]
+ public virtual void TimeOnlyConstructTest()
+ {
+ CheckEquality(
+ 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]
public virtual void IntervalConstructTest()
diff --git a/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs
new file mode 100644
index 0000000000..cf9b41ccc8
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests.Sql/ExtractorTestBase.cs
@@ -0,0 +1,522 @@
+// 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 Microsoft.VisualBasic;
+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 = 4;
+ 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 bool CheckContstraintExtracted => true;
+ protected virtual bool SeqStartEqualsToMin => false;
+
+ 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 bool SortOrderSupported => Driver.ServerInfo.Index.Features.HasFlag(IndexFeatures.SortOrder);
+
+
+ protected override void TestFixtureSetUp()
+ {
+ if (IgnoreTests) {
+ throw new IgnoreException(string.Empty);
+ }
+ base.TestFixtureSetUp();
+
+ PopulateTypeToColumnName();
+ PopulateSchemasToCheck();
+ }
+
+ protected override void TestFixtureTearDown()
+ {
+ if (!IgnoreTests) {
+ foreach (var query in cleanups) {
+ ExecuteQueryLineByLine(query, true);
+ }
+ }
+
+ base.TestFixtureTearDown();
+ }
+
+ [Test]
+ public void SchemaExtractionTest()
+ {
+ Assert.That(ExtractDefaultSchema(), Is.Not.Null);
+
+ var catalog = ExtractCatalog();
+ 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);
+
+ // 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 && StorageProviderInfo.Instance.CheckProviderIsNot(StorageProvider.Sqlite)) {
+ 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.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.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);
+ 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);
+ 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);
+ 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);
+ }
+ }
+
+
+ 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.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].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);
+ }
+
+ 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");
+ }
+ }
+
+
+ protected virtual string GetPartialIndexExtractionPrepareScript(string tableName) => null;
+ protected virtual string GetPartialIndexExtractionCleanUpScript(string tableName) => null;
+
+ [Test]
+ public void PartialIndexExtractionTest()
+ {
+ 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));
+ 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"));
+ }
+ }
+
+
+ protected virtual string GetUniqueConstraintExtractionPrepareScript(string tableName) => "";
+ protected virtual 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()
+ {
+ Require.ProviderIsNot(StorageProvider.Sqlite);
+
+ 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.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.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);
+ }
+
+
+ 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);
+ }
+
+
+ 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;
+ 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));
+
+ Assert.That(seq1Descriptor.IsCyclic, Is.False);
+
+ Assert.That(schema.Sequences["seq2"], Is.Not.Null);
+ var seq2Descriptor = schema.Sequences["seq2"].SequenceDescriptor;
+ 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));
+
+ Assert.That(seq2Descriptor.IsCyclic, Is.True);
+ }
+
+ 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.Interval] = "interval_column";
+ TypeToColumnName[SqlType.DateTime] = "datetime_column";
+ TypeToColumnName[SqlType.DateTimeOffset] = "datetimeoffset_column";
+#if NET6_0_OR_GREATER
+ 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;
+ if(Driver.ServerInfo.Query.Features.HasFlag(QueryFeatures.DdlBatches)) {
+ _ = ExecuteNonQuery(sqlQuery);
+ }
+ else {
+ ExecuteQueryLineByLine(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;
+ try {
+ _ = ExecuteNonQuery(q);
+ }
+ catch {
+ if (!ignoreExceptions)
+ throw;
+ }
+ }
+ }
+
+ protected void RegisterCleanupScript(Func func) => cleanups.Add(func());
+ protected void RegisterCleanupScript(Func func, string param) => cleanups.Add(func(param));
+ }
+}
diff --git a/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Firebird/ExtractorTest.cs
index cd15f3e060..3d164fb888 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
+ 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 ASC 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};";
}
}
diff --git a/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/MySQL/ExtractorTest.cs
index c7a850c971..c4d01ff879 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
+ 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
+ }
}
diff --git a/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Oracle/ExtractorTest.cs
index 7fce21bdd4..326c474fe9 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
+ 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
diff --git a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs
index a00e1d2d5b..c6540638e9 100644
--- a/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs
+++ b/Orm/Xtensive.Orm.Tests.Sql/PostgreSql/ExtractorTest.cs
@@ -1,55 +1,221 @@
-// 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.Model;
+using Xtensive.Sql;
+using Xtensive.Sql.Dml;
+using Xtensive.Sql.Drivers.PostgreSql.v8_0;
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 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
+ 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\" 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}\";";
+
+ 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.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.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.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.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)
{
- 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 +224,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
diff --git a/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlDateTimePartsTest.cs
new file mode 100644
index 0000000000..be0bc899cb
--- /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.Tests.Sql/SqlServer/MSSQLExtractorTests.cs b/Orm/Xtensive.Orm.Tests.Sql/SqlServer/MSSQLExtractorTests.cs
index 5f6335c3a4..1924be18a7 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 MSSQLExtractorTest : 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
+ 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 createViewsQuery = GetMSSqlViewsExtractionTestPrepareStript();
+ RegisterCleanupScript(GetMSSqlViewsExtractionTestCleanUpScript);
- Assert.IsTrue(
- schema.Tables["MSSQL2005Extr_PartitionsTest_Table"]
- .PartitionDescriptor
- .PartitionSchema.Name=="MSSQL2005Extr_PartitionsTest_PFA_Schema");
+ ExecuteQueryLineByLine(createViewsQuery);
- 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
diff --git a/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs b/Orm/Xtensive.Orm.Tests.Sql/Sqlite/ExtractorTest.cs
index 243788cbdd..4e914df9b2 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
+ 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
diff --git a/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs b/Orm/Xtensive.Orm.Tests.Sql/TypeMappingTest.cs
index 93eea89ad5..f278df1a8b 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
+ 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();
}
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/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..af6e8e5340
--- /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
+
+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/ConstructorTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/ConstructorTest.cs
new file mode 100644
index 0000000000..d26665b263
--- /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
+
+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/DateOnly/DateOnlyToStringTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs
new file mode 100644
index 0000000000..67265a1d02
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/DateOnlyToStringTest.cs
@@ -0,0 +1,24 @@
+// 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 NUnit.Framework;
+using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model;
+
+namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateOnlys
+{
+ public class DateOnlyToStringTest : DateTimeBaseTest
+ {
+ [Test]
+ public void ToStringTest()
+ {
+ ExecuteInsideSession((s) => {
+ RunTest(s, c => c.DateOnly.ToString("o") == FirstDateOnly.ToString("o"));
+ RunWrongTest(s, c => c.DateOnly.ToString("o") == FirstDateOnly.AddDays(1).ToString("o"));
+ });
+ }
+ }
+}
+#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..deee0f4fe4
--- /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
+
+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..5c34ce186f
--- /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
+
+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..3730f82f61
--- /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
+
+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..c16b7406d6
--- /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
+
+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..3e7820bae5
--- /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
+
+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..070b4e7051
--- /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
+
+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..2a2031a374
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/PartsExtractionTest.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
+
+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..30115b9c0c
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateOnly/WhereTest.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.
+
+#if NET6_0_OR_GREATER
+
+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 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 e0dd74a9fb..73d7923676 100644
--- a/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.cs
+++ b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/ComparisonTest.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
@@ -14,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);
@@ -29,7 +29,7 @@ public void EqualsTest()
[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));
@@ -40,18 +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);
+ 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/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/DateTime/DateTimeToIsoTest.cs b/Orm/Xtensive.Orm.Tests/Linq/DateTimeAndDateTimeOffset/DateTime/DateTimeToIsoTest.cs
index b58dd50aee..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 e1c301044d..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,32 +15,26 @@ namespace Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.DateTimes
public class DistinctTest : DateTimeBaseTest
{
[Test]
- public void DistinctByDateTimeTest()
- {
- ExecuteInsideSession(() => DistinctPrivate(c => c.DateTime));
- }
+ 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 7eb9f0794d..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,7 +15,7 @@ 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 },
@@ -22,7 +26,7 @@ public void DateTimeJoinTest()
[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 },
@@ -33,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 },
@@ -41,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
@@ -51,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