Skip to content

Commit ce6b36c

Browse files
feat: added support new datetime types (#517)
1 parent 8949ae2 commit ce6b36c

File tree

15 files changed

+469
-355
lines changed

15 files changed

+469
-355
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
ports: [ "2135:2135", "2136:2136", "8765:8765" ]
2323
env:
2424
YDB_LOCAL_SURVIVE_RESTART: true
25-
YDB_FEATURE_FLAGS: enable_parameterized_decimal
25+
YDB_FEATURE_FLAGS: enable_parameterized_decimal,enable_table_datetime64
2626
options: '--name ydb-local -h localhost'
2727

2828
steps:

src/Ydb.Sdk/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
- Feat ADO.NET: Deleted support for `DateTimeOffset` was a mistake.
2+
- Feat ADO.NET: Added support for `Date32`, `Datetime64`, `Timestamp64` and `Interval64` types in YDB.
13
- Feat: Implement `YdbRetryPolicy` with AWS-inspired Exponential Backoff and Jitter.
24
- Dev: LogLevel `Warning` -> `Debug` on DeleteSession has been `RpcException`.
35

src/Ydb.Sdk/src/Ado/Internal/SqlParam.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using Ydb.Sdk.Ado.YdbType;
2-
31
namespace Ydb.Sdk.Ado.Internal;
42

53
internal interface ISqlParam
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Ydb.Sdk.Ado.Internal;
2+
3+
internal static class TimeSpanUtils
4+
{
5+
internal const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;
6+
}

src/Ydb.Sdk/src/Ado/YdbType/YdbTypedValueExtensions.cs renamed to src/Ydb.Sdk/src/Ado/Internal/YdbTypedValueExtensions.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Google.Protobuf;
22
using Google.Protobuf.WellKnownTypes;
33

4-
namespace Ydb.Sdk.Ado.YdbType;
4+
namespace Ydb.Sdk.Ado.Internal;
55

66
internal static class YdbTypedValueExtensions
77
{
@@ -123,22 +123,39 @@ internal static TypedValue Uuid(this Guid value)
123123
internal static TypedValue Date(this DateTime value) => MakePrimitiveTypedValue(Type.Types.PrimitiveTypeId.Date,
124124
new Ydb.Value { Uint32Value = (uint)(value.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerDay) });
125125

126+
internal static TypedValue Date32(this DateTime value) => MakePrimitiveTypedValue(Type.Types.PrimitiveTypeId.Date32,
127+
new Ydb.Value { Int32Value = (int)(value.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerDay) });
128+
126129
internal static TypedValue Datetime(this DateTime dateTimeValue) => MakePrimitiveTypedValue(
127-
Type.Types.PrimitiveTypeId.Datetime,
128-
new Ydb.Value
130+
Type.Types.PrimitiveTypeId.Datetime, new Ydb.Value
129131
{ Uint32Value = (uint)(dateTimeValue.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerSecond) }
130132
);
131133

134+
internal static TypedValue Datetime64(this DateTime dateTimeValue) => MakePrimitiveTypedValue(
135+
Type.Types.PrimitiveTypeId.Datetime64,
136+
new Ydb.Value { Int64Value = dateTimeValue.Subtract(DateTime.UnixEpoch).Ticks / TimeSpan.TicksPerSecond }
137+
);
138+
132139
internal static TypedValue Timestamp(this DateTime dateTimeValue) => MakePrimitiveTypedValue(
133140
Type.Types.PrimitiveTypeId.Timestamp, new Ydb.Value
134141
{
135-
Uint64Value = (ulong)(dateTimeValue.Ticks - DateTime.UnixEpoch.Ticks) * Duration.NanosecondsPerTick / 1000
142+
Uint64Value = (ulong)(dateTimeValue.Ticks - DateTime.UnixEpoch.Ticks) / TimeSpanUtils.TicksPerMicrosecond
136143
}
137144
);
138145

146+
internal static TypedValue Timestamp64(this DateTime dateTimeValue) => MakePrimitiveTypedValue(
147+
Type.Types.PrimitiveTypeId.Timestamp64, new Ydb.Value
148+
{ Int64Value = (dateTimeValue.Ticks - DateTime.UnixEpoch.Ticks) / TimeSpanUtils.TicksPerMicrosecond }
149+
);
150+
139151
internal static TypedValue Interval(this TimeSpan timeSpanValue) => MakePrimitiveTypedValue(
140152
Type.Types.PrimitiveTypeId.Interval,
141-
new Ydb.Value { Int64Value = timeSpanValue.Ticks * Duration.NanosecondsPerTick / 1000 }
153+
new Ydb.Value { Int64Value = timeSpanValue.Ticks / TimeSpanUtils.TicksPerMicrosecond }
154+
);
155+
156+
internal static TypedValue Interval64(this TimeSpan timeSpanValue) => MakePrimitiveTypedValue(
157+
Type.Types.PrimitiveTypeId.Interval64,
158+
new Ydb.Value { Int64Value = timeSpanValue.Ticks / TimeSpanUtils.TicksPerMicrosecond }
142159
);
143160

144161
internal static TypedValue List(this IReadOnlyList<TypedValue> values)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
namespace Ydb.Sdk.Ado.Internal;
2+
3+
internal static class YdbValueExtensions
4+
{
5+
private static readonly DateTime UnixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
6+
7+
internal static bool IsNull(this Ydb.Value value) => value.ValueCase == Ydb.Value.ValueOneofCase.NullFlagValue;
8+
9+
internal static bool GetBool(this Ydb.Value value) => value.BoolValue;
10+
11+
internal static sbyte GetInt8(this Ydb.Value value) => (sbyte)value.Int32Value;
12+
13+
internal static byte GetUint8(this Ydb.Value value) => (byte)value.Uint32Value;
14+
15+
internal static short GetInt16(this Ydb.Value value) => (short)value.Int32Value;
16+
17+
internal static ushort GetUint16(this Ydb.Value value) => (ushort)value.Uint32Value;
18+
19+
internal static int GetInt32(this Ydb.Value value) => value.Int32Value;
20+
21+
internal static uint GetUint32(this Ydb.Value value) => value.Uint32Value;
22+
23+
internal static long GetInt64(this Ydb.Value value) => value.Int64Value;
24+
25+
internal static ulong GetUint64(this Ydb.Value value) => value.Uint64Value;
26+
27+
internal static float GetFloat(this Ydb.Value value) => value.FloatValue;
28+
29+
internal static double GetDouble(this Ydb.Value value) => value.DoubleValue;
30+
31+
internal static DateTime GetDate(this Ydb.Value value) =>
32+
UnixEpoch.AddTicks(value.Uint32Value * TimeSpan.TicksPerDay);
33+
34+
internal static DateTime GetDate32(this Ydb.Value value) =>
35+
UnixEpoch.AddTicks(value.Int32Value * TimeSpan.TicksPerDay);
36+
37+
internal static DateTime GetDatetime(this Ydb.Value value) =>
38+
UnixEpoch.AddTicks(value.Uint32Value * TimeSpan.TicksPerSecond);
39+
40+
internal static DateTime GetDatetime64(this Ydb.Value value) =>
41+
UnixEpoch.AddTicks(value.Int64Value * TimeSpan.TicksPerSecond);
42+
43+
internal static DateTime GetTimestamp(this Ydb.Value value) =>
44+
UnixEpoch.AddTicks((long)(value.Uint64Value * TimeSpanUtils.TicksPerMicrosecond));
45+
46+
internal static DateTime GetTimestamp64(this Ydb.Value value) =>
47+
UnixEpoch.AddTicks(value.Int64Value * TimeSpanUtils.TicksPerMicrosecond);
48+
49+
internal static TimeSpan GetInterval(this Ydb.Value value) =>
50+
TimeSpan.FromTicks(value.Int64Value * TimeSpanUtils.TicksPerMicrosecond);
51+
52+
internal static TimeSpan GetInterval64(this Ydb.Value value) =>
53+
TimeSpan.FromTicks(value.Int64Value * TimeSpanUtils.TicksPerMicrosecond);
54+
55+
internal static byte[] GetBytes(this Ydb.Value value) => value.BytesValue.ToByteArray();
56+
57+
internal static string GetText(this Ydb.Value value) => value.TextValue;
58+
59+
internal static string GetJson(this Ydb.Value value) => value.TextValue;
60+
61+
internal static string GetJsonDocument(this Ydb.Value value) => value.TextValue;
62+
63+
internal static Guid GetUuid(this Ydb.Value value)
64+
{
65+
var high = value.High128;
66+
var low = value.Low128;
67+
68+
var lowBytes = BitConverter.GetBytes(low);
69+
var highBytes = BitConverter.GetBytes(high);
70+
71+
var guidBytes = new byte[16];
72+
Array.Copy(lowBytes, 0, guidBytes, 0, 8);
73+
Array.Copy(highBytes, 0, guidBytes, 8, 8);
74+
75+
return new Guid(guidBytes);
76+
}
77+
78+
internal static decimal GetDecimal(this Ydb.Value value, uint scale)
79+
{
80+
var lo = value.Low128;
81+
var hi = value.High128;
82+
var isNegative = (hi & 0x8000_0000_0000_0000UL) != 0;
83+
unchecked
84+
{
85+
if (isNegative)
86+
{
87+
if (lo == 0)
88+
hi--;
89+
90+
lo--;
91+
lo = ~lo;
92+
hi = ~hi;
93+
}
94+
}
95+
96+
if (hi >> 32 != 0)
97+
throw new OverflowException("Value does not fit into decimal");
98+
99+
return new decimal((int)lo, (int)(lo >> 32), (int)hi, isNegative, (byte)scale);
100+
}
101+
}

src/Ydb.Sdk/src/Ado/ThrowHelper.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/Ydb.Sdk/src/Ado/YdbCommand.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Diagnostics.CodeAnalysis;
44
using System.Text;
55
using Ydb.Sdk.Ado.Internal;
6-
using Ydb.Sdk.Ado.YdbType;
76

87
namespace Ydb.Sdk.Ado;
98

0 commit comments

Comments
 (0)