Skip to content

Commit 7ef908d

Browse files
authored
Add NpgsqlDataSource.ReloadTypes{Async} (npgsql#5919)
1 parent 919ed97 commit 7ef908d

File tree

4 files changed

+98
-3
lines changed

4 files changed

+98
-3
lines changed

src/Npgsql/NpgsqlConnection.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1897,7 +1897,7 @@ public void ReloadTypes()
18971897
/// Flushes the type cache for this connection's connection string and reloads the types for this connection only.
18981898
/// Type changes will appear for other connections only after they are re-opened from the pool.
18991899
/// </summary>
1900-
public async Task ReloadTypesAsync()
1900+
public async Task ReloadTypesAsync(CancellationToken cancellationToken = default)
19011901
{
19021902
CheckReady();
19031903

@@ -1908,7 +1908,7 @@ public async Task ReloadTypesAsync()
19081908
NpgsqlTimeout.Infinite,
19091909
forceReload: true,
19101910
async: true,
1911-
CancellationToken.None).ConfigureAwait(false);
1911+
cancellationToken).ConfigureAwait(false);
19121912
}
19131913

19141914
/// <summary>

src/Npgsql/NpgsqlDataSource.cs

+23-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
66
using System.Net.Security;
7-
using System.Security.Cryptography.X509Certificates;
87
using System.Threading;
98
using System.Threading.Tasks;
109
using System.Transactions;
@@ -227,6 +226,29 @@ public static NpgsqlDataSource Create(string connectionString)
227226
public static NpgsqlDataSource Create(NpgsqlConnectionStringBuilder connectionStringBuilder)
228227
=> Create(connectionStringBuilder.ToString());
229228

229+
/// <summary>
230+
/// Flushes the type cache for this data source.
231+
/// Type changes will appear for connections only after they are re-opened from the pool.
232+
/// </summary>
233+
public void ReloadTypes()
234+
{
235+
using var connection = OpenConnection();
236+
connection.ReloadTypes();
237+
}
238+
239+
/// <summary>
240+
/// Flushes the type cache for this data source.
241+
/// Type changes will appear for connections only after they are re-opened from the pool.
242+
/// </summary>
243+
public async Task ReloadTypesAsync(CancellationToken cancellationToken = default)
244+
{
245+
var connection = await OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
246+
await using (connection.ConfigureAwait(false))
247+
{
248+
await connection.ReloadTypesAsync(cancellationToken).ConfigureAwait(false);
249+
}
250+
}
251+
230252
internal async Task Bootstrap(
231253
NpgsqlConnector connector,
232254
NpgsqlTimeout timeout,

src/Npgsql/PublicAPI.Unshipped.txt

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ Npgsql.SslNegotiation
6161
Npgsql.SslNegotiation.Direct = 1 -> Npgsql.SslNegotiation
6262
Npgsql.SslNegotiation.Postgres = 0 -> Npgsql.SslNegotiation
6363
override Npgsql.NpgsqlMultiHostDataSource.Clear() -> void
64+
Npgsql.NpgsqlDataSource.ReloadTypes() -> void
65+
Npgsql.NpgsqlDataSource.ReloadTypesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
66+
Npgsql.NpgsqlConnection.ReloadTypesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
67+
*REMOVED*Npgsql.NpgsqlConnection.ReloadTypesAsync() -> System.Threading.Tasks.Task!
6468
*REMOVED*Npgsql.NpgsqlDataSourceBuilder.MapComposite(System.Type! clrType, string? pgName = null, Npgsql.INpgsqlNameTranslator? nameTranslator = null) -> Npgsql.TypeMapping.INpgsqlTypeMapper!
6569
*REMOVED*Npgsql.NpgsqlDataSourceBuilder.MapComposite<T>(string? pgName = null, Npgsql.INpgsqlNameTranslator? nameTranslator = null) -> Npgsql.TypeMapping.INpgsqlTypeMapper!
6670
*REMOVED*Npgsql.NpgsqlDataSourceBuilder.MapEnum(System.Type! clrType, string? pgName = null, Npgsql.INpgsqlNameTranslator? nameTranslator = null) -> Npgsql.TypeMapping.INpgsqlTypeMapper!

test/Npgsql.Tests/DataSourceTests.cs

+69
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text.Json.Serialization;
66
using System.Threading.Tasks;
77
using NUnit.Framework;
8+
using static Npgsql.Tests.TestUtil;
89

910
// ReSharper disable MethodHasAsyncOverload
1011

@@ -380,4 +381,72 @@ public async Task ConfigureJsonOptions_is_order_independent()
380381
Assert.That(reader.GetFieldValue<Test>(0).Id, Is.EqualTo(1));
381382
}
382383
}
384+
385+
[Test]
386+
public async Task ReloadTypes([Values] bool async)
387+
{
388+
await using var adminConnection = await OpenConnectionAsync();
389+
var type = await GetTempTypeName(adminConnection);
390+
391+
var dataSourceBuilder = CreateDataSourceBuilder();
392+
dataSourceBuilder.MapEnum<Mood>(type);
393+
await using var dataSource = dataSourceBuilder.Build();
394+
395+
await using var connection = await dataSource.OpenConnectionAsync();
396+
await connection.ExecuteNonQueryAsync($"CREATE TYPE {type} AS ENUM ('sad', 'ok', 'happy')");
397+
398+
if (async)
399+
await dataSource.ReloadTypesAsync();
400+
else
401+
dataSource.ReloadTypes();
402+
403+
Assert.ThrowsAsync<InvalidCastException>(async () => await connection.ExecuteScalarAsync($"SELECT 'happy'::{type}"));
404+
405+
// Close connection and reopen to make sure it picks up the new type and mapping from the data source
406+
await connection.CloseAsync();
407+
await connection.OpenAsync();
408+
409+
Assert.DoesNotThrowAsync(async () => await connection.ExecuteScalarAsync($"SELECT 'happy'::{type}"));
410+
}
411+
412+
[Test]
413+
public async Task ReloadTypes_across_data_sources([Values] bool async)
414+
{
415+
await using var adminConnection = await OpenConnectionAsync();
416+
var type = await GetTempTypeName(adminConnection);
417+
418+
var dataSourceBuilder = CreateDataSourceBuilder();
419+
dataSourceBuilder.MapEnum<Mood>(type);
420+
await using var dataSource1 = dataSourceBuilder.Build();
421+
await using var connection1 = await dataSource1.OpenConnectionAsync();
422+
423+
await using var dataSource2 = dataSourceBuilder.Build();
424+
await using var connection2 = await dataSource2.OpenConnectionAsync();
425+
426+
await connection1.ExecuteNonQueryAsync($"CREATE TYPE {type} AS ENUM ('sad', 'ok', 'happy')");
427+
428+
if (async)
429+
await dataSource1.ReloadTypesAsync();
430+
else
431+
dataSource1.ReloadTypes();
432+
433+
Assert.ThrowsAsync<InvalidCastException>(async () => await connection1.ExecuteScalarAsync($"SELECT 'happy'::{type}"));
434+
Assert.ThrowsAsync<InvalidCastException>(async () => await connection2.ExecuteScalarAsync($"SELECT 'happy'::{type}"));
435+
436+
// Close connection and reopen to check that the new type and mapping is not available in dataSource2
437+
await connection2.CloseAsync();
438+
await connection2.OpenAsync();
439+
440+
Assert.ThrowsAsync<InvalidCastException>(async () => await connection2.ExecuteScalarAsync($"SELECT 'happy'::{type}"));
441+
442+
await dataSource2.ReloadTypesAsync();
443+
444+
// Close connection2 and reopen to make sure it picks up the new type and mapping from dataSource2
445+
await connection2.CloseAsync();
446+
await connection2.OpenAsync();
447+
448+
Assert.DoesNotThrowAsync(async () => await connection2.ExecuteScalarAsync($"SELECT 'happy'::{type}"));
449+
}
450+
451+
enum Mood { Sad, Ok, Happy }
383452
}

0 commit comments

Comments
 (0)