Skip to content

Commit 18a5ce8

Browse files
committed
Add support for appending all TimeStamp types
1 parent 4a76c9c commit 18a5ce8

File tree

4 files changed

+91
-42
lines changed

4 files changed

+91
-42
lines changed

DuckDB.NET.Data/Internal/Writer/DateTimeVectorDataWriter.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,32 @@ namespace DuckDB.NET.Data.Internal.Writer;
55

66
internal sealed unsafe class DateTimeVectorDataWriter(IntPtr vector, void* vectorData, DuckDBType columnType) : VectorDataWriterBase(vector, vectorData, columnType)
77
{
8-
internal override bool AppendDateTime(DateTime value, int rowIndex) => AppendValueInternal(NativeMethods.DateTimeHelpers.DuckDBToTimestamp(DuckDBTimestamp.FromDateTime(value)), rowIndex);
8+
internal override bool AppendDateTime(DateTime value, int rowIndex)
9+
{
10+
if (ColumnType == DuckDBType.Date)
11+
{
12+
return AppendValueInternal(NativeMethods.DateTimeHelpers.DuckDBToDate((DuckDBDateOnly)value.Date), rowIndex);
13+
}
14+
15+
var timestamp = NativeMethods.DateTimeHelpers.DuckDBToTimestamp(DuckDBTimestamp.FromDateTime(value));
16+
17+
if (ColumnType == DuckDBType.TimestampNs)
18+
{
19+
timestamp.Micros *= 1000;
20+
}
21+
22+
if (ColumnType == DuckDBType.TimestampMs)
23+
{
24+
timestamp.Micros /= 1000;
25+
}
26+
27+
if (ColumnType == DuckDBType.TimestampS)
28+
{
29+
timestamp.Micros /= 1000000;
30+
}
31+
32+
return AppendValueInternal(timestamp, rowIndex);
33+
}
934

1035
#if NET6_0_OR_GREATER
1136
internal override bool AppendDateOnly(DateOnly value, int rowIndex) => AppendValueInternal(NativeMethods.DateTimeHelpers.DuckDBToDate(value), rowIndex);

DuckDB.NET.Data/Internal/Writer/VectorDataWriterBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ namespace DuckDB.NET.Data.Internal.Writer;
77
internal unsafe class VectorDataWriterBase(IntPtr vector, void* vectorData, DuckDBType columnType)
88
{
99
private unsafe ulong* validity;
10-
internal IntPtr Vector { get; } = vector;
10+
11+
internal IntPtr Vector => vector;
12+
internal DuckDBType ColumnType => columnType;
1113

1214
public unsafe void AppendNull(int rowIndex)
1315
{

DuckDB.NET.Data/Internal/Writer/VectorDataWriterFactory.cs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,34 @@
22
using DuckDB.NET.Data.Internal.Reader;
33
using DuckDB.NET.Native;
44

5-
namespace DuckDB.NET.Data.Internal.Writer
5+
namespace DuckDB.NET.Data.Internal.Writer;
6+
7+
internal static class VectorDataWriterFactory
68
{
7-
internal static class VectorDataWriterFactory
9+
public static unsafe VectorDataWriterBase CreateWriter(IntPtr vector, DuckDBLogicalType logicalType)
810
{
9-
public static unsafe VectorDataWriterBase CreateWriter(IntPtr vector, DuckDBLogicalType logicalType)
10-
{
11-
var dataPointer = NativeMethods.Vectors.DuckDBVectorGetData(vector);
12-
var columnType = NativeMethods.LogicalType.DuckDBGetTypeId(logicalType);
11+
var dataPointer = NativeMethods.Vectors.DuckDBVectorGetData(vector);
12+
var columnType = NativeMethods.LogicalType.DuckDBGetTypeId(logicalType);
1313

14-
return columnType switch
15-
{
16-
DuckDBType.Uuid => new GuidVectorDataWriter(vector, dataPointer, columnType),
17-
DuckDBType.Date => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
18-
DuckDBType.Time => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
19-
DuckDBType.Interval => new IntervalVectorDataWriter(vector, dataPointer, columnType),
20-
DuckDBType.Timestamp => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
14+
return columnType switch
15+
{
16+
DuckDBType.Uuid => new GuidVectorDataWriter(vector, dataPointer, columnType),
17+
DuckDBType.Date => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
18+
DuckDBType.Time => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
19+
DuckDBType.Interval => new IntervalVectorDataWriter(vector, dataPointer, columnType),
20+
DuckDBType.Timestamp => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
2121

22-
DuckDBType.Boolean => new BooleanVectorDataWriter(vector, dataPointer, columnType),
22+
DuckDBType.Boolean => new BooleanVectorDataWriter(vector, dataPointer, columnType),
2323

24-
DuckDBType.Blob => new StringVectorDataWriter(vector, dataPointer, columnType),
25-
DuckDBType.Varchar => new StringVectorDataWriter(vector, dataPointer, columnType),
24+
DuckDBType.Blob => new StringVectorDataWriter(vector, dataPointer, columnType),
25+
DuckDBType.Varchar => new StringVectorDataWriter(vector, dataPointer, columnType),
2626

27-
DuckDBType.Decimal => new DecimalVectorDataWriter(vector, dataPointer, logicalType, columnType),
28-
_ => new NumericVectorDataWriter(vector, dataPointer, columnType)
29-
};
30-
}
27+
DuckDBType.Decimal => new DecimalVectorDataWriter(vector, dataPointer, logicalType, columnType),
28+
DuckDBType.TimestampS => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
29+
DuckDBType.TimestampMs => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
30+
DuckDBType.TimestampNs => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
31+
DuckDBType.TimestampTz => new DateTimeVectorDataWriter(vector, dataPointer, columnType),
32+
_ => new NumericVectorDataWriter(vector, dataPointer, columnType)
33+
};
3134
}
32-
}
35+
}

DuckDB.NET.Test/ManagedAppenderTests.cs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.IO;
99
using System.Linq;
1010
using System.Numerics;
11+
using Dapper;
1112

1213
namespace DuckDB.NET.Test;
1314

@@ -83,8 +84,7 @@ public void CommonTypes()
8384
public void UnicodeTests()
8485
{
8586
var words = new List<string> { "hello", "안녕하세요", "Ø3mm CHAIN", null, "" };
86-
var table = "CREATE TABLE UnicodeAppenderTestTable (index INTEGER, words VARCHAR);";
87-
Command.CommandText = table;
87+
Command.CommandText = "CREATE TABLE UnicodeAppenderTestTable (index INTEGER, words VARCHAR);";
8888
Command.ExecuteNonQuery();
8989

9090
using (var appender = Connection.CreateAppender("UnicodeAppenderTestTable"))
@@ -157,8 +157,7 @@ public void ByteArray()
157157
[Fact]
158158
public void Decimals()
159159
{
160-
var table = "CREATE TABLE managedAppenderDecimals(a INTEGER, b decimal(3, 1), c decimal (9, 4), d decimal (18, 6), e decimal(38, 12));";
161-
Command.CommandText = table;
160+
Command.CommandText = "CREATE TABLE managedAppenderDecimals(a INTEGER, b decimal(3, 1), c decimal (9, 4), d decimal (18, 6), e decimal(38, 12));";
162161
Command.ExecuteNonQuery();
163162

164163
var rows = 20;
@@ -194,8 +193,7 @@ public void Decimals()
194193
[Fact]
195194
public void GuidValues()
196195
{
197-
var table = "CREATE TABLE managedAppenderGuids(a UUID);";
198-
Command.CommandText = table;
196+
Command.CommandText = "CREATE TABLE managedAppenderGuids(a UUID);";
199197
Command.ExecuteNonQuery();
200198

201199
var guids = Enumerable.Range(0, 20).Select(i => (Guid?)Guid.NewGuid()).ToList();
@@ -209,19 +207,14 @@ public void GuidValues()
209207
}
210208
}
211209

212-
Command.CommandText = "SELECT * FROM managedAppenderGuids";
213-
using (var reader = Command.ExecuteReader())
214-
{
215-
var result = reader.Cast<IDataRecord>().Select(record => record.IsDBNull(0) ? (Guid?)null : record.GetGuid(0)).ToList();
216-
result.Should().BeEquivalentTo(guids);
217-
}
210+
var result = Connection.Query<Guid?>("SELECT * FROM managedAppenderGuids");
211+
result.Should().BeEquivalentTo(guids);
218212
}
219213

220214
[Fact]
221215
public void IntervalValues()
222216
{
223-
var table = "CREATE TABLE managedAppenderInterval(a INTERVAL);";
224-
Command.CommandText = table;
217+
Command.CommandText = "CREATE TABLE managedAppenderInterval(a INTERVAL);";
225218
Command.ExecuteNonQuery();
226219

227220
var timeSpans = Enumerable.Range(0, 20).Select(i => TimeSpan.FromSeconds(Random.Shared.Next(1_000_000, 1_000_000 * 10))).ToList();
@@ -234,13 +227,39 @@ public void IntervalValues()
234227
}
235228
}
236229

237-
Command.CommandText = "SELECT * FROM managedAppenderInterval";
238-
using (var reader = Command.ExecuteReader())
239-
{
240-
var result = reader.Cast<IDataRecord>().Select(record => (TimeSpan)record.GetValue(0)).ToList();
230+
var result = Connection.Query<TimeSpan>("SELECT * FROM managedAppenderInterval");
231+
232+
result.Should().BeEquivalentTo(timeSpans);
233+
}
241234

242-
result.Should().BeEquivalentTo(timeSpans);
235+
[Fact]
236+
public void TemporalValues()
237+
{
238+
Command.CommandText = "CREATE TABLE managedAppenderTemporal(a Date, b TimeStamp, c TIMESTAMP_NS, d TIMESTAMP_MS, e TIMESTAMP_S, f TIMESTAMPTZ);";
239+
Command.ExecuteNonQuery();
240+
241+
var dates = Enumerable.Range(0, 20).Select(i => new DateTime(1900, 1, 1).AddDays(Random.Shared.Next(1, 50000))).ToList();
242+
243+
using (var appender = Connection.CreateAppender("managedAppenderTemporal"))
244+
{
245+
foreach (var value in dates)
246+
{
247+
appender.CreateRow()
248+
.AppendValue(value).AppendValue(value)
249+
.AppendValue(value).AppendValue(value)
250+
.AppendValue(value).AppendValue(value)
251+
.EndRow();
252+
}
243253
}
254+
255+
var result = Connection.Query<(DateOnly, DateTime, DateTime, DateTime, DateTime, DateTime)>("SELECT a, b, c, d, e, f FROM managedAppenderTemporal").ToList();
256+
257+
result.Select(tuple => tuple.Item1).Should().BeEquivalentTo(dates.Select(DateOnly.FromDateTime));
258+
result.Select(tuple => tuple.Item2).Should().BeEquivalentTo(dates);
259+
result.Select(tuple => tuple.Item3).Should().BeEquivalentTo(dates);
260+
result.Select(tuple => tuple.Item4).Should().BeEquivalentTo(dates);
261+
result.Select(tuple => tuple.Item5).Should().BeEquivalentTo(dates);
262+
result.Select(tuple => tuple.Item6).Should().BeEquivalentTo(dates);
244263
}
245264

246265
[Fact]

0 commit comments

Comments
 (0)