using Npgsql.Internal;
using Npgsql.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
namespace Npgsql;
abstract class ConnectorSource : IDisposable
{
internal NpgsqlConnectionStringBuilder Settings { get; }
///
/// Contains the connection string returned to the user from
/// after the connection has been opened. Does not contain the password unless Persist Security Info=true.
///
internal string UserFacingConnectionString { get; }
// Note that while the dictionary is protected by locking, we assume that the lists it contains don't need to be
// (i.e. access to connectors of a specific transaction won't be concurrent)
protected readonly Dictionary> _pendingEnlistedConnectors
= new();
internal abstract (int Total, int Idle, int Busy) Statistics { get; }
internal ConnectorSource(NpgsqlConnectionStringBuilder settings, string connString)
{
Settings = settings;
UserFacingConnectionString = settings.PersistSecurityInfo
? connString
: settings.ToStringWithoutPassword();
}
internal abstract ValueTask Get(
NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken);
internal abstract bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector);
internal abstract ValueTask OpenNewConnector(
NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken);
internal abstract void Return(NpgsqlConnector connector);
internal abstract void Clear();
internal abstract bool OwnsConnectors { get; }
public virtual void Dispose() { }
#region Pending Enlisted Connections
internal virtual void AddPendingEnlistedConnector(NpgsqlConnector connector, Transaction transaction)
{
lock (_pendingEnlistedConnectors)
{
if (!_pendingEnlistedConnectors.TryGetValue(transaction, out var list))
list = _pendingEnlistedConnectors[transaction] = new List();
list.Add(connector);
}
}
internal virtual bool TryRemovePendingEnlistedConnector(NpgsqlConnector connector, Transaction transaction)
{
lock (_pendingEnlistedConnectors)
{
if (!_pendingEnlistedConnectors.TryGetValue(transaction, out var list))
return false;
list.Remove(connector);
if (list.Count == 0)
_pendingEnlistedConnectors.Remove(transaction);
return true;
}
}
internal virtual bool TryRentEnlistedPending(Transaction transaction, NpgsqlConnection connection,
[NotNullWhen(true)] out NpgsqlConnector? connector)
{
lock (_pendingEnlistedConnectors)
{
if (!_pendingEnlistedConnectors.TryGetValue(transaction, out var list))
{
connector = null;
return false;
}
connector = list[list.Count - 1];
list.RemoveAt(list.Count - 1);
if (list.Count == 0)
_pendingEnlistedConnectors.Remove(transaction);
return true;
}
}
#endregion
}