Skip to content
Merged
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
42 changes: 40 additions & 2 deletions src/net-questdb-client-tests/HttpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,45 @@ await sender.Table("name")
await sender.SendAsync();

var expected =
"name ts=1645660800000000t 1645660800000000000\n";
"name ts=1645660800000000000n 1645660800000000000\n";
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
}

[Test]
public async Task SendColumnNanos()
{
using var srv = new DummyHttpServer();
await srv.StartAsync(HttpPort);
using var sender = Sender.New($"http::addr={Host}:{HttpPort};auto_flush=off;");

const long timestampNanos = 1645660800123456789L;
await sender.Table("name")
.ColumnNanos("ts", timestampNanos)
.AtAsync(timestampNanos);

await sender.SendAsync();

var expected =
"name ts=1645660800123456789n 1645660800123456789\n";
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
}

[Test]
public async Task SendAtNanos()
{
using var srv = new DummyHttpServer();
await srv.StartAsync(HttpPort);
using var sender = Sender.New($"http::addr={Host}:{HttpPort};auto_flush=off;");

const long timestampNanos = 1645660800987654321L;
await sender.Table("name")
.Column("value", 42)
.AtNanosAsync(timestampNanos);

await sender.SendAsync();

var expected =
"name value=42i 1645660800987654321\n";
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
}

Expand Down Expand Up @@ -1163,7 +1201,7 @@ public async Task TransactionMultipleTypes()
await sender.CommitAsync();

var expected =
"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";
"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";
Assert.That(srv.PrintBuffer(), Is.EqualTo(expected));
}

Expand Down
42 changes: 41 additions & 1 deletion src/net-questdb-client-tests/TcpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,47 @@ await sender.Table("name")
await sender.SendAsync();

var expected =
"name ts=1645660800000000t 1645660800000000000\n";
"name ts=1645660800000000000n 1645660800000000000\n";
WaitAssert(srv, expected);
}

[Test]
public async Task SendColumnNanos()
{
using var srv = CreateTcpListener(_port);
srv.AcceptAsync();

using var sender = Sender.New($"tcp::addr={_host}:{_port};");

const long timestampNanos = 1645660800123456789L;
await sender.Table("name")
.ColumnNanos("ts", timestampNanos)
.AtAsync(timestampNanos);

await sender.SendAsync();

var expected =
"name ts=1645660800123456789n 1645660800123456789\n";
WaitAssert(srv, expected);
}

[Test]
public async Task SendAtNanos()
{
using var srv = CreateTcpListener(_port);
srv.AcceptAsync();

using var sender = Sender.New($"tcp::addr={_host}:{_port};");

const long timestampNanos = 1645660800987654321L;
await sender.Table("name")
.Column("value", 42)
.AtNanosAsync(timestampNanos);

await sender.SendAsync();

var expected =
"name value=42i 1645660800987654321\n";
WaitAssert(srv, expected);
}

Expand Down
23 changes: 21 additions & 2 deletions src/net-questdb-client/Buffers/BufferV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public void AtNow()
public void At(DateTime timestamp)
{
var epoch = timestamp.Ticks - EpochTicks;
PutAscii(' ').Put(epoch).PutAscii('0').PutAscii('0');
PutAscii(' ').Put(epoch * 100);
FinishLine();
}

Expand All @@ -125,6 +125,13 @@ public void At(long epochNano)
FinishLine();
}

/// <inheritdoc />
public void AtNanos(long timestampNanos)
{
PutAscii(' ').Put(timestampNanos);
FinishLine();
}

/// <inheritdoc />
public void Clear()
{
Expand Down Expand Up @@ -317,7 +324,7 @@ public IBuffer Column(ReadOnlySpan<char> name, DateTime timestamp)
}

var epoch = timestamp.Ticks - EpochTicks;
Column(name).Put(epoch / 10).PutAscii('t');
Column(name).Put(epoch * 100).PutAscii('n');
return this;
}

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

/// <inheritdoc />
public IBuffer ColumnNanos(ReadOnlySpan<char> name, long timestampNanos)
{
if (WithinTransaction && !_hasTable)
{
Table(_currentTableName);
}

Column(name).Put(timestampNanos).PutAscii('n');
return this;
}

/// <summary />
public IBuffer EncodeUtf8(ReadOnlySpan<char> name)
{
Expand Down
14 changes: 14 additions & 0 deletions src/net-questdb-client/Buffers/IBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ public interface IBuffer
/// <returns>Itself</returns>
public IBuffer Column(ReadOnlySpan<char> name, DateTimeOffset timestamp);

/// <summary>
/// Set value of TIMESTAMP column with exact nanosecond precision.
/// </summary>
/// <param name="name">Column name</param>
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
/// <returns>Itself</returns>
public IBuffer ColumnNanos(ReadOnlySpan<char> name, long timestampNanos);

/// <summary>
/// Finishes the line without specifying Designated Timestamp. QuestDB will set the timestamp at the time of writing to
/// the table.
Expand All @@ -164,6 +172,12 @@ public interface IBuffer
/// <param name="epochNano">Nanoseconds since Unix epoch</param>
public void At(long epochNano);

/// <summary>
/// Finishes the line setting timestamp with exact nanosecond precision.
/// </summary>
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
public void AtNanos(long timestampNanos);

/// <summary>
/// Clears the buffer.
/// </summary>
Expand Down
23 changes: 23 additions & 0 deletions src/net-questdb-client/Senders/AbstractSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ public ISender Column(ReadOnlySpan<char> name, DateTimeOffset value)
return this;
}

/// <inheritdoc />
public ISender ColumnNanos(ReadOnlySpan<char> name, long timestampNanos)
{
Buffer.ColumnNanos(name, timestampNanos);
return this;
}

public ISender Column<T>(ReadOnlySpan<char> name, IEnumerable<T> value, IEnumerable<int> shape) where T : struct
{
Buffer.Column(name, value, shape);
Expand Down Expand Up @@ -196,6 +203,22 @@ public void At(long value, CancellationToken ct = default)
FlushIfNecessary(ct);
}

/// <inheritdoc />
public ValueTask AtNanosAsync(long timestampNanos, CancellationToken ct = default)
{
GuardLastFlushNotSet();
Buffer.AtNanos(timestampNanos);
return FlushIfNecessaryAsync(ct);
}

/// <inheritdoc />
public void AtNanos(long timestampNanos, CancellationToken ct = default)
{
GuardLastFlushNotSet();
Buffer.AtNanos(timestampNanos);
FlushIfNecessary(ct);
}

/// <inheritdoc />
public void AtNow(CancellationToken ct = default)
{
Expand Down
19 changes: 19 additions & 0 deletions src/net-questdb-client/Senders/ISender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ public interface ISenderV1 : IDisposable
/// <inheritdoc cref="Column(System.ReadOnlySpan{char},System.ReadOnlySpan{char})" />
public ISender Column(ReadOnlySpan<char> name, DateTimeOffset value);

/// <summary>
/// Adds a timestamp column with exact nanosecond precision.
/// </summary>
/// <param name="name">The name of the column</param>
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
/// <returns>Itself</returns>
public ISender ColumnNanos(ReadOnlySpan<char> name, long timestampNanos);

/// <summary>
/// Adds a value for the designated timestamp column.
/// </summary>
Expand Down Expand Up @@ -180,6 +188,17 @@ public interface ISenderV1 : IDisposable
/// <inheritdoc cref="AtAsync(DateTime, CancellationToken)" />
public void At(long value, CancellationToken ct = default);

/// <summary>
/// Adds exact nanosecond precision timestamp for the designated timestamp column.
/// </summary>
/// <param name="timestampNanos">Nanoseconds since Unix epoch</param>
/// <param name="ct">A cancellation token applied requests caused by auto-flushing</param>
/// <returns>Itself</returns>
public ValueTask AtNanosAsync(long timestampNanos, CancellationToken ct = default);

/// <inheritdoc cref="AtNanosAsync" />
public void AtNanos(long timestampNanos, CancellationToken ct = default);

/// <inheritdoc cref="AtNowAsync" />
[Obsolete("Not compatible with deduplication. Please use `At(DateTime.UtcNow)` instead.")]
public void AtNow(CancellationToken ct = default);
Expand Down