Skip to content

Commit 678e526

Browse files
committed
Upgrade deps and namespace + CancellationToken correctness
1 parent cbcd638 commit 678e526

File tree

8 files changed

+140
-145
lines changed

8 files changed

+140
-145
lines changed
Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using Npgsql;
22

3+
namespace Wololo.PgKeyValueDB;
4+
35
internal static class NpgsqlDataSourceExtensions
46
{
57
public record struct NpgsqlCommandContext(string Sql, IEnumerable<NpgsqlParameter>? Parameters = null, bool Prepare = true);
@@ -25,7 +27,23 @@ internal static int Execute(this NpgsqlDataSource dataSource, NpgsqlCommandConte
2527
}
2628
}
2729

28-
internal static async Task<int> ExecuteAsync(this NpgsqlDataSource dataSource, NpgsqlCommandContext context)
30+
internal static T? Execute<T>(this NpgsqlDataSource dataSource, NpgsqlCommandContext context)
31+
{
32+
using var conn = dataSource.OpenConnection();
33+
using var cmd = new NpgsqlCommand(context.Sql, conn);
34+
if (context.Parameters != null)
35+
foreach (var parameter in context.Parameters)
36+
cmd.Parameters.Add(parameter);
37+
if (context.Prepare)
38+
cmd.Prepare();
39+
using var reader = cmd.ExecuteReader();
40+
if (!reader.Read())
41+
return default;
42+
var value = reader.GetFieldValue<T>(0);
43+
return value;
44+
}
45+
46+
internal static async Task<int> ExecuteAsync(this NpgsqlDataSource dataSource, NpgsqlCommandContext context, CancellationToken token)
2947
{
3048
try
3149
{
@@ -35,8 +53,8 @@ internal static async Task<int> ExecuteAsync(this NpgsqlDataSource dataSource, N
3553
foreach (var parameter in context.Parameters)
3654
cmd.Parameters.Add(parameter);
3755
if (context.Prepare)
38-
await cmd.PrepareAsync();
39-
return await cmd.ExecuteNonQueryAsync();
56+
await cmd.PrepareAsync(token);
57+
return await cmd.ExecuteNonQueryAsync(token);
4058
}
4159
catch (PostgresException e)
4260
{
@@ -46,49 +64,33 @@ internal static async Task<int> ExecuteAsync(this NpgsqlDataSource dataSource, N
4664
}
4765
}
4866

49-
internal static T? Execute<T>(this NpgsqlDataSource dataSource, NpgsqlCommandContext context)
50-
{
51-
using var conn = dataSource.OpenConnection();
52-
using var cmd = new NpgsqlCommand(context.Sql, conn);
53-
if (context.Parameters != null)
54-
foreach (var parameter in context.Parameters)
55-
cmd.Parameters.Add(parameter);
56-
if (context.Prepare)
57-
cmd.Prepare();
58-
using var reader = cmd.ExecuteReader();
59-
if (!reader.Read())
60-
return default;
61-
var value = reader.GetFieldValue<T>(0);
62-
return value;
63-
}
64-
65-
internal static async Task<T?> ExecuteAsync<T>(this NpgsqlDataSource dataSource, NpgsqlCommandContext context)
67+
internal static async Task<T?> ExecuteAsync<T>(this NpgsqlDataSource dataSource, NpgsqlCommandContext context, CancellationToken token)
6668
{
67-
await using var conn = await dataSource.OpenConnectionAsync();
69+
await using var conn = await dataSource.OpenConnectionAsync(token);
6870
await using var cmd = new NpgsqlCommand(context.Sql, conn);
6971
if (context.Parameters != null)
7072
foreach (var parameter in context.Parameters)
7173
cmd.Parameters.Add(parameter);
7274
if (context.Prepare)
73-
cmd.Prepare();
74-
await using var reader = await cmd.ExecuteReaderAsync();
75-
if (!await reader.ReadAsync())
75+
await cmd.PrepareAsync(token);
76+
await using var reader = await cmd.ExecuteReaderAsync(token);
77+
if (!await reader.ReadAsync(token))
7678
return default;
7779
var value = await reader.GetFieldValueAsync<T>(0);
7880
return value;
7981
}
8082

81-
internal static async IAsyncEnumerable<T> ExecuteListAsync<T>(this NpgsqlDataSource dataSource, NpgsqlCommandContext context)
83+
internal static async IAsyncEnumerable<T> ExecuteListAsync<T>(this NpgsqlDataSource dataSource, NpgsqlCommandContext context, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken token)
8284
{
8385
await using var conn = await dataSource.OpenConnectionAsync();
8486
await using var cmd = new NpgsqlCommand(context.Sql, conn);
8587
if (context.Parameters != null)
8688
foreach (var parameter in context.Parameters)
8789
cmd.Parameters.Add(parameter);
8890
if (context.Prepare)
89-
cmd.Prepare();
90-
await using var reader = await cmd.ExecuteReaderAsync();
91-
while (reader.Read())
92-
yield return reader.GetFieldValue<T>(0);
91+
await cmd.PrepareAsync(token);
92+
await using var reader = await cmd.ExecuteReaderAsync(token);
93+
while (await reader.ReadAsync(token))
94+
yield return await reader.GetFieldValueAsync<T>(0);
9395
}
9496
}

src/PgKeyValueDB/PgKeyValueDB.cs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Text.Json;
33
using Npgsql;
44
using NpgsqlTypes;
5-
using Ctx = NpgsqlDataSourceExtensions.NpgsqlCommandContext;
5+
using Ctx = Wololo.PgKeyValueDB.NpgsqlDataSourceExtensions.NpgsqlCommandContext;
66

77
namespace Wololo.PgKeyValueDB;
88

@@ -20,7 +20,7 @@ public PgKeyValueDB(NpgsqlDataSource dataSource, string schemaName, string table
2020
this.dataSource = dataSource;
2121
this.schemaName = schemaName;
2222
this.tableName = tableName;
23-
this.tableRef = $"{schemaName}.{tableName}";
23+
tableRef = $"{schemaName}.{tableName}";
2424
JsonSerializerOptions = jsonSerializerOptions;
2525
Init();
2626
}
@@ -42,15 +42,15 @@ primary key (pid, id)
4242
dataSource.Execute(new Ctx($"create index if not exists idx_{schemaName}_{tableName}_expires on {tableRef} (expires) where expires is not null", Prepare: false));
4343
}
4444

45-
static IEnumerable<NpgsqlParameter> CreateParams(string pid, string? id = null)
45+
static List<NpgsqlParameter> CreateParams(string pid, string? id = null)
4646
{
4747
var baseParams = new List<NpgsqlParameter> { new() { ParameterName = "pid", Value = pid } };
4848
if (id != null)
4949
baseParams.Add(new() { ParameterName = "id", Value = id });
5050
return baseParams;
5151
}
5252

53-
static IEnumerable<NpgsqlParameter> CreateParams<T>(string pid, string? id, T? value, DateTimeOffset? expires)
53+
static List<NpgsqlParameter> CreateParams<T>(string pid, string? id, T? value, DateTimeOffset? expires)
5454
{
5555
var baseParams = new List<NpgsqlParameter> { new() { ParameterName = "pid", Value = pid } };
5656
if (id != null)
@@ -76,52 +76,52 @@ static IEnumerable<NpgsqlParameter> CreateParams<T>(string pid, string? id, T? v
7676

7777
public bool Create<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null) =>
7878
dataSource.Execute(new Ctx(CreateCreateSql, CreateParams(pid, id, value, expires))) > 0;
79-
public async Task<bool> CreateAsync<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null) =>
80-
await dataSource.ExecuteAsync(new Ctx(CreateCreateSql, CreateParams(pid, id, value, expires))) > 0;
79+
public async Task<bool> CreateAsync<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null, CancellationToken token = default) =>
80+
await dataSource.ExecuteAsync(new Ctx(CreateCreateSql, CreateParams(pid, id, value, expires)), token) > 0;
8181
public bool Update<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null) =>
8282
dataSource.Execute(new Ctx(UpdateSql, CreateParams(pid, id, value, expires))) > 0;
83-
public async Task<bool> UpdateAsync<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null) =>
84-
await dataSource.ExecuteAsync(new Ctx(UpdateSql, CreateParams(pid, id, value, expires))) > 0;
83+
public async Task<bool> UpdateAsync<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null, CancellationToken token = default) =>
84+
await dataSource.ExecuteAsync(new Ctx(UpdateSql, CreateParams(pid, id, value, expires)), token) > 0;
8585
public bool Upsert<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null) =>
8686
dataSource.Execute(new Ctx(UpsertSql, CreateParams(pid, id, value, expires))) > 0;
87-
public async Task<bool> UpsertAsync<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null) =>
88-
await dataSource.ExecuteAsync(new Ctx(UpsertSql, CreateParams(pid, id, value, expires))) > 0;
87+
public async Task<bool> UpsertAsync<T>(string id, T value, string pid = DEFAULT_PID, DateTimeOffset? expires = null, CancellationToken token = default) =>
88+
await dataSource.ExecuteAsync(new Ctx(UpsertSql, CreateParams(pid, id, value, expires)), token) > 0;
8989
public bool Remove(string id, string pid = DEFAULT_PID) =>
9090
dataSource.Execute(new Ctx(DeleteSql, CreateParams(pid, id))) > 0;
91-
public async Task<bool> RemoveAsync(string id, string pid = DEFAULT_PID) =>
92-
await dataSource.ExecuteAsync(new Ctx(DeleteSql, CreateParams(pid, id))) > 0;
91+
public async Task<bool> RemoveAsync(string id, string pid = DEFAULT_PID, CancellationToken token = default) =>
92+
await dataSource.ExecuteAsync(new Ctx(DeleteSql, CreateParams(pid, id)), token) > 0;
9393
public int RemoveAll(string pid = DEFAULT_PID) =>
9494
dataSource.Execute(new Ctx(DeleteAllSql, CreateParams(pid)));
9595
public int RemoveAll<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null) =>
9696
dataSource.Execute(BuildCommandParams(DeleteAllSql, pid, where));
97-
public async Task<int> RemoveAllAsync(string pid = DEFAULT_PID) =>
98-
await dataSource.ExecuteAsync(new Ctx(DeleteAllSql, CreateParams(pid)));
99-
public async Task<int> RemoveAllAsync<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null) =>
100-
await dataSource.ExecuteAsync(BuildCommandParams(DeleteAllSql, pid, where));
97+
public async Task<int> RemoveAllAsync(string pid = DEFAULT_PID, CancellationToken token = default) =>
98+
await dataSource.ExecuteAsync(new Ctx(DeleteAllSql, CreateParams(pid)), token);
99+
public async Task<int> RemoveAllAsync<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null, CancellationToken token = default) =>
100+
await dataSource.ExecuteAsync(BuildCommandParams(DeleteAllSql, pid, where), token);
101101
public int RemoveAllExpired(string pid = DEFAULT_PID) =>
102102
dataSource.Execute(new Ctx(DeleteAllExpiredSql, CreateParams(pid)));
103-
public async Task<int> RemoveAllExpiredAsync(string pid = DEFAULT_PID) =>
104-
await dataSource.ExecuteAsync(new Ctx(DeleteAllExpiredSql, CreateParams(pid)));
103+
public async Task<int> RemoveAllExpiredAsync(string pid = DEFAULT_PID, CancellationToken token = default) =>
104+
await dataSource.ExecuteAsync(new Ctx(DeleteAllExpiredSql, CreateParams(pid)), token);
105105
public int RemoveAllExpiredGlobal() =>
106106
dataSource.Execute(new Ctx(DeleteAllExpiredGlobalSql));
107-
public async Task<int> RemoveAllExpiredGlobalAsync() =>
108-
await dataSource.ExecuteAsync(new Ctx(DeleteAllExpiredGlobalSql));
107+
public async Task<int> RemoveAllExpiredGlobalAsync(CancellationToken token = default) =>
108+
await dataSource.ExecuteAsync(new Ctx(DeleteAllExpiredGlobalSql), token);
109109
public T? Get<T>(string id, string pid = DEFAULT_PID) =>
110110
dataSource.Execute<T>(new Ctx(SelectSql, CreateParams(pid, id)));
111-
public async Task<T?> GetAsync<T>(string id, string pid = DEFAULT_PID) =>
112-
await dataSource.ExecuteAsync<T>(new Ctx(SelectSql, CreateParams(pid, id)));
111+
public async Task<T?> GetAsync<T>(string id, string pid = DEFAULT_PID, CancellationToken token = default) =>
112+
await dataSource.ExecuteAsync<T>(new Ctx(SelectSql, CreateParams(pid, id)), token);
113113
public bool Exists(string id, string pid = DEFAULT_PID) =>
114114
dataSource.Execute<bool>(new Ctx(ExistsSql, CreateParams(pid, id)));
115-
public async Task<bool> ExistsAsync(string id, string pid = DEFAULT_PID) =>
116-
await dataSource.ExecuteAsync<bool>(new Ctx(ExistsSql, CreateParams(pid, id)));
115+
public async Task<bool> ExistsAsync(string id, string pid = DEFAULT_PID, CancellationToken token = default) =>
116+
await dataSource.ExecuteAsync<bool>(new Ctx(ExistsSql, CreateParams(pid, id)), token);
117117
public long Count(string pid = DEFAULT_PID) =>
118118
dataSource.Execute<long>(new Ctx(CountSql, CreateParams(pid)));
119119
public long Count<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null) =>
120120
dataSource.Execute<long>(BuildCommandParams(CountSql, pid, where));
121-
public async Task<long> CountAsync(string pid = DEFAULT_PID) =>
122-
await dataSource.ExecuteAsync<long>(new Ctx(CountSql, CreateParams(pid)));
123-
public async Task<long> CountAsync<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null) =>
124-
await dataSource.ExecuteAsync<long>(BuildCommandParams(CountSql, pid, where));
121+
public async Task<long> CountAsync(string pid = DEFAULT_PID, CancellationToken token = default) =>
122+
await dataSource.ExecuteAsync<long>(new Ctx(CountSql, CreateParams(pid)), token);
123+
public async Task<long> CountAsync<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null, CancellationToken token = default) =>
124+
await dataSource.ExecuteAsync<long>(BuildCommandParams(CountSql, pid, where), token);
125125

126126
private Ctx BuildCommandParams<T>(string sql, string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null)
127127
{
@@ -136,7 +136,7 @@ private Ctx BuildCommandParams<T>(string sql, string pid = DEFAULT_PID, Expressi
136136
return new Ctx(sql, baseParams);
137137
}
138138

139-
public IAsyncEnumerable<T> GetListAsync<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null, long? limit = null, long? offset = null)
139+
public IAsyncEnumerable<T> GetListAsync<T>(string pid = DEFAULT_PID, Expression<Func<T, bool>>? where = null, long? limit = null, long? offset = null, CancellationToken token = default)
140140
{
141141
var sql = $"select value from {tableRef} where pid = @pid and (expires is null or now() < expires)";
142142
var baseParams = new List<NpgsqlParameter>
@@ -156,6 +156,6 @@ public IAsyncEnumerable<T> GetListAsync<T>(string pid = DEFAULT_PID, Expression<
156156
{
157157
sql += " limit @limit offset @offset";
158158
}
159-
return dataSource.ExecuteListAsync<T>(new Ctx(sql, baseParams));
159+
return dataSource.ExecuteListAsync<T>(new Ctx(sql, baseParams), token);
160160
}
161161
}

src/PgKeyValueDB/PgKeyValueDB.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
<TargetFramework>net8.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7+
<RootNamespace>Wololo.PgKeyValueDB</RootNamespace>
78
<PackageReadmeFile>README.md</PackageReadmeFile>
89
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
910
<RepositoryUrl>https://github.com/bjornharrtell/PgKeyValueDB</RepositoryUrl>
1011
</PropertyGroup>
1112

1213
<ItemGroup>
13-
<PackageReference Include="Npgsql" Version="9.0.3" />
14-
<PackageReference Include="Npgsql.DependencyInjection" Version="9.0.3" />
14+
<PackageReference Include="Npgsql" Version="9.0.4" />
15+
<PackageReference Include="Npgsql.DependencyInjection" Version="9.0.4" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

src/PgKeyValueDB/ServiceCollectionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using System.Text.Json;
2+
using Microsoft.Extensions.DependencyInjection;
23
using Microsoft.Extensions.DependencyInjection.Extensions;
34
using Npgsql;
4-
using Wololo.PgKeyValueDB;
55

6-
namespace Microsoft.Extensions.DependencyInjection;
6+
namespace Wololo.PgKeyValueDB;
77

88
public static class NpgsqlServiceCollectionExtensions
99
{

src/PgKeyValueDB/SqlExpressionVisitor.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class SqlExpressionVisitor(Type documentType, JsonSerializerOptions jsonS
1717
private readonly JsonNamingPolicy propertyNamingPolicy = jsonSerializerOptions.PropertyNamingPolicy ?? JsonNamingPolicy.CamelCase;
1818

1919
public string WhereClause => whereClause.ToString();
20-
public NpgsqlParameter[] Parameters => parameters.ToArray();
20+
public NpgsqlParameter[] Parameters => [.. parameters];
2121

2222
protected override Expression VisitBinary(BinaryExpression node)
2323
{
@@ -59,15 +59,6 @@ private static bool IsNumericType(Type type)
5959
type == typeof(double) || type == typeof(float) || type == typeof(short);
6060
}
6161

62-
private static string GetCommonNumericType(Type type1, Type type2)
63-
{
64-
if (type1 == typeof(decimal) || type2 == typeof(decimal)) return "numeric";
65-
if (type1 == typeof(double) || type2 == typeof(double)) return "double precision";
66-
if (type1 == typeof(float) || type2 == typeof(float)) return "real";
67-
if (type1 == typeof(long) || type2 == typeof(long)) return "bigint";
68-
return "integer";
69-
}
70-
7162
private static string GetOperator(ExpressionType nodeType) => nodeType switch
7263
{
7364
ExpressionType.Equal => " = ",
@@ -84,16 +75,16 @@ private static string GetCommonNumericType(Type type1, Type type2)
8475
protected override Expression VisitMethodCall(MethodCallExpression node)
8576
{
8677
// Handle IsNullOrWhiteSpace extension method calls
87-
if (node.Method.Name == nameof(string.IsNullOrWhiteSpace) &&
88-
node.Method.IsStatic &&
78+
if (node.Method.Name == nameof(string.IsNullOrWhiteSpace) &&
79+
node.Method.IsStatic &&
8980
node.Arguments.Count == 1 &&
9081
node.Arguments[0].Type == typeof(string))
9182
{
9283
var argument = node.Arguments[0];
93-
84+
9485
// Check if it's a constant/closure variable (not a property access on the entity)
95-
if (argument.NodeType == ExpressionType.Constant ||
96-
(argument.NodeType == ExpressionType.MemberAccess &&
86+
if (argument.NodeType == ExpressionType.Constant ||
87+
(argument.NodeType == ExpressionType.MemberAccess &&
9788
((MemberExpression)argument).Expression?.NodeType == ExpressionType.Constant))
9889
{
9990
// For constants/variables, evaluate the IsNullOrWhiteSpace call and use the result

test/PgKeyValueDB.Tests/GlobalUsings.cs

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/PgKeyValueDB.Tests/PgKeyValueDB.Tests.csproj

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44
<TargetFramework>net8.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7-
7+
<RootNamespace>Wololo.PgKeyValueDB.Tests</RootNamespace>
88
<IsPackable>false</IsPackable>
99
<IsTestProject>true</IsTestProject>
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
1414
<PackageReference Include="MysticMind.PostgresEmbed" Version="4.0.0" />
15-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
16-
<PackageReference Include="Npgsql.DependencyInjection" Version="9.0.3" />
17-
<PackageReference Include="MSTest.TestAdapter" Version="3.9.3" />
18-
<PackageReference Include="MSTest.TestFramework" Version="3.9.3" />
19-
<PackageReference Include="coverlet.collector" Version="6.0.4">
20-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
21-
<PrivateAssets>all</PrivateAssets>
15+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
16+
<PackageReference Include="Npgsql.DependencyInjection" Version="9.0.4" />
17+
<PackageReference Include="MSTest.TestAdapter" Version="4.0.0" />
18+
<PackageReference Include="MSTest.TestFramework" Version="4.0.0" />
19+
<PackageReference Include="coverlet.collector" Version="6.0.4">
20+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
21+
<PrivateAssets>all</PrivateAssets>
2222
</PackageReference>
2323
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
2424
</ItemGroup>

0 commit comments

Comments
 (0)