Skip to content

Commit d1e7910

Browse files
authored
Data source type mapper (#4691)
And remove connection type mapping Closes #4494
1 parent 36b3857 commit d1e7910

57 files changed

Lines changed: 1602 additions & 2676 deletions

Some content is hidden

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

Npgsql.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<s:Boolean x:Key="/Default/UserDictionary/Words/=bytea/@EntryIndexedValue">True</s:Boolean>
8686
<s:Boolean x:Key="/Default/UserDictionary/Words/=citext/@EntryIndexedValue">True</s:Boolean>
8787
<s:Boolean x:Key="/Default/UserDictionary/Words/=Conformant/@EntryIndexedValue">True</s:Boolean>
88+
<s:Boolean x:Key="/Default/UserDictionary/Words/=containee/@EntryIndexedValue">True</s:Boolean>
8889
<s:Boolean x:Key="/Default/UserDictionary/Words/=Datemultirange/@EntryIndexedValue">True</s:Boolean>
8990
<s:Boolean x:Key="/Default/UserDictionary/Words/=daterange/@EntryIndexedValue">True</s:Boolean>
9091
<s:Boolean x:Key="/Default/UserDictionary/Words/=DDEX/@EntryIndexedValue">True</s:Boolean>

src/Npgsql/BackendMessages/RowDescriptionMessage.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal RowDescriptionMessage(int numFields = 10)
4242
_insensitiveIndex = new Dictionary<string, int>(source._insensitiveIndex);
4343
}
4444

45-
internal RowDescriptionMessage Load(NpgsqlReadBuffer buf, ConnectorTypeMapper typeMapper)
45+
internal RowDescriptionMessage Load(NpgsqlReadBuffer buf, TypeMapper typeMapper)
4646
{
4747
_nameIndex.Clear();
4848
_insensitiveIndex?.Clear();
@@ -77,7 +77,7 @@ internal RowDescriptionMessage Load(NpgsqlReadBuffer buf, ConnectorTypeMapper ty
7777
}
7878

7979
internal static RowDescriptionMessage CreateForReplication(
80-
ConnectorTypeMapper typeMapper, uint tableOID, FormatCode formatCode, IReadOnlyList<RelationMessage.Column> columns)
80+
TypeMapper typeMapper, uint tableOID, FormatCode formatCode, IReadOnlyList<RelationMessage.Column> columns)
8181
{
8282
var msg = new RowDescriptionMessage(columns.Count);
8383
var numFields = msg.Count = columns.Count;
@@ -238,7 +238,7 @@ internal FieldDescription(FieldDescription source)
238238
}
239239

240240
internal void Populate(
241-
ConnectorTypeMapper typeMapper, string name, uint tableOID, short columnAttributeNumber,
241+
TypeMapper typeMapper, string name, uint tableOID, short columnAttributeNumber,
242242
uint oid, short typeSize, int typeModifier, FormatCode formatCode
243243
)
244244
{
@@ -309,7 +309,7 @@ internal PostgresType PostgresType
309309
internal void ResolveHandler()
310310
=> Handler = IsBinaryFormat ? _typeMapper.ResolveByOID(TypeOID) : _typeMapper.UnrecognizedTypeHandler;
311311

312-
ConnectorTypeMapper _typeMapper;
312+
TypeMapper _typeMapper;
313313

314314
internal bool IsBinaryFormat => FormatCode == FormatCode.Binary;
315315
internal bool IsTextFormat => FormatCode == FormatCode.Text;

src/Npgsql/Internal/NpgsqlConnector.Auth.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ async Task AuthenticateGSS(bool async)
317317

318318
async ValueTask<string?> GetPassword(string username, bool async, CancellationToken cancellationToken = default)
319319
{
320-
var password = await _dataSource.GetPassword(async, cancellationToken);
320+
var password = await DataSource.GetPassword(async, cancellationToken);
321321

322322
if (password is not null)
323323
return password;

src/Npgsql/Internal/NpgsqlConnector.cs

Lines changed: 22 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public sealed partial class NpgsqlConnector : IDisposable
106106
/// </summary>
107107
public NpgsqlDatabaseInfo DatabaseInfo { get; internal set; } = default!;
108108

109-
internal ConnectorTypeMapper TypeMapper { get; set; } = default!;
109+
internal TypeMapper TypeMapper { get; set; } = default!;
110110

111111
/// <summary>
112112
/// The current transaction status for this connector.
@@ -161,11 +161,6 @@ public sealed partial class NpgsqlConnector : IDisposable
161161
/// </summary>
162162
volatile Exception? _breakReason;
163163

164-
/// <summary>
165-
/// Semaphore, used to synchronize DatabaseInfo between multiple connections, so it wouldn't be loaded in parallel.
166-
/// </summary>
167-
static readonly SemaphoreSlim DatabaseInfoSemaphore = new(1);
168-
169164
/// <summary>
170165
/// <para>
171166
/// Used by the pool to indicate that I/O is currently in progress on this connector, so that another write
@@ -228,9 +223,9 @@ internal void FlagAsWritableForMultiplexing()
228223
/// Note that in multi-host scenarios, this references the host-specific <see cref="PoolingDataSource"/> rather than the
229224
/// <see cref="NpgsqlMultiHostDataSource"/>.
230225
/// </summary>
231-
readonly NpgsqlDataSource _dataSource;
226+
internal NpgsqlDataSource DataSource { get; }
232227

233-
internal string UserFacingConnectionString => _dataSource.ConnectionString;
228+
internal string UserFacingConnectionString => DataSource.ConnectionString;
234229

235230
/// <summary>
236231
/// Contains the UTC timestamp when this connector was opened, used to implement
@@ -315,7 +310,7 @@ internal NpgsqlConnector(NpgsqlDataSource dataSource, NpgsqlConnection conn)
315310
}
316311

317312
NpgsqlConnector(NpgsqlConnector connector)
318-
: this(connector._dataSource)
313+
: this(connector.DataSource)
319314
{
320315
ProvideClientCertificatesCallback = connector.ProvideClientCertificatesCallback;
321316
UserCertificateValidationCallback = connector.UserCertificateValidationCallback;
@@ -326,7 +321,7 @@ internal NpgsqlConnector(NpgsqlDataSource dataSource, NpgsqlConnection conn)
326321
{
327322
Debug.Assert(dataSource.OwnsConnectors);
328323

329-
_dataSource = dataSource;
324+
DataSource = dataSource;
330325

331326
LoggingConfiguration = dataSource.LoggingConfiguration;
332327
ConnectionLogger = LoggingConfiguration.ConnectionLogger;
@@ -464,7 +459,12 @@ internal async Task Open(NpgsqlTimeout timeout, bool async, CancellationToken ca
464459
{
465460
await OpenCore(this, Settings.SslMode, timeout, async, cancellationToken);
466461

467-
await LoadDatabaseInfo(forceReload: false, timeout, async, cancellationToken);
462+
await DataSource.Bootstrap(this, timeout, forceReload: false, async, cancellationToken);
463+
464+
Debug.Assert(DataSource.TypeMapper is not null);
465+
Debug.Assert(DataSource.DatabaseInfo is not null);
466+
TypeMapper = DataSource.TypeMapper;
467+
DatabaseInfo = DataSource.DatabaseInfo;
468468

469469
if (Settings.Pooling && !Settings.Multiplexing && !Settings.NoResetOnClose && DatabaseInfo.SupportsDiscard)
470470
{
@@ -500,18 +500,18 @@ internal async Task Open(NpgsqlTimeout timeout, bool async, CancellationToken ca
500500
}
501501
}
502502

503-
if (_dataSource.ConnectionInitializerAsync is not null)
503+
if (DataSource.ConnectionInitializerAsync is not null)
504504
{
505-
Debug.Assert(_dataSource.ConnectionInitializer is not null);
505+
Debug.Assert(DataSource.ConnectionInitializer is not null);
506506

507-
var tempConnection = new NpgsqlConnection(_dataSource, this);
507+
var tempConnection = new NpgsqlConnection(DataSource, this);
508508

509509
try
510510
{
511511
if (async)
512-
await _dataSource.ConnectionInitializerAsync(tempConnection);
512+
await DataSource.ConnectionInitializerAsync(tempConnection);
513513
else if (!async)
514-
_dataSource.ConnectionInitializer(tempConnection);
514+
DataSource.ConnectionInitializer(tempConnection);
515515
}
516516
finally
517517
{
@@ -595,47 +595,6 @@ await OpenCore(
595595
}
596596
}
597597

598-
internal async ValueTask LoadDatabaseInfo(bool forceReload, NpgsqlTimeout timeout, bool async,
599-
CancellationToken cancellationToken = default)
600-
{
601-
// The type loading below will need to send queries to the database, and that depends on a type mapper being set up (even if its
602-
// empty). So we set up here, and then later inject the DatabaseInfo.
603-
// For multiplexing connectors, the type mapper is the shared pool-wide one (since when validating/binding parameters on
604-
// multiplexing there's no connector yet). However, in the very first multiplexing connection (bootstrap phase) we create
605-
// a connector-specific mapper, which will later become shared pool-wide one.
606-
TypeMapper =
607-
Settings.Multiplexing && ((MultiplexingDataSource)_dataSource).MultiplexingTypeMapper is { } multiplexingTypeMapper
608-
? multiplexingTypeMapper
609-
: new ConnectorTypeMapper(this);
610-
611-
var key = new NpgsqlDatabaseInfoCacheKey(Settings);
612-
if (forceReload || !NpgsqlDatabaseInfo.Cache.TryGetValue(key, out var database))
613-
{
614-
var hasSemaphore = async
615-
? await DatabaseInfoSemaphore.WaitAsync(timeout.CheckAndGetTimeLeft(), cancellationToken)
616-
: DatabaseInfoSemaphore.Wait(timeout.CheckAndGetTimeLeft(), cancellationToken);
617-
618-
// We've timed out - calling Check, to throw the correct exception
619-
if (!hasSemaphore)
620-
timeout.Check();
621-
622-
try
623-
{
624-
if (forceReload || !NpgsqlDatabaseInfo.Cache.TryGetValue(key, out database))
625-
{
626-
NpgsqlDatabaseInfo.Cache[key] = database = await NpgsqlDatabaseInfo.Load(this, timeout, async);
627-
}
628-
}
629-
finally
630-
{
631-
DatabaseInfoSemaphore.Release();
632-
}
633-
}
634-
635-
DatabaseInfo = database;
636-
TypeMapper.DatabaseInfo = database;
637-
}
638-
639598
internal async ValueTask<ClusterState> QueryClusterState(
640599
NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken = default)
641600
{
@@ -1177,7 +1136,7 @@ async Task MultiplexingReadLoop()
11771136
SpinWait.SpinUntil(() => MultiplexAsyncWritingLock == 0 || IsBroken);
11781137

11791138
ResetReadBuffer();
1180-
_dataSource.Return(this);
1139+
DataSource.Return(this);
11811140
}
11821141
}
11831142

@@ -1214,7 +1173,7 @@ async Task MultiplexingReadLoop()
12141173
}
12151174

12161175
// "Return" the connector to the pool to for cleanup (e.g. update total connector count)
1217-
_dataSource.Return(this);
1176+
DataSource.Return(this);
12181177

12191178
ConnectionLogger.LogError(e, "Exception in multiplexing read loop", Id);
12201179
}
@@ -1930,9 +1889,9 @@ internal void Close()
19301889
}
19311890

19321891
internal bool TryRemovePendingEnlistedConnector(Transaction transaction)
1933-
=> _dataSource.TryRemovePendingEnlistedConnector(this, transaction);
1892+
=> DataSource.TryRemovePendingEnlistedConnector(this, transaction);
19341893

1935-
internal void Return() => _dataSource.Return(this);
1894+
internal void Return() => DataSource.Return(this);
19361895

19371896
/// <inheritdoc />
19381897
public void Dispose() => Close();
@@ -1971,7 +1930,7 @@ internal Exception Break(Exception reason)
19711930
{
19721931
ClusterStateCache.UpdateClusterState(Host, Port, ClusterState.Offline, DateTime.UtcNow,
19731932
Settings.HostRecheckSecondsTranslated);
1974-
_dataSource.Clear();
1933+
DataSource.Clear();
19751934
}
19761935

19771936
LogMessages.BreakingConnection(ConnectionLogger, Id, reason);
@@ -2003,6 +1962,7 @@ internal Exception Break(Exception reason)
20031962
connection.Connector = null;
20041963
connection.ConnectorBindingScope = ConnectorBindingScope.None;
20051964
}
1965+
20061966
connection.FullState = ConnectionState.Broken;
20071967
connection.ReleaseCloseLock();
20081968
}

src/Npgsql/Internal/NpgsqlDatabaseInfo.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ public abstract class NpgsqlDatabaseInfo
1717
{
1818
#region Fields
1919

20-
internal static readonly ConcurrentDictionary<NpgsqlDatabaseInfoCacheKey, NpgsqlDatabaseInfo> Cache
21-
= new();
22-
2320
static volatile INpgsqlDatabaseInfoFactory[] Factories = new INpgsqlDatabaseInfoFactory[]
2421
{
2522
new PostgresMinimalDatabaseInfoFactory(),
@@ -185,7 +182,7 @@ private protected NpgsqlDatabaseInfo(string host, int port, string databaseName,
185182
public PostgresType GetPostgresTypeByName(string pgName)
186183
=> TryGetPostgresTypeByName(pgName, out var pgType)
187184
? pgType
188-
: throw new ArgumentException($"A PostgreSQL type with the name {pgName} was not found in the database");
185+
: throw new ArgumentException($"A PostgreSQL type with the name '{pgName}' was not found in the database");
189186

190187
public bool TryGetPostgresTypeByName(string pgName, [NotNullWhen(true)] out PostgresType? pgType)
191188
{
@@ -302,8 +299,6 @@ public static void RegisterFactory(INpgsqlDatabaseInfoFactory factory)
302299
factories[0] = factory;
303300
Array.Copy(Factories, 0, factories, 1, Factories.Length);
304301
Factories = factories;
305-
306-
Cache.Clear();
307302
}
308303

309304
internal static async Task<NpgsqlDatabaseInfo> Load(NpgsqlConnector conn, NpgsqlTimeout timeout, bool async)
@@ -324,14 +319,11 @@ internal static async Task<NpgsqlDatabaseInfo> Load(NpgsqlConnector conn, Npgsql
324319

325320
// For tests
326321
internal static void ResetFactories()
327-
{
328-
Factories = new INpgsqlDatabaseInfoFactory[]
322+
=> Factories = new INpgsqlDatabaseInfoFactory[]
329323
{
330324
new PostgresMinimalDatabaseInfoFactory(),
331325
new PostgresDatabaseInfoFactory()
332326
};
333-
Cache.Clear();
334-
}
335327

336328
#endregion Factory management
337329
}

src/Npgsql/Internal/TypeHandlers/CompositeHandlers/CompositeHandler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace Npgsql.Internal.TypeHandlers.CompositeHandlers;
4242

4343
sealed partial class CompositeHandler<T> : NpgsqlTypeHandler<T>, ICompositeHandler
4444
{
45-
readonly ConnectorTypeMapper _typeMapper;
45+
readonly TypeMapper _typeMapper;
4646
readonly INpgsqlNameTranslator _nameTranslator;
4747

4848
Func<T>? _constructor;
@@ -51,7 +51,7 @@ sealed partial class CompositeHandler<T> : NpgsqlTypeHandler<T>, ICompositeHandl
5151

5252
public Type CompositeType => typeof(T);
5353

54-
public CompositeHandler(PostgresCompositeType postgresType, ConnectorTypeMapper typeMapper, INpgsqlNameTranslator nameTranslator)
54+
public CompositeHandler(PostgresCompositeType postgresType, TypeMapper typeMapper, INpgsqlNameTranslator nameTranslator)
5555
: base(postgresType)
5656
{
5757
_typeMapper = typeMapper;
@@ -146,7 +146,7 @@ void InitializeCore()
146146
}
147147
}
148148

149-
static CompositeConstructorHandler<T>? CreateConstructorHandler(PostgresCompositeType pgType, ConnectorTypeMapper typeMapper, INpgsqlNameTranslator nameTranslator)
149+
static CompositeConstructorHandler<T>? CreateConstructorHandler(PostgresCompositeType pgType, TypeMapper typeMapper, INpgsqlNameTranslator nameTranslator)
150150
{
151151
var pgFields = pgType.Fields;
152152
var clrType = typeof(T);
@@ -222,7 +222,7 @@ void InitializeCore()
222222
return null;
223223
}
224224

225-
static CompositeMemberHandler<T>[] CreateMemberHandlers(PostgresCompositeType pgType, ConnectorTypeMapper typeMapper, INpgsqlNameTranslator nameTranslator)
225+
static CompositeMemberHandler<T>[] CreateMemberHandlers(PostgresCompositeType pgType, TypeMapper typeMapper, INpgsqlNameTranslator nameTranslator)
226226
{
227227
var pgFields = pgType.Fields;
228228

src/Npgsql/Internal/TypeHandlers/RecordHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ namespace Npgsql.Internal.TypeHandlers;
2424
/// </remarks>
2525
sealed partial class RecordHandler : NpgsqlTypeHandler<object[]>
2626
{
27-
readonly ConnectorTypeMapper _typeMapper;
27+
readonly TypeMapper _typeMapper;
2828

29-
public RecordHandler(PostgresType postgresType, ConnectorTypeMapper typeMapper)
29+
public RecordHandler(PostgresType postgresType, TypeMapper typeMapper)
3030
: base(postgresType)
3131
=> _typeMapper = typeMapper;
3232

0 commit comments

Comments
 (0)