Skip to content

Commit edef8e4

Browse files
committed
MSSQL: TimeConstruct with ticks
1 parent c5b26ca commit edef8e4

2 files changed

Lines changed: 105 additions & 37 deletions

File tree

Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
using System;
88
using System.Linq;
9-
using System.Text;
9+
using System.Collections.Generic;
1010
using Xtensive.Sql.Compiler;
1111
using Xtensive.Sql.Info;
1212
using Xtensive.Sql.Model;
@@ -220,24 +220,14 @@ public override void Visit(SqlFunctionCall node)
220220
DateTimeTruncate(arguments[0]).AcceptVisitor(this);
221221
return;
222222
case SqlFunctionType.DateTimeConstruct:
223-
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)),
224-
arguments[0] - 2001),
225-
arguments[1] - 1),
226-
arguments[2] - 1));
223+
ConstructDateTime(arguments).AcceptVisitor(this);
227224
return;
228225
#if NET6_0_OR_GREATER
229226
case SqlFunctionType.DateConstruct:
230-
Visit(SqlDml.Cast(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)),
231-
arguments[0] - 2001),
232-
arguments[1] - 1),
233-
arguments[2] - 1), SqlType.Date));
227+
ConstructDate(arguments).AcceptVisitor(this);
234228
return;
235229
case SqlFunctionType.TimeConstruct:
236-
Visit(SqlDml.Cast(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Literal(new TimeOnly(0, 0, 0)),
237-
arguments[0]),
238-
arguments[1]),
239-
arguments[2]),
240-
arguments[3]), SqlType.Time));
230+
ConstructTime(arguments).AcceptVisitor(this);
241231
return;
242232
case SqlFunctionType.DateToString:
243233
Visit(DateToString(arguments[0]));
@@ -481,14 +471,83 @@ protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpre
481471
DateAddDay(date, interval / NanosecondsPerDay),
482472
(interval / NanosecondsPerMillisecond) % (MillisecondsPerDay));
483473
}
474+
475+
/// <summary>
476+
/// Creates expression that represents construction of datetime value
477+
/// from arguments (year, month, day).
478+
/// </summary>
479+
/// <param name="arguments">Expressions representing year, month, and day.</param>
480+
/// <returns>Result expression.</returns>
481+
protected virtual SqlExpression ConstructDateTime(IReadOnlyList<SqlExpression> arguments)
482+
{
483+
return DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)),
484+
arguments[0] - 2001),
485+
arguments[1] - 1),
486+
arguments[2] - 1);
487+
}
488+
484489
#if NET6_0_OR_GREATER
485490

491+
/// <summary>
492+
/// Creates expression that represents construction of date value
493+
/// from arguments (year, month, day).
494+
/// </summary>
495+
/// <param name="arguments">Expressions representing year, month, and day.</param>
496+
/// <returns>Result expression.</returns>
497+
protected virtual SqlExpression ConstructDate(IReadOnlyList<SqlExpression> arguments)
498+
{
499+
return SqlDml.Cast(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)),
500+
arguments[0] - 2001),
501+
arguments[1] - 1),
502+
arguments[2] - 1), SqlType.Date);
503+
}
504+
505+
/// <summary>
506+
/// Creates expression that represents construction of time value from arguments.
507+
/// </summary>
508+
/// <param name="arguments">Expressions to construct time from.</param>
509+
/// <returns>Result expression.</returns>
510+
/// <exception cref="NotSupportedException"></exception>
511+
/// <exception cref="InvalidOperationException"></exception>
512+
protected virtual SqlExpression ConstructTime(IReadOnlyList<SqlExpression> arguments)
513+
{
514+
SqlExpression hour, minute, second, millisecond;
515+
if (arguments.Count == 4) {
516+
hour = arguments[0];
517+
minute = arguments[1];
518+
second = arguments[2];
519+
millisecond = arguments[3];
520+
}
521+
else if (arguments.Count == 1) {
522+
var ticks = arguments[0];
523+
hour = SqlDml.Cast(ticks / 36000000000, SqlType.Int32);
524+
minute = SqlDml.Cast((ticks / 600000000) % 60, SqlType.Int32);
525+
second = SqlDml.Cast((ticks / 10000000) % 60, SqlType.Int32);
526+
millisecond = SqlDml.Cast(ticks % 10000000, SqlType.Int32);
527+
}
528+
else {
529+
throw new InvalidOperationException("Unsupported count of parameters");
530+
}
531+
532+
return SqlDml.Cast(
533+
DateAddMillisecond(
534+
DateAddSecond(
535+
DateAddMinute(
536+
DateAddHour(
537+
SqlDml.Literal(new TimeOnly(0, 0, 0)),
538+
hour),
539+
minute),
540+
second),
541+
millisecond),
542+
SqlType.Time);
543+
}
544+
486545
/// <summary>
487546
/// Creates expression that represents addition <paramref name="interval"/> to the given <paramref name="time"/>.
488547
/// </summary>
489548
/// <param name="time">Time expression.</param>
490549
/// <param name="interval">Interval expression to add.</param>
491-
/// <returns></returns>
550+
/// <returns>Result expression.</returns>
492551
protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) =>
493552
DateAddMillisecond(time, (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay));
494553

@@ -498,7 +557,6 @@ protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpressio
498557
/// <param name="time1">First <see cref="TimeOnly"/> expression.</param>
499558
/// <param name="time2">Second <see cref="TimeOnly"/> expression.</param>
500559
/// <returns>Result expression.</returns>
501-
/// <returns></returns>
502560
protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) =>
503561
SqlDml.Modulo(
504562
NanosecondsPerDay + CastToDecimal(DateDiffMillisecond(time2, time1), 18,0) * NanosecondsPerMillisecond,

Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v11/Compiler.cs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// Created by: Denis Krjuchkov
55
// Created: 2012.04.02
66

7+
using System;
8+
using System.Collections.Generic;
79
using Xtensive.Sql.Compiler;
810
using Xtensive.Sql.Dml;
911

@@ -44,30 +46,38 @@ protected override void VisitSelectLimitOffset(SqlSelect node)
4446
}
4547
}
4648

47-
public override void Visit(SqlFunctionCall node)
48-
{
49-
var arguments = node.Arguments;
50-
switch (node.FunctionType) {
51-
case SqlFunctionType.DateTimeConstruct:
52-
Visit(SqlDml.FunctionCall("DATETIME2FROMPARTS", arguments[0], arguments[1], arguments[2], 0, 0, 0, 0, 7));
53-
return;
49+
protected override SqlUserFunctionCall ConstructDateTime(IReadOnlyList<SqlExpression> arguments) =>
50+
SqlDml.FunctionCall("DATETIME2FROMPARTS", arguments[0], arguments[1], arguments[2], 0, 0, 0, 0, 7);
5451
#if NET6_0_OR_GREATER
55-
case SqlFunctionType.DateConstruct: {
56-
Visit(SqlDml.FunctionCall("DATEFROMPARTS", arguments[0], arguments[1], arguments[2]));
57-
return;
58-
}
59-
case SqlFunctionType.TimeConstruct: {
60-
// argument[3] * 10000 operation is based on statement that millisaconds use 3 digits
61-
// default precision of time is 7, and if we use raw argument[3] value the result will be .0000xxx,
62-
// to prevent this and make fractions part valid .xxx0000 we multiply
63-
Visit(SqlDml.FunctionCall("TIMEFROMPARTS", arguments[0], arguments[1], arguments[2], arguments[3] * 10000, 7));
64-
return;
65-
}
66-
#endif
67-
}
6852

69-
base.Visit(node);
53+
protected override SqlUserFunctionCall ConstructDate(IReadOnlyList<SqlExpression> arguments) =>
54+
SqlDml.FunctionCall("DATEFROMPARTS", arguments[0], arguments[1], arguments[2]);
55+
56+
protected override SqlUserFunctionCall ConstructTime(IReadOnlyList<SqlExpression> arguments)
57+
{
58+
SqlExpression hour, minute, second, millisecond;
59+
if (arguments.Count == 4) {
60+
// argument[3] * 10000 operation is based on statement that millisaconds use 3 digits
61+
// default precision of time is 7, and if we use raw argument[3] value the result will be .0000xxx,
62+
// to prevent this and make fractions part valid .xxx0000 we multiply
63+
hour = arguments[0];
64+
minute = arguments[1];
65+
second = arguments[2];
66+
millisecond = arguments[3] * 10000;
67+
}
68+
else if (arguments.Count == 1) {
69+
var ticks = arguments[0];
70+
hour = SqlDml.Cast(ticks / 36000000000, SqlType.Int32);
71+
minute = SqlDml.Cast((ticks / 600000000) % 60, SqlType.Int32);
72+
second = SqlDml.Cast((ticks / 10000000) % 60, SqlType.Int32);
73+
millisecond = SqlDml.Cast(ticks % 10000000, SqlType.Int32);
74+
}
75+
else {
76+
throw new InvalidOperationException("Unsupported count of parameters");
77+
}
78+
return SqlDml.FunctionCall("TIMEFROMPARTS", hour, minute, second, millisecond, 7);
7079
}
80+
#endif
7181

7282
public Compiler(SqlDriver driver)
7383
: base(driver)

0 commit comments

Comments
 (0)