Skip to content

Commit e509af7

Browse files
committed
feat: add support for decimal
1 parent 31f2972 commit e509af7

25 files changed

+1005
-1233
lines changed

README.md

Lines changed: 34 additions & 33 deletions
Large diffs are not rendered by default.

net-questdb-client.sln

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.30114.105
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "net-questdb-client", "src\net-questdb-client\net-questdb-client.csproj", "{456B1860-0102-48D7-861A-5F9963F3887B}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tcp-client-test", "src\tcp-client-test\tcp-client-test.csproj", "{22F903D9-4367-46A2-A25A-F4A6BF9105C6}"
9-
EndProject
108
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-basic", "src\example-basic\example-basic.csproj", "{121EAA4D-3A73-468C-8CAB-A2A4BEF848CF}"
119
EndProject
1210
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-auth-tls", "src\example-auth-tls\example-auth-tls.csproj", "{FBB8181C-6BAB-46C2-A47A-D3566A3997FE}"
@@ -21,7 +19,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-streaming", "src\ex
2119
EndProject
2220
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-auth-http-tls", "src\example-auth-http-tls\example-auth-http-tls.csproj", "{24D93DBB-3783-423F-81CC-6B9BFD33F6CD}"
2321
EndProject
24-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-aot", "example-aot\example-aot.csproj", "{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}"
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-aot", "src\example-aot\example-aot.csproj", "{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}"
2523
EndProject
2624
Global
2725
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -36,10 +34,6 @@ Global
3634
{456B1860-0102-48D7-861A-5F9963F3887B}.Debug|Any CPU.Build.0 = Debug|Any CPU
3735
{456B1860-0102-48D7-861A-5F9963F3887B}.Release|Any CPU.ActiveCfg = Release|Any CPU
3836
{456B1860-0102-48D7-861A-5F9963F3887B}.Release|Any CPU.Build.0 = Release|Any CPU
39-
{22F903D9-4367-46A2-A25A-F4A6BF9105C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40-
{22F903D9-4367-46A2-A25A-F4A6BF9105C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
41-
{22F903D9-4367-46A2-A25A-F4A6BF9105C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
42-
{22F903D9-4367-46A2-A25A-F4A6BF9105C6}.Release|Any CPU.Build.0 = Release|Any CPU
4337
{121EAA4D-3A73-468C-8CAB-A2A4BEF848CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
4438
{121EAA4D-3A73-468C-8CAB-A2A4BEF848CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
4539
{121EAA4D-3A73-468C-8CAB-A2A4BEF848CF}.Release|Any CPU.ActiveCfg = Release|Any CPU

src/dummy-http-server/DummyHttpServer.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ public DummyHttpServer(bool withTokenAuth = false, bool withBasicAuth = false, b
5353
.AddConsole();
5454
});
5555

56-
IlpEndpoint.WithTokenAuth = withTokenAuth;
57-
IlpEndpoint.WithBasicAuth = withBasicAuth;
56+
IlpEndpoint.WithTokenAuth = withTokenAuth;
57+
IlpEndpoint.WithBasicAuth = withBasicAuth;
5858
IlpEndpoint.WithRetriableError = withRetriableError;
59-
IlpEndpoint.WithErrorMessage = withErrorMessage;
59+
IlpEndpoint.WithErrorMessage = withErrorMessage;
6060
_withStartDelay = withStartDelay;
6161

6262
if (withTokenAuth)
@@ -103,7 +103,7 @@ public void Clear()
103103
IlpEndpoint.ReceiveBuffer.Clear();
104104
IlpEndpoint.ReceiveBytes.Clear();
105105
IlpEndpoint.LastError = null;
106-
IlpEndpoint.Counter = 0;
106+
IlpEndpoint.Counter = 0;
107107
}
108108

109109
public async Task StartAsync(int port = 29743, int[]? versions = null)
@@ -112,10 +112,10 @@ public async Task StartAsync(int port = 29743, int[]? versions = null)
112112
{
113113
await Task.Delay(_withStartDelay.Value);
114114
}
115-
versions ??= new[] { 1, 2, };
116-
SettingsEndpoint.Versions = versions;
117-
_port = port;
118-
_app.RunAsync($"http://localhost:{port}");
115+
versions ??= new[] { 1, 2, 3, };
116+
SettingsEndpoint.Versions = versions;
117+
_port = port;
118+
_ = _app.RunAsync($"http://localhost:{port}");
119119
}
120120

121121
public async Task RunAsync()
@@ -133,7 +133,7 @@ public StringBuilder GetReceiveBuffer()
133133
return IlpEndpoint.ReceiveBuffer;
134134
}
135135

136-
public List<byte> GetReceiveBytes()
136+
public List<byte> GetReceivedBytes()
137137
{
138138
return IlpEndpoint.ReceiveBytes;
139139
}
@@ -157,7 +157,7 @@ public async Task<bool> Healthcheck()
157157
var jwtToken = JwtBearer.CreateToken(o =>
158158
{
159159
o.SigningKey = SigningKey;
160-
o.ExpireAt = DateTime.UtcNow.AddDays(1);
160+
o.ExpireAt = DateTime.UtcNow.AddDays(1);
161161
});
162162
return jwtToken;
163163
}
@@ -172,8 +172,8 @@ public int GetCounter()
172172

173173
public string PrintBuffer()
174174
{
175-
var bytes = GetReceiveBytes().ToArray();
176-
var sb = new StringBuilder();
175+
var bytes = GetReceivedBytes().ToArray();
176+
var sb = new StringBuilder();
177177
var lastAppend = 0;
178178

179179
var i = 0;
@@ -242,7 +242,7 @@ public string PrintBuffer()
242242
i--;
243243
break;
244244
default:
245-
throw new NotImplementedException();
245+
throw new NotImplementedException($"Type {bytes[i]} not implemented");
246246
}
247247

248248
lastAppend = i + 1;

src/example-aot/example-aot.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<ProjectReference Include="..\src\net-questdb-client\net-questdb-client.csproj"/>
14+
<ProjectReference Include="..\net-questdb-client\net-questdb-client.csproj"/>
1515
</ItemGroup>
1616

1717
</Project>

src/net-questdb-client-benchmarks/BenchInserts.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
using BenchmarkDotNet.Attributes;
2727
using dummy_http_server;
2828
using QuestDB;
29-
using tcp_client_test;
29+
using net_questdb_client_tests;
3030

3131
#pragma warning disable CS0414 // Field is assigned but its value is never used
3232

src/net-questdb-client-benchmarks/net-questdb-client-benchmarks.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
<ItemGroup>
1717
<ProjectReference Include="..\dummy-http-server\dummy-http-server.csproj"/>
18+
<ProjectReference Include="..\net-questdb-client-tests\net-questdb-client-tests.csproj" />
1819
<ProjectReference Include="..\net-questdb-client\net-questdb-client.csproj"/>
19-
<ProjectReference Include="..\tcp-client-test\tcp-client-test.csproj"/>
2020
</ItemGroup>
2121

2222
<ItemGroup>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*******************************************************************************
2+
* ___ _ ____ ____
3+
* / _ \ _ _ ___ ___| |_| _ \| __ )
4+
* | | | | | | |/ _ \/ __| __| | | | _ \
5+
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
6+
* \__\_\\__,_|\___||___/\__|____/|____/
7+
*
8+
* Copyright (c) 2014-2019 Appsicle
9+
* Copyright (c) 2019-2024 QuestDB
10+
*
11+
* Licensed under the Apache License, Version 2.0 (the "License");
12+
* you may not use this file except in compliance with the License.
13+
* You may obtain a copy of the License at
14+
*
15+
* http://www.apache.org/licenses/LICENSE-2.0
16+
*
17+
* Unless required by applicable law or agreed to in writing, software
18+
* distributed under the License is distributed on an "AS IS" BASIS,
19+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
* See the License for the specific language governing permissions and
21+
* limitations under the License.
22+
*
23+
******************************************************************************/
24+
25+
using System.Text;
26+
using NUnit.Framework;
27+
using QuestDB.Enums;
28+
29+
namespace net_questdb_client_tests;
30+
31+
internal static class DecimalTestHelpers
32+
{
33+
internal static void AssertDecimalField(ReadOnlySpan<byte> buffer,
34+
string columnName,
35+
byte expectedScale,
36+
ReadOnlySpan<byte> expectedMantissa)
37+
{
38+
var payload = ExtractDecimalPayload(buffer, columnName);
39+
Assert.That(payload.Length,
40+
Is.GreaterThanOrEqualTo(4 + expectedMantissa.Length),
41+
$"Decimal field `{columnName}` payload shorter than expected.");
42+
Assert.That(payload[0], Is.EqualTo((byte)'='));
43+
Assert.That(payload[1], Is.EqualTo((byte)BinaryFormatType.DECIMAL));
44+
Assert.That(payload[2], Is.EqualTo(expectedScale), $"Unexpected scale for `{columnName}`.");
45+
Assert.That(payload[3],
46+
Is.EqualTo((byte)expectedMantissa.Length),
47+
$"Unexpected mantissa length for `{columnName}`.");
48+
CollectionAssert.AreEqual(expectedMantissa.ToArray(), payload.Slice(4, expectedMantissa.Length).ToArray(),
49+
$"Mantissa bytes for `{columnName}` did not match expectation.");
50+
}
51+
52+
internal static void AssertDecimalNullField(ReadOnlySpan<byte> buffer, string columnName)
53+
{
54+
var payload = ExtractDecimalPayload(buffer, columnName);
55+
Assert.That(payload.Length,
56+
Is.GreaterThanOrEqualTo(4),
57+
$"Decimal field `{columnName}` payload shorter than expected.");
58+
Assert.That(payload[0], Is.EqualTo((byte)'='));
59+
Assert.That(payload[1], Is.EqualTo((byte)BinaryFormatType.DECIMAL));
60+
Assert.That(payload[2], Is.EqualTo(0), $"Unexpected scale for `{columnName}`.");
61+
Assert.That(payload[3], Is.EqualTo(0), $"Unexpected mantissa length for `{columnName}`.");
62+
}
63+
64+
private static ReadOnlySpan<byte> ExtractDecimalPayload(ReadOnlySpan<byte> buffer, string columnName)
65+
{
66+
var prefix = Encoding.ASCII.GetBytes($"{columnName}=");
67+
var index = buffer.IndexOf(prefix.AsSpan());
68+
Assert.That(index, Is.GreaterThanOrEqualTo(0), $"Column `{columnName}` not found in payload.");
69+
return buffer[(index + prefix.Length)..];
70+
}
71+
}

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class DummyIlpServer : IDisposable
5151

5252
public DummyIlpServer(int port, bool tls)
5353
{
54-
_tls = tls;
54+
_tls = tls;
5555
_server = new TcpListener(IPAddress.Loopback, port);
5656
_server.Start();
5757
}
@@ -77,7 +77,7 @@ private async Task AcceptConnections()
7777
using var socket = await _server.AcceptSocketAsync();
7878
clientSocket = socket;
7979
await using var connection = new NetworkStream(socket, true);
80-
Stream dataStream = connection;
80+
Stream dataStream = connection;
8181
if (_tls)
8282
{
8383
var sslStream = new SslStream(connection);
@@ -137,7 +137,7 @@ private async Task RunServerAuth(Stream connection)
137137
var pubKey1 = FromBase64String(_publicKeyX);
138138
var pubKey2 = FromBase64String(_publicKeyY);
139139

140-
var p = SecNamedCurves.GetByName("secp256r1");
140+
var p = SecNamedCurves.GetByName("secp256r1");
141141
var parameters = new ECDomainParameters(p.Curve, p.G, p.N, p.H);
142142

143143
// Verify the signature
@@ -168,8 +168,8 @@ private static string Pad(string text)
168168
public static byte[] FromBase64String(string encodedPrivateKey)
169169
{
170170
var replace = encodedPrivateKey
171-
.Replace('-', '+')
172-
.Replace('_', '/');
171+
.Replace('-', '+')
172+
.Replace('_', '/');
173173
return Convert.FromBase64String(Pad(replace));
174174
}
175175

@@ -178,7 +178,7 @@ private async Task<int> ReceiveUntilEol(Stream connection)
178178
var len = 0;
179179
while (true)
180180
{
181-
var n = await connection.ReadAsync(_buffer.AsMemory(len));
181+
var n = await connection.ReadAsync(_buffer.AsMemory(len));
182182
var inBuffer = len + n;
183183
for (var i = len; i < inBuffer; i++)
184184
{
@@ -226,13 +226,17 @@ private async Task SaveData(Stream connection, Socket socket)
226226
public string GetTextReceived()
227227
{
228228
return PrintBuffer();
229-
// return Encoding.UTF8.GetString(_received.GetBuffer(), 0, (int)_received.Length);
229+
}
230+
231+
public byte[] GetReceivedBytes()
232+
{
233+
return _received.ToArray();
230234
}
231235

232236
public string PrintBuffer()
233237
{
234-
var bytes = _received.GetBuffer().AsSpan().Slice(0, (int)_received.Length).ToArray();
235-
var sb = new StringBuilder();
238+
var bytes = _received.ToArray();
239+
var sb = new StringBuilder();
236240
var lastAppend = 0;
237241

238242
var i = 0;
@@ -258,7 +262,7 @@ public string PrintBuffer()
258262
for (var j = 0; j < dims; j++)
259263
{
260264
var lengthBytes = bytes.AsSpan()[i..(i + 4)];
261-
var _length = MemoryMarshal.Cast<byte, uint>(lengthBytes)[0];
265+
var _length = MemoryMarshal.Cast<byte, uint>(lengthBytes)[0];
262266
if (length == 0)
263267
{
264268
length = _length;
@@ -301,7 +305,7 @@ public string PrintBuffer()
301305
i--;
302306
break;
303307
default:
304-
throw new NotImplementedException();
308+
throw new NotImplementedException("Unknown type: " + bytes[i]);
305309
}
306310

307311
lastAppend = i + 1;
@@ -315,7 +319,7 @@ public string PrintBuffer()
315319

316320
public void WithAuth(string keyId, string publicKeyX, string publicKeyY)
317321
{
318-
_keyId = keyId;
322+
_keyId = keyId;
319323
_publicKeyX = publicKeyX;
320324
_publicKeyY = publicKeyY;
321325
}

0 commit comments

Comments
 (0)