Skip to content

Commit 2d80867

Browse files
committed
Date/Time operations support
AddXxx() methods, operators, .ToString() support - for SQLite: fixed reading of DateOnly/TimeOnly values from DbDataReader - for MySQL: return default reading from DbDataReader - other minor changes(formatting, copyright, etc.)
1 parent 2af4f96 commit 2d80867

25 files changed

Lines changed: 847 additions & 335 deletions

File tree

Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2011-2022 Xtensive LLC.
1+
// Copyright (C) 2011-2023 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Csaba Beer
@@ -176,6 +176,14 @@ public override void Visit(SqlBinary node)
176176
case SqlNodeType.DateTimeMinusDateTime:
177177
DateTimeSubtractDateTime(node.Left, node.Right).AcceptVisitor(this);
178178
return;
179+
#if NET6_0_OR_GREATER //DO_DATEONLY
180+
case SqlNodeType.TimePlusInterval:
181+
TimeAddInterval(node.Left, node.Right).AcceptVisitor(this);
182+
return;
183+
case SqlNodeType.TimeMinusTime:
184+
TimeSubtractTime(node.Left, node.Right).AcceptVisitor(this);
185+
return;
186+
#endif
179187
case SqlNodeType.Modulo:
180188
Visit(SqlDml.FunctionCall(translator.TranslateToString(SqlNodeType.Modulo), node.Left, node.Right));
181189
return;
@@ -217,32 +225,47 @@ public override void Visit(SqlFunctionCall node)
217225
case SqlFunctionType.DateTimeAddYears:
218226
Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
219227
return;
220-
#if NET6_0_OR_GREATER //DO_DATEONLY
221-
case SqlFunctionType.DateAddDays:
222-
Visit(DateAddDay(node.Arguments[0], node.Arguments[1]));
223-
return;
224-
#endif
225228
case SqlFunctionType.DateTimeConstruct:
226229
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime),
227230
node.Arguments[0] - 2001),
228231
node.Arguments[1] - 1),
229232
node.Arguments[2] - 1));
230233
return;
231234
#if NET6_0_OR_GREATER //DO_DATEONLY
235+
case SqlFunctionType.DateAddYears:
236+
Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
237+
return;
238+
case SqlFunctionType.DateAddMonths:
239+
Visit(DateAddMonth(node.Arguments[0], node.Arguments[1]));
240+
return;
241+
case SqlFunctionType.DateAddDays:
242+
Visit(DateAddDay(node.Arguments[0], node.Arguments[1]));
243+
return;
232244
case SqlFunctionType.DateConstruct:
233245
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateOnly(2001, 1, 1)), SqlType.Date),
234246
node.Arguments[0] - 2001),
235247
node.Arguments[1] - 1),
236248
node.Arguments[2] - 1));
237249
return;
238-
case SqlFunctionType.TimeConstruct: {
250+
case SqlFunctionType.TimeAddHours:
251+
Visit(DateAddHour(node.Arguments[0], node.Arguments[1]));
252+
return;
253+
case SqlFunctionType.TimeAddMinutes:
254+
Visit(DateAddMinute(node.Arguments[0], node.Arguments[1]));
255+
return;
256+
case SqlFunctionType.TimeConstruct:
239257
Visit(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Cast(SqlDml.Literal(new TimeOnly(0, 0, 0)), SqlType.Time),
240258
node.Arguments[0]),
241259
node.Arguments[1]),
242260
node.Arguments[2]),
243261
node.Arguments[3]));
244262
return;
245-
}
263+
case SqlFunctionType.DateToString:
264+
Visit(DateToString(node.Arguments[0]));
265+
return;
266+
case SqlFunctionType.TimeToString:
267+
Visit(TimeToString(node.Arguments[0]));
268+
return;
246269
#endif
247270
case SqlFunctionType.DateTimeToStringIso:
248271
Visit(DateTimeToStringIso(node.Arguments[0]));
@@ -262,7 +285,7 @@ public override void Visit(SqlAlterSequence node)
262285
translator.Translate(context, node, NodeSection.Exit);
263286
}
264287

265-
#region Static helpers
288+
#region Static helpers
266289

267290
protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, SqlExpression date2)
268291
{
@@ -272,13 +295,25 @@ protected static SqlExpression DateTimeSubtractDateTime(SqlExpression date1, Sql
272295
NanosecondsPerMillisecond);
273296
}
274297

298+
#if NET6_0_OR_GREATER //DO_DATEONLY
299+
protected static SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2)
300+
{
301+
return SqlDml.Modulo(
302+
NanosecondsPerDay + CastToLong(DateDiffMillisecond(time2, time1)) * NanosecondsPerMillisecond,
303+
NanosecondsPerDay);
304+
}
305+
#endif
306+
275307
protected static SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpression interval)
276308
{
277309
return DateAddMillisecond(
278310
DateAddDay(date, interval / NanosecondsPerDay),
279311
(interval / NanosecondsPerMillisecond) % (MillisecondsPerDay));
280312
}
281313

314+
protected static SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) =>
315+
DateAddMillisecond(time, (interval / NanosecondsPerMillisecond) % (MillisecondsPerDay));
316+
282317
protected static SqlCast CastToLong(SqlExpression arg) => SqlDml.Cast(arg, SqlType.Int64);
283318

284319
protected static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) =>
@@ -320,6 +355,18 @@ protected static SqlUserFunctionCall BitXor(SqlExpression left, SqlExpression ri
320355
protected static SqlUserFunctionCall BitNot(SqlExpression operand) =>
321356
SqlDml.FunctionCall("BIN_NOT", operand);
322357

358+
#if NET6_0_OR_GREATER //DO_DATEONLY
359+
protected static SqlFunctionCall DateToString(SqlExpression date)
360+
{
361+
return SqlDml.Substring(date, 0, 10);;
362+
}
363+
364+
protected static SqlConcat TimeToString(SqlExpression time)
365+
{
366+
return SqlDml.Concat(SqlDml.Substring(time, 0, 12), SqlDml.Literal("0000"));
367+
}
368+
#endif
369+
323370
protected static SqlConcat DateTimeToStringIso(SqlExpression dateTime)
324371
{
325372
var date = SqlDml.Substring(dateTime, 0, 10);

Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Translator.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2011-2022 Xtensive LLC.
1+
// Copyright (C) 2011-2023 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Csaba Beer
@@ -122,6 +122,9 @@ public override void Translate(IOutput output, SqlNodeType type)
122122
case SqlNodeType.Modulo:
123123
_ = output.Append("MOD"); break;
124124
case SqlNodeType.DateTimeMinusDateTime:
125+
#if NET6_0_OR_GREATER //DO_DATEONLY
126+
case SqlNodeType.TimeMinusTime:
127+
#endif
125128
_ = output.Append("-"); break;
126129
case SqlNodeType.Except:
127130
case SqlNodeType.Intersect:

Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs

Lines changed: 106 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2011-2022 Xtensive LLC.
1+
// Copyright (C) 2011-2023 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Malisa Ncube
@@ -94,6 +94,14 @@ public override void Visit(SqlBinary node)
9494
case SqlNodeType.DateTimeMinusInterval:
9595
DateTimeAddInterval(node.Left, -node.Right).AcceptVisitor(this);
9696
return;
97+
#if NET6_0_OR_GREATER //DO_DATEONLY
98+
case SqlNodeType.TimePlusInterval:
99+
TimeAddInterval(node.Left, node.Right).AcceptVisitor(this);
100+
return;
101+
case SqlNodeType.TimeMinusTime:
102+
TimeSubtractTime(node.Left, node.Right).AcceptVisitor(this);
103+
return;
104+
#endif
97105
default:
98106
base.Visit(node);
99107
return;
@@ -137,13 +145,14 @@ public override void Visit(SqlQueryExpression node)
137145
/// <inheritdoc/>
138146
public override void Visit(SqlFunctionCall node)
139147
{
148+
var arguments = node.Arguments;
149+
140150
switch (node.FunctionType) {
141151
case SqlFunctionType.Truncate:
142-
var argument = node.Arguments[0];
143-
SqlDml.FunctionCall("TRUNCATE", argument, SqlDml.Literal(0)).AcceptVisitor(this);
152+
SqlDml.FunctionCall("TRUNCATE", arguments[0], SqlDml.Literal(0)).AcceptVisitor(this);
144153
return;
145154
case SqlFunctionType.Concat:
146-
Visit(SqlDml.Concat(node.Arguments.ToArray(node.Arguments.Count)));
155+
Visit(SqlDml.Concat(arguments.ToArray(node.Arguments.Count)));
147156
return;
148157
case SqlFunctionType.CharLength:
149158
SqlDml.FunctionCall(translator.TranslateToString(SqlFunctionType.CharLength), node.Arguments[0]).AcceptVisitor(this);
@@ -157,44 +166,75 @@ public override void Visit(SqlFunctionCall node)
157166
SqlDml.FunctionCall(translator.TranslateToString(SqlFunctionType.Rand)).AcceptVisitor(this);
158167
return;
159168
case SqlFunctionType.Square:
160-
SqlDml.Power(node.Arguments[0], 2).AcceptVisitor(this);
169+
SqlDml.Power(arguments[0], 2).AcceptVisitor(this);
161170
return;
162171
case SqlFunctionType.IntervalToMilliseconds:
163-
Visit(CastToLong(node.Arguments[0]) / NanosecondsPerMillisecond);
172+
Visit(CastToLong(arguments[0]) / NanosecondsPerMillisecond);
164173
return;
165174
case SqlFunctionType.IntervalConstruct:
166175
case SqlFunctionType.IntervalToNanoseconds:
167-
Visit(CastToLong(node.Arguments[0]));
176+
Visit(CastToLong(arguments[0]));
168177
return;
169178
case SqlFunctionType.DateTimeAddMonths:
170-
Visit(DateTimeAddMonth(node.Arguments[0], node.Arguments[1]));
179+
Visit(DateTimeAddMonth(arguments[0], arguments[1]));
171180
return;
172181
case SqlFunctionType.DateTimeAddYears:
173-
Visit(DateTimeAddYear(node.Arguments[0], node.Arguments[1]));
182+
Visit(DateTimeAddYear(arguments[0], arguments[1]));
174183
return;
175184
case SqlFunctionType.DateTimeConstruct:
176185
Visit(DateTimeAddDay(DateTimeAddMonth(DateTimeAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)),
177-
node.Arguments[0] - 2001),
178-
node.Arguments[1] - 1),
179-
node.Arguments[2] - 1));
186+
arguments[0] - 2001),
187+
arguments[1] - 1),
188+
arguments[2] - 1));
180189
return;
181190
#if NET6_0_OR_GREATER //DO_DATEONLY
191+
case SqlFunctionType.DateAddYears:
192+
Visit(DateAddYear(arguments[0], arguments[1]));
193+
return;
194+
case SqlFunctionType.DateAddMonths:
195+
Visit(DateAddMonth(arguments[0], arguments[1]));
196+
return;
197+
case SqlFunctionType.DateAddDays:
198+
Visit(DateAddDay(arguments[0], arguments[1]));
199+
return;
182200
case SqlFunctionType.DateConstruct:
183201
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateOnly(2001, 1, 1)),
184-
node.Arguments[0] - 2001),
185-
node.Arguments[1] - 1),
186-
node.Arguments[2] - 1));
202+
arguments[0] - 2001),
203+
arguments[1] - 1),
204+
arguments[2] - 1));
205+
return;
206+
case SqlFunctionType.TimeAddHours:
207+
Visit(SqlDml.FunctionCall("TIME", SqlDml.FunctionCall(
208+
"DATE_ADD",
209+
SqlDml.Literal(new DateTime(2001, 1, 1)),
210+
SqlDml.RawConcat(
211+
SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.FunctionCall("TIME_TO_SEC", arguments[0]) + arguments[1] * 3600),
212+
SqlDml.Native("SECOND")))));
213+
return;
214+
case SqlFunctionType.TimeAddMinutes:
215+
Visit(SqlDml.FunctionCall("TIME",
216+
SqlDml.FunctionCall("DATE_ADD",
217+
SqlDml.Literal(new DateTime(2001, 1, 1)),
218+
SqlDml.RawConcat(
219+
SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.FunctionCall("TIME_TO_SEC", arguments[0]) + arguments[1] * 60),
220+
SqlDml.Native("SECOND")))));
187221
return;
188222
case SqlFunctionType.TimeConstruct:
189223
Visit(SqlDml.FunctionCall("TIME", TimeAddMillisecond(TimeAddSecond(TimeAddMinute(TimeAddHour(SqlDml.Literal(new DateTime(2001, 1, 1)),
190-
node.Arguments[0]),
191-
node.Arguments[1]),
192-
node.Arguments[2]),
193-
node.Arguments[3])));
224+
arguments[0]),
225+
arguments[1]),
226+
arguments[2]),
227+
arguments[3])));
228+
return;
229+
case SqlFunctionType.DateToString:
230+
Visit(DateToString(arguments[0]));
231+
return;
232+
case SqlFunctionType.TimeToString:
233+
Visit(TimeToString(arguments[0]));
194234
return;
195235
#endif
196236
case SqlFunctionType.DateTimeToStringIso:
197-
Visit(DateTimeToStringIso(node.Arguments[0]));
237+
Visit(DateTimeToStringIso(arguments[0]));
198238
return;
199239
}
200240

@@ -204,7 +244,7 @@ public override void Visit(SqlFunctionCall node)
204244
#if NET6_0_OR_GREATER //DO_DATEONLY
205245
public override void Visit(SqlPlaceholder node)
206246
{
207-
if(node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type==typeof(TimeOnly)) {
247+
if (node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type == typeof(TimeOnly)) {
208248
_ = context.Output.Append("TIME(");
209249
base.Visit(node);
210250
_ = context.Output.Append(")");
@@ -263,51 +303,81 @@ protected virtual SqlExpression DateTimeAddInterval(SqlExpression date, SqlExpre
263303
(interval / NanosecondsPerMillisecond * NanosecondsPerMicrosecond) % (MillisecondsPerDay * NanosecondsPerMicrosecond));
264304
}
265305

306+
#if NET6_0_OR_GREATER //DO_DATEONLY
307+
protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpression time2) =>
308+
SqlDml.Modulo(
309+
NanosecondsPerDay + CastToDecimal(SqlDml.FunctionCall("TIME_TO_SEC", time1) - SqlDml.FunctionCall("TIME_TO_SEC", time2), 18, 0) * NanosecondsPerSecond,
310+
NanosecondsPerDay);
311+
312+
protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) =>
313+
314+
SqlDml.FunctionCall("TIME",
315+
SqlDml.FunctionCall(
316+
"DATE_ADD",
317+
SqlDml.Literal(new DateTime(2001, 1, 1)),
318+
SqlDml.RawConcat(
319+
SqlDml.RawConcat(SqlDml.Native("INTERVAL "),
320+
SqlDml.FunctionCall("TIME_TO_SEC", time) + interval / NanosecondsPerSecond),
321+
SqlDml.Native("SECOND"))));
322+
#endif
323+
266324
#region Static helpers
267325

268-
private static SqlCast CastToLong(SqlExpression arg) => SqlDml.Cast(arg, SqlType.Int64);
326+
protected static SqlCast CastToLong(SqlExpression arg) => SqlDml.Cast(arg, SqlType.Int64);
269327

270-
private static SqlCast CastToDecimal(SqlExpression arg, short precision, short scale) =>
328+
protected static SqlCast CastToDecimal(SqlExpression arg, short precision, short scale) =>
271329
SqlDml.Cast(arg, SqlType.Decimal, precision, scale);
272330

273-
private static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) =>
331+
protected static SqlUserFunctionCall DateDiffDay(SqlExpression date1, SqlExpression date2) =>
274332
SqlDml.FunctionCall("DATEDIFF", date1, date2);
275333

276-
private static SqlUserFunctionCall DateTimeDiffMicrosecond(SqlExpression datetime1, SqlExpression datetime2) =>
334+
protected static SqlUserFunctionCall DateTimeDiffMicrosecond(SqlExpression datetime1, SqlExpression datetime2) =>
277335
SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("MICROSECOND"), datetime1, datetime2);
278336

279-
private static SqlUserFunctionCall DateTimeAddYear(SqlExpression datetime, SqlExpression years) =>
337+
protected static SqlUserFunctionCall DateTimeDiffSecond(SqlExpression datetime1, SqlExpression datetime2) =>
338+
SqlDml.FunctionCall("TIMESTAMPDIFF", SqlDml.Native("SECOND"), datetime1, datetime2);
339+
340+
protected static SqlUserFunctionCall DateTimeAddYear(SqlExpression datetime, SqlExpression years) =>
280341
SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("YEAR"), years, datetime);
281342

282-
private static SqlUserFunctionCall DateTimeAddMonth(SqlExpression datetime, SqlExpression months) =>
343+
protected static SqlUserFunctionCall DateTimeAddMonth(SqlExpression datetime, SqlExpression months) =>
283344
SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MONTH"), months, datetime);
284345

285-
private static SqlUserFunctionCall DateTimeAddDay(SqlExpression datetime, SqlExpression days) =>
346+
protected static SqlUserFunctionCall DateTimeAddDay(SqlExpression datetime, SqlExpression days) =>
286347
SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("DAY"), days, datetime);
287348

288-
private static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) =>
349+
protected static SqlUserFunctionCall DateTimeAddHour(SqlExpression datetime, SqlExpression days) =>
350+
SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("HOUR"), days, datetime);
351+
352+
protected static SqlUserFunctionCall DateTimeAddMicrosecond(SqlExpression datetime, SqlExpression microseconds) =>
289353
SqlDml.FunctionCall("TIMESTAMPADD", SqlDml.Native("MICROSECOND"), microseconds, datetime);
290354

291355
#if NET6_0_OR_GREATER //DO_DATEONLY
292-
private static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) =>
356+
protected static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpression years) =>
293357
SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(years, SqlDml.Native("YEAR"))));
294358

295-
private static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) =>
359+
protected static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) =>
296360
SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(months, SqlDml.Native("MONTH"))));
297-
private static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) =>
361+
protected static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) =>
298362
SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(days, SqlDml.Native("DAY"))));
299363

300-
private static SqlUserFunctionCall TimeAddHour(SqlExpression time, SqlExpression hours) =>
364+
protected static SqlUserFunctionCall TimeAddHour(SqlExpression time, SqlExpression hours) =>
301365
SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(hours, SqlDml.Native("HOUR"))));
302366

303-
private static SqlUserFunctionCall TimeAddMinute(SqlExpression time, SqlExpression minutes) =>
367+
protected static SqlUserFunctionCall TimeAddMinute(SqlExpression time, SqlExpression minutes) =>
304368
SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(minutes, SqlDml.Native("MINUTE"))));
305369

306-
private static SqlUserFunctionCall TimeAddSecond(SqlExpression time, SqlExpression seconds) =>
370+
protected static SqlUserFunctionCall TimeAddSecond(SqlExpression time, SqlExpression seconds) =>
307371
SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(seconds, SqlDml.Native("SECOND"))));
308372

309-
private static SqlUserFunctionCall TimeAddMillisecond(SqlExpression time, SqlExpression millisecond) =>
373+
protected static SqlUserFunctionCall TimeAddMillisecond(SqlExpression time, SqlExpression millisecond) =>
310374
SqlDml.FunctionCall("DATE_ADD", time, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(millisecond * 1000, SqlDml.Native("MICROSECOND"))));
375+
376+
protected static SqlUserFunctionCall DateToString(SqlExpression dateTime) =>
377+
SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%Y-%m-%d");
378+
379+
protected static SqlUserFunctionCall TimeToString(SqlExpression dateTime) =>
380+
SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%H:%i:%s.%f0");
311381
#endif
312382

313383
protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) =>

0 commit comments

Comments
 (0)