Skip to content

Commit 8b628b7

Browse files
committed
Oracle: TimeOnly construction with hours overflow check
+ Interval type has correct natilve type association (was already in use, see v11.Translator)
1 parent 3e205f9 commit 8b628b7

2 files changed

Lines changed: 50 additions & 12 deletions

File tree

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

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -388,27 +388,46 @@ private static SqlExpression DateConstruct(SqlExpression years, SqlExpression mo
388388

389389
private static SqlExpression TimeConstruct(IReadOnlyList<SqlExpression> arguments)
390390
{
391+
SqlExpression hour, minute, second, microsecond;
391392
if (arguments.Count == 4) {
392-
var hours = arguments[0];
393-
var minutes = arguments[1];
394-
var seconds = arguments[2];
395-
var milliseconds = arguments[3];
396-
397-
return SqlDml.FunctionCall(NumToDSIntervalFunctionName,
398-
seconds + (minutes * 60) + (hours * 3600) + (milliseconds / 1000),
399-
AnsiString(SecondIntervalPart));
393+
hour = arguments[0];
394+
minute = arguments[1];
395+
second = arguments[2];
396+
microsecond = arguments[3] * 10000;
400397
}
401398
else if (arguments.Count == 1) {
402399
var ticks = arguments[0];
403-
var zeroTime = SqlDml.Literal(new TimeOnly(0, 0, 0, 0));
404-
if (!SqlHelper.IsTimeSpanTicks(ticks, out var sourceInterval)) {
405-
sourceInterval = SqlDml.FunctionCall(NumToDSIntervalFunctionName, ticks / 10000000, AnsiString(SecondIntervalPart));
400+
if (SqlHelper.IsTimeSpanTicks(ticks, out var sourceExpression)) {
401+
// try to optimize and reduce calculations when TimeSpan.Ticks where used for TimeOnly(ticks) ctor
402+
var days = SqlDml.Extract(SqlIntervalPart.Day, sourceExpression);
403+
var hours = days * 24 + SqlDml.Extract(SqlIntervalPart.Hour, sourceExpression);
404+
405+
var hourString1 = SqlDml.Cast(hours, new SqlValueType(SqlType.VarChar, 3));
406+
var sourceExpressionAsString = SqlDml.FunctionCall(ToCharFunctionName, sourceExpression);
407+
var minuteToSecondsSubstring = SqlDml.Substring(sourceExpressionAsString, SqlDml.FunctionCall("INSTR", sourceExpressionAsString, AnsiString(":")) - 1 , 16);
408+
var composedTimeString1 = SqlDml.Concat(AnsiString("0 "), hourString1, minuteToSecondsSubstring);
409+
return SqlDml.FunctionCall(ToDSIntervalFunctionName, new[] { composedTimeString1 });
410+
}
411+
else {
412+
hour = SqlDml.Cast(ticks / 36000000000, new SqlValueType(SqlType.Decimal, 10, 0));
413+
minute = SqlDml.Cast((ticks / 600000000) % 60, new SqlValueType(SqlType.Decimal, 10, 0));
414+
second = SqlDml.Cast((ticks / 10000000) % 60, new SqlValueType(SqlType.Decimal, 10, 0));
415+
microsecond = SqlDml.Cast(ticks % 10000000, new SqlValueType(SqlType.Decimal, 10, 0));
406416
}
407-
return SqlDml.Cast(TimeAddInterval(zeroTime, sourceInterval), SqlType.Time);
408417
}
409418
else {
410419
throw new InvalidOperationException("Unsupported count of parameters");
411420
}
421+
422+
// using string version of time allows to control hours overflow
423+
// we cannot add hours, minutes and other parts to 00:00:00.000 time
424+
// because hours might step over 24 hours and start counting from 0.
425+
var hourString = SqlDml.Cast(hour, new SqlValueType(SqlType.VarChar, 3));
426+
var minuteString = SqlDml.Cast(minute, new SqlValueType(SqlType.VarChar, 2));
427+
var secondString = SqlDml.Cast(second, new SqlValueType(SqlType.VarChar, 2));
428+
var microsecondString = SqlDml.Cast(microsecond, new SqlValueType(SqlType.VarChar, 7));
429+
var composedTimeString = SqlDml.Concat(AnsiString("0 "), hourString, SqlDml.Literal(":"), minuteString, SqlDml.Literal(":"), secondString, SqlDml.Literal("."), microsecondString);
430+
return SqlDml.FunctionCall(ToDSIntervalFunctionName, new[] { composedTimeString });
412431
}
413432

414433
private static SqlExpression TimeToNanoseconds(SqlExpression time)

Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v11/ServerInfoProvider.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,31 @@
44
// Created by: Denis Krjuchkov
55
// Created: 2009.07.17
66

7+
using Xtensive.Sql.Info;
8+
79
namespace Xtensive.Sql.Drivers.Oracle.v11
810
{
911
internal class ServerInfoProvider : v10.ServerInfoProvider
1012
{
1113
// Constructors
1214

15+
public override DataTypeCollection GetDataTypesInfo()
16+
{
17+
const DataTypeFeatures common = DataTypeFeatures.Default | DataTypeFeatures.Nullable |
18+
DataTypeFeatures.NonKeyIndexing | DataTypeFeatures.Grouping | DataTypeFeatures.Ordering |
19+
DataTypeFeatures.Multiple;
20+
const DataTypeFeatures index = DataTypeFeatures.Indexing | DataTypeFeatures.Clustering |
21+
DataTypeFeatures.FillFactor | DataTypeFeatures.KeyConstraint;
22+
23+
var baseCollection = base.GetDataTypesInfo();
24+
25+
baseCollection.Interval = DataTypeInfo.Range(SqlType.Interval, common | index,
26+
ValueRange.TimeSpan, "INTERVAL DAY(6) TO SECONDS(7)");
27+
28+
return baseCollection;
29+
}
30+
31+
1332
public ServerInfoProvider(SqlDriver driver)
1433
: base(driver)
1534
{

0 commit comments

Comments
 (0)