Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion ClickHouse.Driver.Tests/ADO/DataReaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Data;
using System;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using ClickHouse.Driver.ADO.Readers;
Expand Down Expand Up @@ -200,4 +201,13 @@ public async Task ShouldEnumerateRows()
Assert.That(rows, Is.EqualTo(Enumerable.Range(0, 100)).AsCollection);
ClassicAssert.IsFalse(reader.Read());
}

[Test]
public async Task ShouldReadTimeSpanWhenIntervalNanosecond()
{
using var reader = (ClickHouseDataReader)await connection.ExecuteReaderAsync("SELECT toIntervalNanosecond(100) as value");
ClassicAssert.IsTrue(reader.Read());
Assert.That(reader.GetTimeSpan(0), Is.EqualTo(TimeSpan.FromTicks(1)));
ClassicAssert.IsFalse(reader.Read());
}
}
6 changes: 4 additions & 2 deletions ClickHouse.Driver.Tests/ORM/DapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ private static bool ShouldBeSupportedByDapper(string clickHouseType)
return false;
if (clickHouseType.Contains("Nested"))
return false;
if (clickHouseType.StartsWith("Interval")) // TimeSpan does not implement IConvertible
return false;
switch (clickHouseType)
{
case "UUID":
Expand Down Expand Up @@ -253,13 +255,13 @@ updated DateTime DEFAULT now()
// Verify the insert worked and defaults were applied
var result = await connection.QueryAsync("SELECT * FROM test.dapper_except");
var row = result.Single() as IDictionary<string, object>;

Assert.That(row, Is.Not.Null);
Assert.That(row.Count, Is.EqualTo(5)); // All 5 columns should be present
Assert.That(row["id"], Is.EqualTo(100));
Assert.That(row["name"], Is.EqualTo("dapper-test"));
Assert.That(row["value"], Is.EqualTo(123.45));

// Verify default timestamps were set
var created = (DateTime)row["created"];
var updated = (DateTime)row["updated"];
Expand Down
3 changes: 2 additions & 1 deletion ClickHouse.Driver.Tests/SQL/SqlSimpleSelectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public async Task ShouldSelectNumericTypes()
.Where(dt => dt.Contains("Int") || dt.Contains("Float"))
.Where(dt => !dt.Contains("128") || TestUtilities.SupportedFeatures.HasFlag(Feature.WideTypes))
.Where(dt => !dt.Contains("256") || TestUtilities.SupportedFeatures.HasFlag(Feature.WideTypes))
.Where(dt => !dt.StartsWith("Interval"))
.Select(dt => $"to{dt}(55)")
.ToArray();

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

using var reader = await connection.ExecuteReaderAsync($"SELECT * FROM generateRandom('value {type.Replace("'", "\\'")}', 10, 10, 10) LIMIT 100");
Expand Down
5 changes: 5 additions & 0 deletions ClickHouse.Driver.Tests/Types/TypeMappingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class TypeMappingTests
[TestCase("DateTime64(3)", ExpectedResult = typeof(DateTime))]
[TestCase("DateTime64(3, 'Etc/UTC')", ExpectedResult = typeof(DateTime))]

[TestCase("IntervalNanosecond", ExpectedResult = typeof(TimeSpan))]

[TestCase("Map(String, Int32)", ExpectedResult = typeof(Dictionary<string, int>))]
[TestCase("Map(Tuple(Int32, Int32), Int32)", ExpectedResult = typeof(Dictionary<Tuple<int,int>, int>))]

Expand Down Expand Up @@ -77,6 +79,9 @@ public class TypeMappingTests

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

// TODO: What if we map all intervals to TimeSpan?
[TestCase(typeof(TimeSpan), ExpectedResult = "IntervalNanosecond")]

[TestCase(typeof(IPAddress), ExpectedResult = "IPv4")]
[TestCase(typeof(Guid), ExpectedResult = "UUID")]

Expand Down
2 changes: 2 additions & 0 deletions ClickHouse.Driver.Tests/Utilities/TestUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ public static IEnumerable<DataTypeSample> GetDataTypeSamples()
yield return new DataTypeSample("DateTime64(7, 'UTC')", typeof(DateTime), "toDateTime64('2043-03-01 18:34:04.4444444', 9, 'UTC')", new DateTime(644444444444444444, DateTimeKind.Utc));
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));

yield return new DataTypeSample("IntervalNanosecond", typeof(TimeSpan), "toIntervalNanosecond(123456700)", TimeSpan.FromTicks(1234567));

yield return new DataTypeSample("Decimal32(3)", typeof(ClickHouseDecimal), "toDecimal32(123.45, 3)", new ClickHouseDecimal(123.450m));
yield return new DataTypeSample("Decimal32(3)", typeof(ClickHouseDecimal), "toDecimal32(-123.45, 3)", new ClickHouseDecimal(-123.450m));

Expand Down
3 changes: 3 additions & 0 deletions ClickHouse.Driver/ADO/Readers/ClickHouseDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ public override bool IsDBNull(int ordinal)
// Custom extension
public BigInteger GetBigInteger(int ordinal) => (BigInteger)GetValue(ordinal);

// Custom extension
public virtual TimeSpan GetTimeSpan(int ordinal) => (TimeSpan)GetValue(ordinal);

public override bool Read()
{
if (reader.PeekChar() == -1)
Expand Down
3 changes: 3 additions & 0 deletions ClickHouse.Driver/Formats/HttpParameterFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ internal static string Format(ClickHouseType type, object value, bool quote)
case DateType dt:
return Convert.ToDateTime(value, CultureInfo.InvariantCulture).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);

case IntervalNanosecondType dt:
return (((TimeSpan)value).Ticks * IntervalNanosecondType.NanosecondsPerTick).ToString(CultureInfo.InvariantCulture);

case StringType st:
case FixedStringType tt:
case Enum8Type e8t:
Expand Down
22 changes: 22 additions & 0 deletions ClickHouse.Driver/Types/IntervalNanosecondType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using ClickHouse.Driver.Formats;

namespace ClickHouse.Driver.Types;

internal class IntervalNanosecondType : IntervalType
{
#if NET7_0_OR_GREATER
public const long NanosecondsPerTick = TimeSpan.NanosecondsPerTick;
#else
public const long NanosecondsPerTick = 100;
#endif

public override Type FrameworkType => typeof(TimeSpan);

// Anything less than 100 nanoseconds will be truncated to 0 as TimeSpan's smallest unit is 1 tick (100 nanoseconds)
public override object Read(ExtendedBinaryReader reader) => TimeSpan.FromTicks(reader.ReadInt64() / NanosecondsPerTick);

public override string ToString() => "IntervalNanosecond";

public override void Write(ExtendedBinaryWriter writer, object value) => writer.Write(((TimeSpan)value).Ticks * NanosecondsPerTick);
}
6 changes: 6 additions & 0 deletions ClickHouse.Driver/Types/IntervalType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ClickHouse.Driver.Types;

internal abstract class IntervalType : ClickHouseType
{
public virtual bool Signed => true;
}
3 changes: 3 additions & 0 deletions ClickHouse.Driver/Types/TypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@
RegisterParameterizedType<DateTime32Type>();
RegisterParameterizedType<DateTime64Type>();

// Interval types
RegisterPlainType<IntervalNanosecondType>();

// Special 'nothing' type
RegisterPlainType<NothingType>();

Expand Down Expand Up @@ -355,7 +358,7 @@
// case 0x2E: return new SimpleAggregateFunctionType(); // TODO function
// case 0x2F: return new NestedType(); // TODO nested types
case 0x30:
var _serializationVersion = reader.ReadByte(); // <uint8_serialization_version>

Check warning on line 361 in ClickHouse.Driver/Types/TypeConverter.cs

View workflow job for this annotation

GitHub Actions / Build & Run Examples

Variable '_serializationVersion' should begin with lower-case letter (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md)

Check warning on line 361 in ClickHouse.Driver/Types/TypeConverter.cs

View workflow job for this annotation

GitHub Actions / Short

Variable '_serializationVersion' should begin with lower-case letter (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md)
var _maxDynamicPaths = reader.Read7BitEncodedInt(); // <var_int_max_dynamic_paths>
var _maxDynamicTypes = reader.ReadInt32(); // <uint8_max_dynamic_types>
return new JsonType(); // TODO JSON settings
Expand Down
Loading