Skip to content

Commit 4784a7f

Browse files
authored
feat: support for binary DOUBLE and ARRAY[DOUBLE] (#38)
1 parent 1fe7650 commit 4784a7f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+3693
-1537
lines changed

example-aot/Program.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using QuestDB;
2+
3+
using var sender = Sender.New("http::addr=localhost:9000;");
4+
5+
await sender.Table("trades")
6+
.Symbol("symbol", "ETH-USD")
7+
.Symbol("side", "sell")
8+
.Column("price", 2615.54)
9+
.Column("amount", 0.00044)
10+
.AtAsync(DateTime.UtcNow);
11+
12+
await sender.Table("trades")
13+
.Symbol("symbol", "BTC-USD")
14+
.Symbol("side", "sell")
15+
.Column("price", 39269.98)
16+
.Column("amount", 0.001)
17+
.AtAsync(DateTime.UtcNow);
18+
19+
await sender.SendAsync();
20+
21+
// Test with:
22+
// dotnet publish -r osx-arm64 -c Release

example-aot/example-aot.csproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<RootNamespace>example_aot</RootNamespace>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
<PublishAot>true</PublishAot>
10+
<InvariantGlobalization>true</InvariantGlobalization>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\src\net-questdb-client\net-questdb-client.csproj"/>
15+
</ItemGroup>
16+
17+
</Project>

net-questdb-client.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "net-questdb-client-tcp-auth
2525
EndProject
2626
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "net-questdb-client-tcp-auth-test", "src\net-questdb-client-tcp-auth-tests\net-questdb-client-tcp-auth-tests.csproj", "{628A6AE1-C0D4-4A40-98DF-1F094BD60203}"
2727
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-aot", "example-aot\example-aot.csproj", "{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}"
29+
EndProject
2830
Global
2931
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3032
Debug|Any CPU = Debug|Any CPU
@@ -78,5 +80,9 @@ Global
7880
{628A6AE1-C0D4-4A40-98DF-1F094BD60203}.Debug|Any CPU.Build.0 = Debug|Any CPU
7981
{628A6AE1-C0D4-4A40-98DF-1F094BD60203}.Release|Any CPU.ActiveCfg = Release|Any CPU
8082
{628A6AE1-C0D4-4A40-98DF-1F094BD60203}.Release|Any CPU.Build.0 = Release|Any CPU
83+
{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
84+
{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
85+
{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
86+
{5341FCF0-F71D-4160-8D6E-B5EFDF92E9E8}.Release|Any CPU.Build.0 = Release|Any CPU
8187
EndGlobalSection
8288
EndGlobal

src/dummy-http-server/DummyHttpServer.cs

Lines changed: 118 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,11 @@
2323
******************************************************************************/
2424

2525

26-
using System;
27-
using System.Net.Http;
26+
using System.Diagnostics;
27+
using System.Runtime.InteropServices;
2828
using System.Text;
29-
using System.Threading.Tasks;
3029
using FastEndpoints;
3130
using FastEndpoints.Security;
32-
using Microsoft.AspNetCore.Builder;
33-
using Microsoft.AspNetCore.Hosting;
34-
using Microsoft.Extensions.DependencyInjection;
35-
using Microsoft.Extensions.Logging;
3631

3732
namespace dummy_http_server;
3833

@@ -41,30 +36,32 @@ public class DummyHttpServer : IDisposable
4136
private static readonly string SigningKey = Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N");
4237
private static readonly string Username = "admin";
4338
private static readonly string Password = "quest";
44-
private int _port = 29743;
4539
private readonly WebApplication _app;
40+
private int _port = 29743;
41+
private readonly TimeSpan? _withStartDelay;
4642

47-
public DummyHttpServer(bool withTokenAuth = false, bool withBasicAuth = false, bool withRetriableError=false, bool withErrorMessage = false)
43+
public DummyHttpServer(bool withTokenAuth = false, bool withBasicAuth = false, bool withRetriableError = false,
44+
bool withErrorMessage = false, TimeSpan? withStartDelay = null)
4845
{
4946
var bld = WebApplication.CreateBuilder();
5047

51-
bld.Services.AddLogging(
52-
builder =>
53-
{
54-
builder.AddFilter("Microsoft", LogLevel.Warning)
55-
.AddFilter("System", LogLevel.Warning)
56-
.AddConsole();
57-
});
48+
bld.Services.AddLogging(builder =>
49+
{
50+
builder.AddFilter("Microsoft", LogLevel.Warning)
51+
.AddFilter("System", LogLevel.Warning)
52+
.AddConsole();
53+
});
5854

59-
IlpEndpoint.WithTokenAuth = withTokenAuth;
60-
IlpEndpoint.WithBasicAuth = withBasicAuth;
55+
IlpEndpoint.WithTokenAuth = withTokenAuth;
56+
IlpEndpoint.WithBasicAuth = withBasicAuth;
6157
IlpEndpoint.WithRetriableError = withRetriableError;
62-
IlpEndpoint.WithErrorMessage = withErrorMessage;
58+
IlpEndpoint.WithErrorMessage = withErrorMessage;
59+
_withStartDelay = withStartDelay;
6360

6461
if (withTokenAuth)
6562
{
6663
bld.Services.AddAuthenticationJwtBearer(s => s.SigningKey = SigningKey)
67-
.AddAuthorization();
64+
.AddAuthorization();
6865
}
6966

7067

@@ -75,7 +72,7 @@ public DummyHttpServer(bool withTokenAuth = false, bool withBasicAuth = false, b
7572
{
7673
o.Limits.MaxRequestBodySize = 1073741824;
7774
o.ListenLocalhost(29474,
78-
options => { options.UseHttps(); });
75+
options => { options.UseHttps(); });
7976
o.ListenLocalhost(29473);
8077
});
8178

@@ -103,15 +100,21 @@ public void Dispose()
103100
public void Clear()
104101
{
105102
IlpEndpoint.ReceiveBuffer.Clear();
103+
IlpEndpoint.ReceiveBytes.Clear();
106104
IlpEndpoint.LastError = null;
107-
IlpEndpoint.Counter = 0;
105+
IlpEndpoint.Counter = 0;
108106
}
109107

110-
public Task StartAsync(int port = 29743)
108+
public async Task StartAsync(int port = 29743, int[]? versions = null)
111109
{
112-
_port = port;
110+
if (_withStartDelay.HasValue)
111+
{
112+
await Task.Delay(_withStartDelay.Value);
113+
}
114+
versions ??= new[] { 1, 2, };
115+
SettingsEndpoint.Versions = versions;
116+
_port = port;
113117
_app.RunAsync($"http://localhost:{port}");
114-
return Task.CompletedTask;
115118
}
116119

117120
public async Task RunAsync()
@@ -129,6 +132,11 @@ public StringBuilder GetReceiveBuffer()
129132
return IlpEndpoint.ReceiveBuffer;
130133
}
131134

135+
public List<byte> GetReceiveBytes()
136+
{
137+
return IlpEndpoint.ReceiveBytes;
138+
}
139+
132140
public Exception? GetLastError()
133141
{
134142
return IlpEndpoint.LastError;
@@ -148,7 +156,7 @@ public async Task<bool> Healthcheck()
148156
var jwtToken = JwtBearer.CreateToken(o =>
149157
{
150158
o.SigningKey = SigningKey;
151-
o.ExpireAt = DateTime.UtcNow.AddDays(1);
159+
o.ExpireAt = DateTime.UtcNow.AddDays(1);
152160
});
153161
return jwtToken;
154162
}
@@ -160,4 +168,88 @@ public int GetCounter()
160168
{
161169
return IlpEndpoint.Counter;
162170
}
171+
172+
public string PrintBuffer()
173+
{
174+
var bytes = GetReceiveBytes().ToArray();
175+
var sb = new StringBuilder();
176+
var lastAppend = 0;
177+
178+
var i = 0;
179+
for (; i < bytes.Length; i++)
180+
{
181+
if (bytes[i] == (byte)'=')
182+
{
183+
if (bytes[i - 1] == (byte)'=')
184+
{
185+
sb.Append(Encoding.UTF8.GetString(bytes, lastAppend, i + 1 - lastAppend));
186+
switch (bytes[++i])
187+
{
188+
case 14:
189+
sb.Append("ARRAY<");
190+
var type = bytes[++i];
191+
192+
Debug.Assert(type == 10);
193+
var dims = bytes[++i];
194+
195+
++i;
196+
197+
long length = 0;
198+
for (var j = 0; j < dims; j++)
199+
{
200+
var lengthBytes = bytes.AsSpan()[i..(i + 4)];
201+
var lengthValue = MemoryMarshal.Cast<byte, uint>(lengthBytes)[0];
202+
if (length == 0)
203+
{
204+
length = lengthValue;
205+
}
206+
else
207+
{
208+
length *= lengthValue;
209+
}
210+
211+
sb.Append(lengthValue);
212+
sb.Append(',');
213+
i += 4;
214+
}
215+
216+
sb.Remove(sb.Length - 1, 1);
217+
sb.Append('>');
218+
219+
var doubleBytes =
220+
MemoryMarshal.Cast<byte, double>(bytes.AsSpan().Slice(i, (int)(length * 8)));
221+
222+
223+
sb.Append('[');
224+
for (var j = 0; j < length; j++)
225+
{
226+
sb.Append(doubleBytes[j]);
227+
sb.Append(',');
228+
}
229+
230+
sb.Remove(sb.Length - 1, 1);
231+
sb.Append(']');
232+
233+
i += (int)(length * 8);
234+
i--;
235+
break;
236+
case 16:
237+
sb.Remove(sb.Length - 1, 1);
238+
var doubleValue = MemoryMarshal.Cast<byte, double>(bytes.AsSpan().Slice(++i, 8));
239+
sb.Append(doubleValue[0]);
240+
i += 8;
241+
i--;
242+
break;
243+
default:
244+
throw new NotImplementedException();
245+
}
246+
247+
lastAppend = i + 1;
248+
}
249+
}
250+
}
251+
252+
sb.Append(Encoding.UTF8.GetString(bytes, lastAppend, i - lastAppend));
253+
return sb.ToString();
254+
}
163255
}

src/dummy-http-server/IlpEndpoint.cs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,46 +24,63 @@
2424
******************************************************************************/
2525

2626

27-
using System;
28-
using System.Linq;
27+
using System.Diagnostics.CodeAnalysis;
2928
using System.Text;
30-
using System.Threading;
31-
using System.Threading.Tasks;
3229
using FastEndpoints;
30+
3331
// ReSharper disable ClassNeverInstantiated.Global
3432
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
3533

3634
namespace dummy_http_server;
3735

38-
public record Request : IPlainTextRequest
36+
public record Request
3937
{
40-
public string Content { get; set; }
38+
public byte[] ByteContent { get; init; }
39+
public string StringContent { get; init; }
4140
}
4241

42+
[SuppressMessage("ReSharper", "InconsistentNaming")]
4343
public record JsonErrorResponse
4444
{
4545
public string code { get; init; }
4646
public string message { get; init; }
4747
public int line { get; init; }
4848
public string errorId { get; init; }
49-
49+
5050
public override string ToString()
5151
{
52-
return $"\nServer Response (\n\tCode: `{code}`\n\tMessage: `{message}`\n\tLine: `{line}`\n\tErrorId: `{errorId}` \n)";
52+
return
53+
$"\nServer Response (\n\tCode: `{code}`\n\tMessage: `{message}`\n\tLine: `{line}`\n\tErrorId: `{errorId}` \n)";
54+
}
55+
}
56+
57+
public class Binder : IRequestBinder<Request>
58+
{
59+
public async ValueTask<Request> BindAsync(BinderContext ctx, CancellationToken ct)
60+
{
61+
// populate and return a request dto object however you please...
62+
var ms = new MemoryStream();
63+
await ctx.HttpContext.Request.Body.CopyToAsync(ms, ct);
64+
return new Request
65+
{
66+
ByteContent = ms.ToArray(),
67+
StringContent = Encoding.UTF8.GetString(ms.ToArray()),
68+
};
5369
}
5470
}
5571

5672
public class IlpEndpoint : Endpoint<Request, JsonErrorResponse?>
5773
{
74+
private const string Username = "admin";
75+
private const string Password = "quest";
5876
public static readonly StringBuilder ReceiveBuffer = new();
77+
public static readonly List<byte> ReceiveBytes = new();
5978
public static Exception? LastError = new();
6079
public static bool WithTokenAuth = false;
6180
public static bool WithBasicAuth = false;
6281
public static bool WithRetriableError = false;
6382
public static bool WithErrorMessage = false;
64-
private const string Username = "admin";
65-
private const string Password = "quest";
66-
public static int Counter = 0;
83+
public static int Counter;
6784

6885
public override void Configure()
6986
{
@@ -79,6 +96,7 @@ public override void Configure()
7996
}
8097

8198
Description(b => b.Accepts<Request>());
99+
RequestBinder(new Binder());
82100
}
83101

84102
public override async Task HandleAsync(Request req, CancellationToken ct)
@@ -92,14 +110,15 @@ public override async Task HandleAsync(Request req, CancellationToken ct)
92110

93111
if (WithErrorMessage)
94112
{
95-
await SendAsync(new JsonErrorResponse()
96-
{ code = "code", errorId = "errorid", line = 1, message = "message" }, 400, ct);
113+
await SendAsync(new JsonErrorResponse
114+
{ code = "code", errorId = "errorid", line = 1, message = "message", }, 400, ct);
97115
return;
98116
}
99-
117+
100118
try
101119
{
102-
ReceiveBuffer.Append(req.Content);
120+
ReceiveBuffer.Append(req.StringContent);
121+
ReceiveBytes.AddRange(req.ByteContent);
103122
await SendNoContentAsync(ct);
104123
}
105124
catch (Exception ex)

0 commit comments

Comments
 (0)