Skip to content

Commit 7c30810

Browse files
committed
PostgreSql: TimeOnly construction with hours overflow check
1 parent 74c49c5 commit 7c30810

2 files changed

Lines changed: 22 additions & 19 deletions

File tree

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -517,35 +517,36 @@ protected virtual SqlExpression ConstructDate(IReadOnlyList<SqlExpression> argum
517517
/// <exception cref="InvalidOperationException"></exception>
518518
protected virtual SqlExpression ConstructTime(IReadOnlyList<SqlExpression> arguments)
519519
{
520-
SqlExpression hour, minute, second, millisecond;
520+
SqlExpression hour, minute, second, microsecond;
521521
if (arguments.Count == 4) {
522522
hour = arguments[0];
523523
minute = arguments[1];
524524
second = arguments[2];
525-
millisecond = arguments[3];
525+
microsecond = arguments[3] * 10000;
526526
}
527527
else if (arguments.Count == 1) {
528528
var ticks = arguments[0];
529+
// try to optimize and reduce calculations when TimeSpan.Ticks where used for TimeOnly(ticks) ctor
530+
ticks = SqlHelper.IsTimeSpanTicks(ticks, out var sourceInterval) ? sourceInterval / 100 : ticks;
529531
hour = SqlDml.Cast(ticks / 36000000000, SqlType.Int32);
530532
minute = SqlDml.Cast((ticks / 600000000) % 60, SqlType.Int32);
531533
second = SqlDml.Cast((ticks / 10000000) % 60, SqlType.Int32);
532-
millisecond = SqlDml.Cast(ticks % 10000000, SqlType.Int32);
534+
microsecond = SqlDml.Cast(ticks % 10000000, SqlType.Int32);
533535
}
534536
else {
535537
throw new InvalidOperationException("Unsupported count of parameters");
536538
}
537539

538-
return SqlDml.Cast(
539-
DateAddMillisecond(
540-
DateAddSecond(
541-
DateAddMinute(
542-
DateAddHour(
543-
SqlDml.Literal(new TimeOnly(0, 0, 0)),
544-
hour),
545-
minute),
546-
second),
547-
millisecond),
548-
SqlType.Time);
540+
// Using string version of time allows to control hours overflow
541+
// we cannot add hours, minutes and other parts to 00:00:00.000000 time
542+
// because hours might step over 24 hours and start counting from 0.
543+
// Starting from v11 built-in function with hour overflow control is used.
544+
var hourString = SqlDml.Cast(hour, new SqlValueType(SqlType.VarChar, 3));
545+
var minuteString = SqlDml.Cast(minute, new SqlValueType(SqlType.VarChar, 2));
546+
var secondString = SqlDml.Cast(second, new SqlValueType(SqlType.VarChar, 2));
547+
var microsecondString = SqlDml.Cast(microsecond, new SqlValueType(SqlType.VarChar, 7));
548+
var composedTimeString = SqlDml.Concat(hourString, SqlDml.Literal(":"), minuteString, SqlDml.Literal(":"), secondString, SqlDml.Literal("."), microsecondString);
549+
return SqlDml.Cast(composedTimeString, SqlType.Time);
549550
}
550551

551552
/// <summary>

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,29 +53,31 @@ protected override SqlUserFunctionCall ConstructDateTime(IReadOnlyList<SqlExpres
5353
protected override SqlUserFunctionCall ConstructDate(IReadOnlyList<SqlExpression> arguments) =>
5454
SqlDml.FunctionCall("DATEFROMPARTS", arguments[0], arguments[1], arguments[2]);
5555

56-
protected override SqlUserFunctionCall ConstructTime(IReadOnlyList<SqlExpression> arguments)
56+
protected override SqlExpression ConstructTime(IReadOnlyList<SqlExpression> arguments)
5757
{
58-
SqlExpression hour, minute, second, millisecond;
58+
SqlExpression hour, minute, second, microsecond;
5959
if (arguments.Count == 4) {
6060
// argument[3] * 10000 operation is based on statement that millisaconds use 3 digits
6161
// default precision of time is 7, and if we use raw argument[3] value the result will be .0000xxx,
6262
// to prevent this and make fractions part valid .xxx0000 we multiply
6363
hour = arguments[0];
6464
minute = arguments[1];
6565
second = arguments[2];
66-
millisecond = arguments[3] * 10000;
66+
microsecond = arguments[3] * 10000;
6767
}
6868
else if (arguments.Count == 1) {
6969
var ticks = arguments[0];
70+
// try to optimize and reduce calculations when TimeSpan.Ticks where used for TimeOnly(ticks) ctor
71+
ticks = SqlHelper.IsTimeSpanTicks(ticks, out var sourceInterval) ? sourceInterval / 100 : ticks;
7072
hour = SqlDml.Cast(ticks / 36000000000, SqlType.Int32);
7173
minute = SqlDml.Cast((ticks / 600000000) % 60, SqlType.Int32);
7274
second = SqlDml.Cast((ticks / 10000000) % 60, SqlType.Int32);
73-
millisecond = SqlDml.Cast(ticks % 10000000, SqlType.Int32);
75+
microsecond = SqlDml.Cast(ticks % 10000000, SqlType.Int32);
7476
}
7577
else {
7678
throw new InvalidOperationException("Unsupported count of parameters");
7779
}
78-
return SqlDml.FunctionCall("TIMEFROMPARTS", hour, minute, second, millisecond, 7);
80+
return SqlDml.FunctionCall("TIMEFROMPARTS", hour, minute, second, microsecond, 7);
7981
}
8082
#endif
8183

0 commit comments

Comments
 (0)