-
Notifications
You must be signed in to change notification settings - Fork 877
Add API to NpgsqlDataSource to resolve type mappings #6084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| using System.Transactions; | ||
| using Microsoft.Extensions.Logging; | ||
| using Npgsql.Internal; | ||
| using Npgsql.Internal.Postgres; | ||
| using Npgsql.Internal.ResolverFactories; | ||
| using Npgsql.Properties; | ||
| using Npgsql.Util; | ||
|
|
@@ -247,6 +248,68 @@ public async Task ReloadTypesAsync(CancellationToken cancellationToken = default | |
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Attempts to return a mapping for a specific type. | ||
| /// </summary> | ||
| public PgTypeInfo? TryGetMapping<T>(string? dataTypeName = null) => TryGetMapping(typeof(T), dataTypeName); | ||
|
|
||
| /// <summary> | ||
| /// Attempts to return a mapping for a specific type. | ||
| /// </summary> | ||
| public PgTypeInfo? TryGetMapping(Type? type = null, string? dataTypeName = null) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API design note: rather than a single method with two nullable parameters - which throws when both are null - we can have two methods, so that the annotations prevent the call at compile-time (internally they'd both just immediately call into a single method). |
||
| { | ||
| if (type is null && string.IsNullOrEmpty(dataTypeName)) | ||
| throw new ArgumentException($"Either {nameof(type)} or {nameof(dataTypeName)} must be specified."); | ||
|
|
||
| // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract | ||
| if (SerializerOptions is null) | ||
| using (OpenConnection()) { } | ||
|
|
||
| return TryGetMappingCore(type, dataTypeName); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Attempts to return a mapping for a specific type. | ||
| /// </summary> | ||
| public ValueTask<PgTypeInfo?> TryGetMappingAsync<T>(string? dataTypeName = null, CancellationToken cancellationToken = default) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that for cases where we don't actually need a generic type parameter, i.e. a Type parameter is enough (and will likely always be enough), there's no reason to introduce the additional generic API. |
||
| => TryGetMappingAsync(typeof(T), dataTypeName, cancellationToken); | ||
|
|
||
| /// <summary> | ||
| /// Attempts to return a mapping for a specific type. | ||
| /// </summary> | ||
| public async ValueTask<PgTypeInfo?> TryGetMappingAsync(Type? type = null, string? dataTypeName = null, CancellationToken cancellationToken = default) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, ideally we'd be able to return mapping info to the user without ever connecting to the database, purely based on how the data source was configured - in which case we wouldn't need an async API here. This is a bit related to us returning a separate type (not PgTypeInfo), which would presumably return restricted info that's purely client-side mapping config. Or am I missing something and this mapping information API really doesn't make sense without first connecting to the database and getting actual type OIDs etc.? |
||
| { | ||
| if (type is null && string.IsNullOrEmpty(dataTypeName)) | ||
| throw new ArgumentException($"Either {nameof(type)} or {nameof(dataTypeName)} must be specified."); | ||
|
|
||
| // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract | ||
| if (SerializerOptions is null) | ||
| { | ||
| var connection = await OpenConnectionAsync(cancellationToken).ConfigureAwait(false); | ||
| await connection.DisposeAsync().ConfigureAwait(false); | ||
| } | ||
|
|
||
| return TryGetMappingCore(type, dataTypeName); | ||
| } | ||
|
|
||
| PgTypeInfo? TryGetMappingCore(Type? type, string? dataTypeName) | ||
| { | ||
| Debug.Assert(IsBootstrapped); | ||
| Debug.Assert(SerializerOptions is not null); | ||
| Debug.Assert(DatabaseInfo is not null); | ||
|
|
||
| PgTypeId? pgTypeID = null; | ||
|
|
||
| if (!string.IsNullOrEmpty(dataTypeName)) | ||
| { | ||
| if (!DatabaseInfo.TryGetPostgresTypeByName(DataTypeName.NormalizeName(dataTypeName), out var pgType)) | ||
| throw new NotSupportedException($"The data type name '{dataTypeName}' isn't present in your database. You may need to install an extension or upgrade to a newer version."); | ||
| pgTypeID = SerializerOptions.ToCanonicalTypeId(pgType.GetRepresentationalType()); | ||
| } | ||
|
|
||
| return SerializerOptions.GetTypeInfoInternal(type, pgTypeID); | ||
| } | ||
|
|
||
| internal async Task Bootstrap( | ||
| NpgsqlConnector connector, | ||
| NpgsqlTimeout timeout, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that PgTypeInfo is [Experimental], and also in the Internal namespace - I think that up to now the idea was that this was only for converter writers; we'd be exposing this to users for the first time. This also shows in the public API surface of this type - most of the stuff there probably shouldn't be shown to users.
I think that if we want to add an introspection API like this, we need another type. We do have the PostgresType hierarchy, but that really models the PG type only (no CLR type there, for example).
(just noting that there's no real urgent need for this API from my side)
/cc @NinoFloris
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. We just have to agree on what exactly that type has to have. Probably, CLR type, the name of postgres's type + whether it supports writing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NpgsqlMappingInfo or something? Maybe @NinoFloris has a good idea here.
It could actually reference the PostgresType rather than just a string name (for full introspection capabilities). Besides that, just having the CLR type seems sufficient to me as a start, writability seems like an extra thing we don't necessarily need to do...