From bee6da82e56818076a4fab769f6e6a93a96a1677 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 3 Sep 2026 16:03:15 +0500 Subject: [PATCH] Use same underlying values for parts in SqlDateTimeOffsetPart/SqlDateTimePart/SqlIntervalPart to less memory consumed but still have fast conversion --- .../Sql.Drivers.Firebird/v2_5/Compiler.cs | 82 +++++------ .../Sql.Drivers.MySql/v5_0/Compiler.cs | 8 +- .../Sql.Drivers.MySql/v5_0/Translator.cs | 5 +- .../Sql.Drivers.Oracle/v09/Compiler.cs | 128 +++++++++--------- .../Sql.Drivers.Oracle/v09/Translator.cs | 4 +- .../Sql.Drivers.PostgreSql/v8_0/Compiler.cs | 42 +++--- .../Sql.Drivers.PostgreSql/v8_0/Translator.cs | 8 +- .../Sql.Drivers.SqlServer/v09/Compiler.cs | 42 +++--- .../Sql.Drivers.SqlServer/v10/Compiler.cs | 52 +++---- .../Sql.Drivers.Sqlite/v3/Compiler.cs | 8 +- Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs | 4 +- .../Sql/Dml/Expressions/SqlExtract.cs | 64 ++++++--- Orm/Xtensive.Orm/Sql/Dml/Extensions.cs | 7 + .../Sql/Dml/SqlDateTimeOffsetPart.cs | 62 ++++++--- Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs | 50 +++++-- Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs | 40 ++++-- 16 files changed, 359 insertions(+), 247 deletions(-) diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs index ab47b9c79f..1394d21a21 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) 2003-2010 Xtensive LLC. +// Copyright (C) 2003-2010 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Csaba Beer @@ -74,45 +74,49 @@ public override void Visit(SqlQueryExpression node) /// public override void Visit(SqlExtract node) { - switch (node.IntervalPart) { - case SqlIntervalPart.Day: - Visit(CastToLong(node.Operand / NanosecondsPerDay)); - return; - case SqlIntervalPart.Hour: - Visit(CastToLong(node.Operand / (60 * 60 * NanosecondsPerSecond)) % 24); - return; - case SqlIntervalPart.Minute: - Visit(CastToLong(node.Operand / (60 * NanosecondsPerSecond)) % 60); - return; - case SqlIntervalPart.Second: - Visit(CastToLong(node.Operand / NanosecondsPerSecond) % 60); - return; - case SqlIntervalPart.Millisecond: - Visit(CastToLong(node.Operand / NanosecondsPerMillisecond) % MillisecondsPerSecond); - return; - case SqlIntervalPart.Nanosecond: - Visit(CastToLong(node.Operand)); - return; + if (node.IsIntervalPart) { + switch (node.IntervalPart) { + case SqlIntervalPart.Day: + Visit(CastToLong(node.Operand / NanosecondsPerDay)); + return; + case SqlIntervalPart.Hour: + Visit(CastToLong(node.Operand / (60 * 60 * NanosecondsPerSecond)) % 24); + return; + case SqlIntervalPart.Minute: + Visit(CastToLong(node.Operand / (60 * NanosecondsPerSecond)) % 60); + return; + case SqlIntervalPart.Second: + Visit(CastToLong(node.Operand / NanosecondsPerSecond) % 60); + return; + case SqlIntervalPart.Millisecond: + Visit(CastToLong(node.Operand / NanosecondsPerMillisecond) % MillisecondsPerSecond); + return; + case SqlIntervalPart.Nanosecond: + Visit(CastToLong(node.Operand)); + return; + } } - switch (node.DateTimePart) { - case 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; - case SqlDateTimePart.Second: - if (!case_SqlDateTimePart_Second) { - case_SqlDateTimePart_Second = true; - Visit(SqlDml.Truncate(node)); - case_SqlDateTimePart_Second = false; - } - else - base.Visit(node); - return; + if (node.IsDateTimePart) { + switch (node.DateTimePart) { + case 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; + case SqlDateTimePart.Second: + if (!case_SqlDateTimePart_Second) { + case_SqlDateTimePart_Second = true; + Visit(SqlDml.Truncate(node)); + case_SqlDateTimePart_Second = false; + } + else + base.Visit(node); + return; + } } base.Visit(node); } 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 a8ec7b5870..9679fb1d45 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) 2003-2010 Xtensive LLC. +// Copyright (C) 2003-2010 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Malisa Ncube @@ -211,9 +211,9 @@ public override void VisitSelectLimitOffset(SqlSelect node) public override void Visit(SqlExtract node) { - if (node.DateTimePart==SqlDateTimePart.DayOfWeek || node.DateTimePart==SqlDateTimePart.DayOfYear) { - Visit(SqlDml.FunctionCall(node.DateTimePart.ToString(), node.Operand)); - return; + if (node.IsDateTimePart && (node.DateTimePart==SqlDateTimePart.DayOfWeek || node.DateTimePart==SqlDateTimePart.DayOfYear)) { + Visit(SqlDml.FunctionCall(node.DateTimePart.ToString(), node.Operand)); + return; } base.Visit(node); } 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 57e5ceb0f7..c50bacd29f 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 @@ -415,9 +415,8 @@ public override string Translate(SqlCompilerContext context, object literalValue /// public override string Translate(SqlCompilerContext context, SqlExtract node, ExtractSection section) { - bool isSecond = node.DateTimePart==SqlDateTimePart.Second || node.IntervalPart==SqlIntervalPart.Second; - bool isMillisecond = node.DateTimePart==SqlDateTimePart.Millisecond || - node.IntervalPart==SqlIntervalPart.Millisecond; + bool isSecond = node.IsSecondExtraction; + bool isMillisecond = node.IsMillisecondExtraction; if (!(isSecond || isMillisecond)) return base.Translate(context, node, section); switch (section) { 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 5e0f22a719..754c28964e 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs @@ -97,69 +97,73 @@ 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; + if (node.IsDateTimeOffsetPart) { + 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; + } } - 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; + if (node.IsDateTimePart) { + 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; + } } } 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 ee749053a7..def80b5ffe 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -94,7 +94,7 @@ public override string Translate(SqlCompilerContext context, SqlNextValue node, public override string Translate(SqlCompilerContext context, SqlExtract node, ExtractSection section) { - if (node.DateTimePart==SqlDateTimePart.Second || node.IntervalPart==SqlIntervalPart.Second) + if (node.IsSecondExtraction && !node.IsDateTimeOffsetPart) switch (section) { case ExtractSection.Entry: return "TRUNC(EXTRACT("; @@ -104,7 +104,7 @@ public override string Translate(SqlCompilerContext context, SqlExtract node, Ex return base.Translate(context, node, section); } - if (node.DateTimePart==SqlDateTimePart.Millisecond || node.IntervalPart==SqlIntervalPart.Millisecond) + if (node.IsMillisecondExtraction && !node.IsDateTimeOffsetPart) switch (section) { case ExtractSection.Entry: return "MOD(EXTRACT("; 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 497b9aa9ac..fb77e77ac4 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 @@ -276,24 +276,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; - - case SqlDateTimeOffsetPart.UtcDateTime: - DateTimeOffsetToUtcDateTime(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.LocalDateTime: - DateTimeOffsetToLocalDateTime(node.Operand).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Offset: - DateTimeOffsetExtractOffset(node); - 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; + } } base.Visit(node); } @@ -323,9 +325,9 @@ protected void DateTimeOffsetExtractOffset(SqlExtract node) { using (context.EnterScope(node)) { context.Output.AppendText(translator.Translate(context, node, ExtractSection.Entry)); - var part = node.DateTimePart!=SqlDateTimePart.Nothing + var part = node.IsDateTimePart ? translator.Translate(node.DateTimePart) - : node.IntervalPart!=SqlIntervalPart.Nothing + : node.IsIntervalPart ? translator.Translate(node.IntervalPart) : translator.Translate(node.DateTimeOffsetPart); context.Output.AppendText(part); 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 4cb241051a..8730395514 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 @@ -331,12 +331,8 @@ public override string Translate(SqlCompilerContext context, SqlArray node, Arra public override string 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)) { return base.Translate(context, node, section); } 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 04c12ecf80..feee5f14b0 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs @@ -206,29 +206,31 @@ public override void Visit(SqlTrim node) public override void Visit(SqlExtract node) { - if (node.DateTimePart==SqlDateTimePart.DayOfWeek) { + if (node.IsDateTimePart && node.DateTimePart==SqlDateTimePart.DayOfWeek) { Visit((DatePartWeekDay(node.Operand) + DateFirst + 6) % 7); return; } - switch (node.IntervalPart) { - case SqlIntervalPart.Day: - Visit(CastToLong(node.Operand / NanosecondsPerDay)); - return; - case SqlIntervalPart.Hour: - Visit(CastToLong(node.Operand / (60 * 60 * NanosecondsPerSecond)) % 24); - return; - case SqlIntervalPart.Minute: - Visit(CastToLong(node.Operand / (60 * NanosecondsPerSecond)) % 60); - return; - case SqlIntervalPart.Second: - Visit(CastToLong(node.Operand / NanosecondsPerSecond) % 60); - return; - case SqlIntervalPart.Millisecond: - Visit(CastToLong(node.Operand / NanosecondsPerMillisecond) % MillisecondsPerSecond); - return; - case SqlIntervalPart.Nanosecond: - Visit(CastToLong(node.Operand)); - return; + if (node.IsIntervalPart) { + switch (node.IntervalPart) { + case SqlIntervalPart.Day: + Visit(CastToLong(node.Operand / NanosecondsPerDay)); + return; + case SqlIntervalPart.Hour: + Visit(CastToLong(node.Operand / (60 * 60 * NanosecondsPerSecond)) % 24); + return; + case SqlIntervalPart.Minute: + Visit(CastToLong(node.Operand / (60 * NanosecondsPerSecond)) % 60); + return; + case SqlIntervalPart.Second: + Visit(CastToLong(node.Operand / NanosecondsPerSecond) % 60); + return; + case SqlIntervalPart.Millisecond: + Visit(CastToLong(node.Operand / NanosecondsPerMillisecond) % MillisecondsPerSecond); + return; + case SqlIntervalPart.Nanosecond: + Visit(CastToLong(node.Operand)); + return; + } } base.Visit(node); } 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 259733adf4..4a553fa00f 100644 --- a/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs +++ b/Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v10/Compiler.cs @@ -52,31 +52,33 @@ protected override SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpr public override void Visit(SqlExtract node) { - switch (node.DateTimeOffsetPart) { - case SqlDateTimeOffsetPart.DayOfWeek: - Visit((DatePartWeekDay(node.Operand) + DateFirst + 6) % 7); - return; - case SqlDateTimeOffsetPart.TimeZoneHour: - Visit(DateTimeOffsetTimeZoneInMinutes(node.Operand) / 60); - return; - case SqlDateTimeOffsetPart.TimeZoneMinute: - Visit(DateTimeOffsetTimeZoneInMinutes(node.Operand) % 60); - 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: - SqlDml.Cast(Switchoffset(node.Operand, UtcTimeZone), SqlType.DateTime).AcceptVisitor(this); - return; - case SqlDateTimeOffsetPart.Offset: - DateTimeOffsetPartOffset(node.Operand).AcceptVisitor(this); - return; + if (node.IsDateTimeOffsetPart) { + switch (node.DateTimeOffsetPart) { + case SqlDateTimeOffsetPart.DayOfWeek: + Visit((DatePartWeekDay(node.Operand) + DateFirst + 6) % 7); + return; + case SqlDateTimeOffsetPart.TimeZoneHour: + Visit(DateTimeOffsetTimeZoneInMinutes(node.Operand) / 60); + return; + case SqlDateTimeOffsetPart.TimeZoneMinute: + Visit(DateTimeOffsetTimeZoneInMinutes(node.Operand) % 60); + 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: + SqlDml.Cast(Switchoffset(node.Operand, UtcTimeZone), SqlType.DateTime).AcceptVisitor(this); + return; + case SqlDateTimeOffsetPart.Offset: + DateTimeOffsetPartOffset(node.Operand).AcceptVisitor(this); + return; + } } base.Visit(node); } diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs index 2d1ee3a494..478d939dc9 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/v3/Compiler.cs @@ -105,15 +105,15 @@ 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; } @@ -301,7 +301,7 @@ private void VisitInterval(SqlExtract node) private void VisitDateTime(SqlExtract node) { - if (node.DateTimePart==SqlDateTimePart.Millisecond) { + if (node.IsDateTimePart && node.DateTimePart==SqlDateTimePart.Millisecond) { Visit(CastToLong(DateGetMilliseconds(node.Operand))); return; } diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs index 10046919ee..1f8bc5b750 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlCompiler.cs @@ -1624,9 +1624,9 @@ public virtual void Visit(SqlExtract node) { using (context.EnterScope(node)) { context.Output.AppendText(translator.Translate(context, node, ExtractSection.Entry)); - var part = node.DateTimePart!=SqlDateTimePart.Nothing + var part = node.IsDateTimePart ? translator.Translate(node.DateTimePart) - : node.IntervalPart!=SqlIntervalPart.Nothing + : node.IsIntervalPart ? translator.Translate(node.IntervalPart) : translator.Translate(node.DateTimeOffsetPart); context.Output.AppendText(part); diff --git a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs index a0c95f5658..ee4d4d20ef 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Expressions/SqlExtract.cs @@ -12,20 +12,42 @@ namespace Xtensive.Sql.Dml { public class SqlExtract : SqlExpression { - public SqlDateTimePart DateTimePart { get; private set; } - public SqlDateTimeOffsetPart DateTimeOffsetPart { get; private set; } - public SqlIntervalPart IntervalPart { get; private set; } + private const int DateTimeTypeId = 3; + private const int DateTimeOffsetTypeId = 4; + private const int IntervalTypeId = 5; + + private SqlDateTimeOffsetPart internalValue; + private int typeMarker; + + public SqlDateTimePart DateTimePart => + typeMarker == DateTimeTypeId ? internalValue.ToDateTimePartFast() : SqlDateTimePart.Nothing; + + public SqlDateTimeOffsetPart DateTimeOffsetPart => + typeMarker == DateTimeOffsetTypeId ? internalValue : SqlDateTimeOffsetPart.Nothing; + + public SqlIntervalPart IntervalPart => + typeMarker == IntervalTypeId ? internalValue.ToIntervalPartFast() : SqlIntervalPart.Nothing; public SqlExpression Operand { get; private set; } + public bool IsSecondExtraction => + internalValue == SqlDateTimeOffsetPart.Second; + public bool IsMillisecondExtraction => + internalValue == SqlDateTimeOffsetPart.Millisecond; + + public bool IsDateTimeOffsetPart => typeMarker == DateTimeOffsetTypeId; + + public bool IsDateTimePart => typeMarker == DateTimeTypeId; + + public bool IsIntervalPart => typeMarker == IntervalTypeId; + public override void ReplaceWith(SqlExpression expression) { ArgumentValidator.EnsureArgumentNotNull(expression, "expression"); ArgumentValidator.EnsureArgumentIs(expression, "expression"); var replacingExpression = (SqlExtract) expression; - DateTimePart = replacingExpression.DateTimePart; - DateTimeOffsetPart = replacingExpression.DateTimeOffsetPart; - IntervalPart = replacingExpression.IntervalPart; + internalValue = replacingExpression.internalValue; + typeMarker = replacingExpression.typeMarker; Operand = replacingExpression.Operand; } @@ -33,11 +55,8 @@ internal override object Clone(SqlNodeCloneContext context) { if (context.NodeMapping.ContainsKey(this)) return context.NodeMapping[this]; - var clone = DateTimePart!=SqlDateTimePart.Nothing - ? new SqlExtract(DateTimePart, (SqlExpression) Operand.Clone(context)) - : IntervalPart!=SqlIntervalPart.Nothing - ? new SqlExtract(IntervalPart, (SqlExpression) Operand.Clone(context)) - : new SqlExtract(DateTimeOffsetPart, (SqlExpression) Operand.Clone(context)); + + var clone = new SqlExtract(this.internalValue, this.typeMarker, (SqlExpression)this.Operand.Clone(context)); context.NodeMapping[this] = clone; return clone; } @@ -52,27 +71,32 @@ public override void AcceptVisitor(ISqlVisitor visitor) internal SqlExtract(SqlDateTimePart dateTimePart, SqlExpression operand) : base(SqlNodeType.Extract) { - DateTimePart = dateTimePart; - DateTimeOffsetPart = SqlDateTimeOffsetPart.Nothing; - IntervalPart = SqlIntervalPart.Nothing; + internalValue = dateTimePart.ToDtoPartFast(); + typeMarker = DateTimeTypeId; Operand = operand; } internal SqlExtract(SqlIntervalPart intervalPart, SqlExpression operand) : base(SqlNodeType.Extract) { - DateTimePart = SqlDateTimePart.Nothing; - DateTimeOffsetPart = SqlDateTimeOffsetPart.Nothing; - IntervalPart = intervalPart; + internalValue = intervalPart.ToDtoPartFast(); + typeMarker = IntervalTypeId; Operand = operand; } public SqlExtract(SqlDateTimeOffsetPart dateTimeOffsetPart, SqlExpression operand) : base(SqlNodeType.Extract) { - DateTimePart = SqlDateTimePart.Nothing; - IntervalPart = SqlIntervalPart.Nothing; - DateTimeOffsetPart = dateTimeOffsetPart; + internalValue = dateTimeOffsetPart; + typeMarker = DateTimeOffsetTypeId; + Operand = operand; + } + + private SqlExtract(SqlDateTimeOffsetPart internalValue, int typeMarker, SqlExpression operand) + : base(SqlNodeType.Extract) + { + this.internalValue = internalValue; + this.typeMarker = typeMarker; Operand = operand; } } diff --git a/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs b/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs index f08ceb74d5..811668cdc1 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Extensions.cs @@ -52,5 +52,12 @@ public static string ToString(this SqlLockType lockType, bool humanReadable) result.Append("|Throw exception if locked"); return result.ToString(); } + + // See the description in the Parts file. Be careful. + internal static SqlDateTimeOffsetPart ToDtoPartFast(this SqlDateTimePart datePart) => (SqlDateTimeOffsetPart) (int) datePart; + internal static SqlDateTimeOffsetPart ToDtoPartFast(this SqlIntervalPart datePart) => (SqlDateTimeOffsetPart) (int) datePart; + + internal static SqlDateTimePart ToDateTimePartFast(this SqlDateTimeOffsetPart dtoPart) => (SqlDateTimePart) (int) dtoPart; + internal static SqlIntervalPart ToIntervalPartFast(this SqlDateTimeOffsetPart dtoPart) => (SqlIntervalPart) (int) dtoPart; } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs index ef07fee4f7..b118e91d03 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimeOffsetPart.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2003-2010 Xtensive LLC. +// Copyright (C) 2003-2010 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. @@ -6,26 +6,50 @@ namespace Xtensive.Sql.Dml { + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] public enum SqlDateTimeOffsetPart { - Year, - Month, - Day, - Hour, - Minute, - Second, - Millisecond, - Nanosecond, - TimeZoneHour, - TimeZoneMinute, - DayOfYear, - DayOfWeek, - Date, - DateTime, - LocalDateTime, - UtcDateTime, - Offset, - Nothing, + Year = 0, + Month = 1, + Day = 2, + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + TimeZoneHour = 8, + TimeZoneMinute = 9, + DayOfYear = 10, + DayOfWeek = 11, + Date = 12, + DateTime = 13, + LocalDateTime = 14, + UtcDateTime = 15, + Offset = 16, + Nothing = 25, } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs index 6650b64122..e431858e08 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlDateTimePart.cs @@ -6,21 +6,45 @@ namespace Xtensive.Sql.Dml { + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] public enum SqlDateTimePart { - Year, - Month, - Day, - Hour, - Minute, - Second, - Millisecond, - Nanosecond, - TimeZoneHour, - TimeZoneMinute, - DayOfYear, - DayOfWeek, - Nothing, + Year = 0, + Month = 1, + Day = 2, + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + TimeZoneHour = 8, + TimeZoneMinute = 9, + DayOfYear = 10, + DayOfWeek = 11, + Nothing = 25, } } diff --git a/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs b/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs index f5fcfe39f8..ae828b022e 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/SqlIntervalPart.cs @@ -1,4 +1,4 @@ -// 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: Denis Krjuchkov @@ -8,15 +8,39 @@ namespace Xtensive.Sql.Dml { + // IMPORTANT DateTime related enums are similar to each other to a certain degree + // and contain some subset of parts listed below. + // To be able to fast-convert between enums + // the same parts of enums have the same assigned value keep this pattern. + // Newer parts, if any should be added to this comment and assighed accordingly + // Year = 0, + // Month = 1, + // Day = 2, + // Hour = 3, + // Minute = 4, + // Second = 5, + // Millisecond = 6, + // Nanosecond = 7, + // TimeZoneHour = 8, + // TimeZoneMinute = 9, + // DayOfYear = 10, + // DayOfWeek = 11, + // Date = 12, + // DateTime = 13, + // LocalDateTime = 14, + // UtcDateTime = 15, + // Offset = 16, + // Nothing = 25, + [Serializable] public enum SqlIntervalPart { - Day = 0, - Hour = 1, - Minute = 2, - Second = 3, - Millisecond = 4, - Nanosecond = 5, - Nothing = 10, + Day = 2, + Hour = 3, + Minute = 4, + Second = 5, + Millisecond = 6, + Nanosecond = 7, + Nothing = 25, } } \ No newline at end of file