Skip to content

Commit 12af39c

Browse files
authored
feat(nanos): support of timestamp column with nanos precision. (#47)
1 parent 6b1bd1c commit 12af39c

File tree

6 files changed

+158
-5
lines changed

6 files changed

+158
-5
lines changed

src/net-questdb-client-tests/HttpTests.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,45 @@ await sender.Table("name")
747747
await sender.SendAsync();
748748

749749
var expected =
750-
"name ts=1645660800000000t 1645660800000000000\n";
750+
"name ts=1645660800000000000n 1645660800000000000\n";
751+
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
752+
}
753+
754+
[Test]
755+
public async Task SendColumnNanos()
756+
{
757+
using var srv = new DummyHttpServer();
758+
await srv.StartAsync(HttpPort);
759+
using var sender = Sender.New($"http::addr={Host}:{HttpPort};auto_flush=off;");
760+
761+
const long timestampNanos = 1645660800123456789L;
762+
await sender.Table("name")
763+
.ColumnNanos("ts", timestampNanos)
764+
.AtAsync(timestampNanos);
765+
766+
await sender.SendAsync();
767+
768+
var expected =
769+
"name ts=1645660800123456789n 1645660800123456789\n";
770+
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
771+
}
772+
773+
[Test]
774+
public async Task SendAtNanos()
775+
{
776+
using var srv = new DummyHttpServer();
777+
await srv.StartAsync(HttpPort);
778+
using var sender = Sender.New($"http::addr={Host}:{HttpPort};auto_flush=off;");
779+
780+
const long timestampNanos = 1645660800987654321L;
781+
await sender.Table("name")
782+
.Column("value", 42)
783+
.AtNanosAsync(timestampNanos);
784+
785+
await sender.SendAsync();
786+
787+
var expected =
788+
"name value=42i 1645660800987654321\n";
751789
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
752790
}
753791

@@ -1163,7 +1201,7 @@ public async Task TransactionMultipleTypes()
11631201
await sender.CommitAsync();
11641202

11651203
var expected =
1166-
"tableName,foo=bah 86400000000000\ntableName foo=123i 86400000000000\ntableName foo=123 86400000000000\ntableName foo=0t 86400000000000\ntableName foo=-3600000000t 86400000000000\ntableName foo=f 86400000000000\n";
1204+
"tableName,foo=bah 86400000000000\ntableName foo=123i 86400000000000\ntableName foo=123 86400000000000\ntableName foo=0n 86400000000000\ntableName foo=-3600000000000n 86400000000000\ntableName foo=f 86400000000000\n";
11671205
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
11681206
}
11691207

src/net-questdb-client-tests/TcpTests.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,47 @@ await sender.Table("name")
335335
await sender.SendAsync();
336336

337337
var expected =
338-
"name ts=1645660800000000t 1645660800000000000\n";
338+
"name ts=1645660800000000000n 1645660800000000000\n";
339+
WaitAssert(srv, expected);
340+
}
341+
342+
[Test]
343+
public async Task SendColumnNanos()
344+
{
345+
using var srv = CreateTcpListener(_port);
346+
srv.AcceptAsync();
347+
348+
using var sender = Sender.New($"tcp::addr={_host}:{_port};");
349+
350+
const long timestampNanos = 1645660800123456789L;
351+
await sender.Table("name")
352+
.ColumnNanos("ts", timestampNanos)
353+
.AtAsync(timestampNanos);
354+
355+
await sender.SendAsync();
356+
357+
var expected =
358+
"name ts=1645660800123456789n 1645660800123456789\n";
359+
WaitAssert(srv, expected);
360+
}
361+
362+
[Test]
363+
public async Task SendAtNanos()
364+
{
365+
using var srv = CreateTcpListener(_port);
366+
srv.AcceptAsync();
367+
368+
using var sender = Sender.New($"tcp::addr={_host}:{_port};");
369+
370+
const long timestampNanos = 1645660800987654321L;
371+
await sender.Table("name")
372+
.Column("value", 42)
373+
.AtNanosAsync(timestampNanos);
374+
375+
await sender.SendAsync();
376+
377+
var expected =
378+
"name value=42i 1645660800987654321\n";
339379
WaitAssert(srv, expected);
340380
}
341381

src/net-questdb-client/Buffers/BufferV1.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public void AtNow()
108108
public void At(DateTime timestamp)
109109
{
110110
var epoch = timestamp.Ticks - EpochTicks;
111-
PutAscii(' ').Put(epoch).PutAscii('0').PutAscii('0');
111+
PutAscii(' ').Put(epoch * 100);
112112
FinishLine();
113113
}
114114

@@ -125,6 +125,13 @@ public void At(long epochNano)
125125
FinishLine();
126126
}
127127

128+
/// <inheritdoc />
129+
public void AtNanos(long timestampNanos)
130+
{
131+
PutAscii(' ').Put(timestampNanos);
132+
FinishLine();
133+
}
134+
128135
/// <inheritdoc />
129136
public void Clear()
130137
{
@@ -317,7 +324,7 @@ public IBuffer Column(ReadOnlySpan<char> name, DateTime timestamp)
317324
}
318325

319326
var epoch = timestamp.Ticks - EpochTicks;
320-
Column(name).Put(epoch / 10).PutAscii('t');
327+
Column(name).Put(epoch * 100).PutAscii('n');
321328
return this;
322329
}
323330

@@ -333,6 +340,18 @@ public IBuffer Column(ReadOnlySpan<char> name, DateTimeOffset timestamp)
333340
return this;
334341
}
335342

343+
/// <inheritdoc />
344+
public IBuffer ColumnNanos(ReadOnlySpan<char> name, long timestampNanos)
345+
{
346+
if (WithinTransaction && !_hasTable)
347+
{
348+
Table(_currentTableName);
349+
}
350+
351+
Column(name).Put(timestampNanos).PutAscii('n');
352+
return this;
353+
}
354+
336355
/// <summary />
337356
public IBuffer EncodeUtf8(ReadOnlySpan<char> name)
338357
{

src/net-questdb-client/Buffers/IBuffer.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ public interface IBuffer
140140
/// <returns>Itself</returns>
141141
public IBuffer Column(ReadOnlySpan<char> name, DateTimeOffset timestamp);
142142

143+
/// <summary>
144+
/// Set value of TIMESTAMP column with exact nanosecond precision.
145+
/// </summary>
146+
/// <param name="name">Column name</param>
147+
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
148+
/// <returns>Itself</returns>
149+
public IBuffer ColumnNanos(ReadOnlySpan<char> name, long timestampNanos);
150+
143151
/// <summary>
144152
/// Finishes the line without specifying Designated Timestamp. QuestDB will set the timestamp at the time of writing to
145153
/// the table.
@@ -164,6 +172,12 @@ public interface IBuffer
164172
/// <param name="epochNano">Nanoseconds since Unix epoch</param>
165173
public void At(long epochNano);
166174

175+
/// <summary>
176+
/// Finishes the line setting timestamp with exact nanosecond precision.
177+
/// </summary>
178+
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
179+
public void AtNanos(long timestampNanos);
180+
167181
/// <summary>
168182
/// Clears the buffer.
169183
/// </summary>

src/net-questdb-client/Senders/AbstractSender.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ public ISender Column(ReadOnlySpan<char> name, DateTimeOffset value)
122122
return this;
123123
}
124124

125+
/// <inheritdoc />
126+
public ISender ColumnNanos(ReadOnlySpan<char> name, long timestampNanos)
127+
{
128+
Buffer.ColumnNanos(name, timestampNanos);
129+
return this;
130+
}
131+
125132
public ISender Column<T>(ReadOnlySpan<char> name, IEnumerable<T> value, IEnumerable<int> shape) where T : struct
126133
{
127134
Buffer.Column(name, value, shape);
@@ -196,6 +203,22 @@ public void At(long value, CancellationToken ct = default)
196203
FlushIfNecessary(ct);
197204
}
198205

206+
/// <inheritdoc />
207+
public ValueTask AtNanosAsync(long timestampNanos, CancellationToken ct = default)
208+
{
209+
GuardLastFlushNotSet();
210+
Buffer.AtNanos(timestampNanos);
211+
return FlushIfNecessaryAsync(ct);
212+
}
213+
214+
/// <inheritdoc />
215+
public void AtNanos(long timestampNanos, CancellationToken ct = default)
216+
{
217+
GuardLastFlushNotSet();
218+
Buffer.AtNanos(timestampNanos);
219+
FlushIfNecessary(ct);
220+
}
221+
199222
/// <inheritdoc />
200223
public void AtNow(CancellationToken ct = default)
201224
{

src/net-questdb-client/Senders/ISender.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ public interface ISenderV1 : IDisposable
149149
/// <inheritdoc cref="Column(System.ReadOnlySpan{char},System.ReadOnlySpan{char})" />
150150
public ISender Column(ReadOnlySpan<char> name, DateTimeOffset value);
151151

152+
/// <summary>
153+
/// Adds a timestamp column with exact nanosecond precision.
154+
/// </summary>
155+
/// <param name="name">The name of the column</param>
156+
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
157+
/// <returns>Itself</returns>
158+
public ISender ColumnNanos(ReadOnlySpan<char> name, long timestampNanos);
159+
152160
/// <summary>
153161
/// Adds a value for the designated timestamp column.
154162
/// </summary>
@@ -180,6 +188,17 @@ public interface ISenderV1 : IDisposable
180188
/// <inheritdoc cref="AtAsync(DateTime, CancellationToken)" />
181189
public void At(long value, CancellationToken ct = default);
182190

191+
/// <summary>
192+
/// Adds exact nanosecond precision timestamp for the designated timestamp column.
193+
/// </summary>
194+
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
195+
/// <param name="ct">A cancellation token applied requests caused by auto-flushing</param>
196+
/// <returns>Itself</returns>
197+
public ValueTask AtNanosAsync(long timestampNanos, CancellationToken ct = default);
198+
199+
/// <inheritdoc cref="AtNanosAsync" />
200+
public void AtNanos(long timestampNanos, CancellationToken ct = default);
201+
183202
/// <inheritdoc cref="AtNowAsync" />
184203
[Obsolete("Not compatible with deduplication. Please use `At(DateTime.UtcNow)` instead.")]
185204
public void AtNow(CancellationToken ct = default);

0 commit comments

Comments
 (0)