Skip to content

Commit 093b82e

Browse files
Added support for IntervalNanosecond
1 parent 0c45c0a commit 093b82e

File tree

10 files changed

+61
-4
lines changed

10 files changed

+61
-4
lines changed

ClickHouse.Driver.Tests/ADO/DataReaderTests.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Data;
1+
using System;
2+
using System.Data;
23
using System.Linq;
34
using System.Threading.Tasks;
45
using ClickHouse.Driver.ADO.Readers;
@@ -200,4 +201,13 @@ public async Task ShouldEnumerateRows()
200201
Assert.That(rows, Is.EqualTo(Enumerable.Range(0, 100)).AsCollection);
201202
ClassicAssert.IsFalse(reader.Read());
202203
}
204+
205+
[Test]
206+
public async Task ShouldReadTimeSpanWhenIntervalNanosecond()
207+
{
208+
using var reader = (ClickHouseDataReader)await connection.ExecuteReaderAsync("SELECT toIntervalNanosecond(100) as value");
209+
ClassicAssert.IsTrue(reader.Read());
210+
Assert.That(reader.GetTimeSpan(0), Is.EqualTo(TimeSpan.FromTicks(1)));
211+
ClassicAssert.IsFalse(reader.Read());
212+
}
203213
}

ClickHouse.Driver.Tests/ORM/DapperTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ private static bool ShouldBeSupportedByDapper(string clickHouseType)
4646
return false;
4747
if (clickHouseType.Contains("Nested"))
4848
return false;
49+
if (clickHouseType.StartsWith("Interval")) // TimeSpan does not implement IConvertible
50+
return false;
4951
switch (clickHouseType)
5052
{
5153
case "UUID":
@@ -253,13 +255,13 @@ updated DateTime DEFAULT now()
253255
// Verify the insert worked and defaults were applied
254256
var result = await connection.QueryAsync("SELECT * FROM test.dapper_except");
255257
var row = result.Single() as IDictionary<string, object>;
256-
258+
257259
Assert.That(row, Is.Not.Null);
258260
Assert.That(row.Count, Is.EqualTo(5)); // All 5 columns should be present
259261
Assert.That(row["id"], Is.EqualTo(100));
260262
Assert.That(row["name"], Is.EqualTo("dapper-test"));
261263
Assert.That(row["value"], Is.EqualTo(123.45));
262-
264+
263265
// Verify default timestamps were set
264266
var created = (DateTime)row["created"];
265267
var updated = (DateTime)row["updated"];

ClickHouse.Driver.Tests/SQL/SqlSimpleSelectTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public async Task ShouldSelectNumericTypes()
116116
.Where(dt => dt.Contains("Int") || dt.Contains("Float"))
117117
.Where(dt => !dt.Contains("128") || TestUtilities.SupportedFeatures.HasFlag(Feature.WideTypes))
118118
.Where(dt => !dt.Contains("256") || TestUtilities.SupportedFeatures.HasFlag(Feature.WideTypes))
119+
.Where(dt => !dt.StartsWith("Interval"))
119120
.Select(dt => $"to{dt}(55)")
120121
.ToArray();
121122

@@ -244,7 +245,7 @@ public async Task ShouldGetValueDecimal()
244245
[TestCaseSource(typeof(SqlSimpleSelectTests), nameof(SimpleSelectTypes))]
245246
public async Task ShouldExecuteRandomDataSelectQuery(string type)
246247
{
247-
if (type.StartsWith("Nested") || type == "Nothing" || type.StartsWith("Variant") || type.StartsWith("Json"))
248+
if (type.StartsWith("Nested") || type == "Nothing" || type.StartsWith("Variant") || type.StartsWith("Json") || type.StartsWith("Interval"))
248249
Assert.Ignore($"Type {type} not supported by generateRandom");
249250

250251
using var reader = await connection.ExecuteReaderAsync($"SELECT * FROM generateRandom('value {type.Replace("'", "\\'")}', 10, 10, 10) LIMIT 100");

ClickHouse.Driver.Tests/Types/TypeMappingTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public class TypeMappingTests
4747
[TestCase("DateTime64(3)", ExpectedResult = typeof(DateTime))]
4848
[TestCase("DateTime64(3, 'Etc/UTC')", ExpectedResult = typeof(DateTime))]
4949

50+
[TestCase("IntervalNanosecond", ExpectedResult = typeof(TimeSpan))]
51+
5052
[TestCase("Map(String, Int32)", ExpectedResult = typeof(Dictionary<string, int>))]
5153
[TestCase("Map(Tuple(Int32, Int32), Int32)", ExpectedResult = typeof(Dictionary<Tuple<int,int>, int>))]
5254

@@ -77,6 +79,9 @@ public class TypeMappingTests
7779

7880
[TestCase(typeof(DateTime), ExpectedResult = "DateTime")]
7981

82+
// TODO: What if we map all intervals to TimeSpan?
83+
[TestCase(typeof(TimeSpan), ExpectedResult = "IntervalNanosecond")]
84+
8085
[TestCase(typeof(IPAddress), ExpectedResult = "IPv4")]
8186
[TestCase(typeof(Guid), ExpectedResult = "UUID")]
8287

ClickHouse.Driver.Tests/Utilities/TestUtilities.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ public static IEnumerable<DataTypeSample> GetDataTypeSamples()
195195
yield return new DataTypeSample("DateTime64(7, 'UTC')", typeof(DateTime), "toDateTime64('2043-03-01 18:34:04.4444444', 9, 'UTC')", new DateTime(644444444444444444, DateTimeKind.Utc));
196196
yield return new DataTypeSample("DateTime64(7, 'Pacific/Fiji')", typeof(DateTime), "toDateTime64('2043-03-01 18:34:04.4444444', 9, 'Pacific/Fiji')", new DateTime(644444444444444444, DateTimeKind.Unspecified));
197197

198+
yield return new DataTypeSample("IntervalNanosecond", typeof(TimeSpan), "toIntervalNanosecond(123456700)", TimeSpan.FromTicks(1234567));
199+
198200
yield return new DataTypeSample("Decimal32(3)", typeof(ClickHouseDecimal), "toDecimal32(123.45, 3)", new ClickHouseDecimal(123.450m));
199201
yield return new DataTypeSample("Decimal32(3)", typeof(ClickHouseDecimal), "toDecimal32(-123.45, 3)", new ClickHouseDecimal(-123.450m));
200202

ClickHouse.Driver/ADO/Readers/ClickHouseDataReader.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ public override bool IsDBNull(int ordinal)
191191
// Custom extension
192192
public BigInteger GetBigInteger(int ordinal) => (BigInteger)GetValue(ordinal);
193193

194+
// Custom extension
195+
public virtual TimeSpan GetTimeSpan(int ordinal) => (TimeSpan)GetValue(ordinal);
196+
194197
public override bool Read()
195198
{
196199
if (reader.PeekChar() == -1)

ClickHouse.Driver/Formats/HttpParameterFormatter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ internal static string Format(ClickHouseType type, object value, bool quote)
4848
case DateType dt:
4949
return Convert.ToDateTime(value, CultureInfo.InvariantCulture).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
5050

51+
case IntervalNanosecondType dt:
52+
return (((TimeSpan)value).Ticks * IntervalNanosecondType.NanosecondsPerTick).ToString(CultureInfo.InvariantCulture);
53+
5154
case StringType st:
5255
case FixedStringType tt:
5356
case Enum8Type e8t:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using ClickHouse.Driver.Formats;
3+
4+
namespace ClickHouse.Driver.Types;
5+
6+
internal class IntervalNanosecondType : IntervalType
7+
{
8+
#if NET7_0_OR_GREATER
9+
public const long NanosecondsPerTick = TimeSpan.NanosecondsPerTick;
10+
#else
11+
public const long NanosecondsPerTick = 100;
12+
#endif
13+
14+
public override Type FrameworkType => typeof(TimeSpan);
15+
16+
// Anything less than 100 nanoseconds will be truncated to 0 as TimeSpan's smallest unit is 1 tick (100 nanoseconds)
17+
public override object Read(ExtendedBinaryReader reader) => TimeSpan.FromTicks(reader.ReadInt64() / NanosecondsPerTick);
18+
19+
public override string ToString() => "IntervalNanosecond";
20+
21+
public override void Write(ExtendedBinaryWriter writer, object value) => writer.Write(((TimeSpan)value).Ticks * NanosecondsPerTick);
22+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace ClickHouse.Driver.Types;
2+
3+
internal abstract class IntervalType : ClickHouseType
4+
{
5+
public virtual bool Signed => true;
6+
}

ClickHouse.Driver/Types/TypeConverter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ static TypeConverter()
143143
RegisterParameterizedType<DateTime32Type>();
144144
RegisterParameterizedType<DateTime64Type>();
145145

146+
// Interval types
147+
RegisterPlainType<IntervalNanosecondType>();
148+
146149
// Special 'nothing' type
147150
RegisterPlainType<NothingType>();
148151

0 commit comments

Comments
 (0)