You've already forked linux-packaging-mono
Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
parent
0a9828183b
commit
7d7f676260
@@ -3,7 +3,7 @@
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ref\System.Data.Odbc.csproj">
|
||||
<SupportedFramework>net461;netcoreapp2.0;$(AllXamarinFrameworks)</SupportedFramework>
|
||||
<SupportedFramework>uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks)</SupportedFramework>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\src\System.Data.Odbc.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,165 +3,13 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Data.ProviderBase
|
||||
{
|
||||
internal abstract class DbConnectionClosed : DbConnectionInternal
|
||||
internal abstract partial class DbConnectionClosed : DbConnectionInternal
|
||||
{
|
||||
// Construct an "empty" connection
|
||||
protected DbConnectionClosed(ConnectionState state, bool hidePassword, bool allowSetConnectionString) : base(state, hidePassword, allowSetConnectionString)
|
||||
{
|
||||
}
|
||||
protected override void Activate() => throw ADP.ClosedConnectionError();
|
||||
|
||||
public override string ServerVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
throw ADP.ClosedConnectionError();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Activate()
|
||||
{
|
||||
throw ADP.ClosedConnectionError();
|
||||
}
|
||||
|
||||
public override DbTransaction BeginTransaction(IsolationLevel il)
|
||||
{
|
||||
throw ADP.ClosedConnectionError();
|
||||
}
|
||||
|
||||
public override void ChangeDatabase(string database)
|
||||
{
|
||||
throw ADP.ClosedConnectionError();
|
||||
}
|
||||
|
||||
internal override void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
|
||||
{
|
||||
// not much to do here...
|
||||
}
|
||||
|
||||
protected override void Deactivate()
|
||||
{
|
||||
throw ADP.ClosedConnectionError();
|
||||
}
|
||||
|
||||
|
||||
protected override DbReferenceCollection CreateReferenceCollection()
|
||||
{
|
||||
throw ADP.ClosedConnectionError();
|
||||
}
|
||||
|
||||
internal override bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
return base.TryOpenConnectionInternal(outerConnection, connectionFactory, retry, userOptions);
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class DbConnectionBusy : DbConnectionClosed
|
||||
{
|
||||
protected DbConnectionBusy(ConnectionState state) : base(state, true, false)
|
||||
{
|
||||
}
|
||||
|
||||
internal override bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
throw ADP.ConnectionAlreadyOpen(State);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DbConnectionClosedBusy : DbConnectionBusy
|
||||
{
|
||||
// Closed Connection, Currently Busy - changing connection string
|
||||
internal static readonly DbConnectionInternal SingletonInstance = new DbConnectionClosedBusy(); // singleton object
|
||||
|
||||
private DbConnectionClosedBusy() : base(ConnectionState.Closed)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DbConnectionOpenBusy : DbConnectionBusy
|
||||
{
|
||||
// Open Connection, Currently Busy - closing connection
|
||||
internal static readonly DbConnectionInternal SingletonInstance = new DbConnectionOpenBusy(); // singleton object
|
||||
|
||||
private DbConnectionOpenBusy() : base(ConnectionState.Open)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DbConnectionClosedConnecting : DbConnectionBusy
|
||||
{
|
||||
// Closed Connection, Currently Connecting
|
||||
|
||||
internal static readonly DbConnectionInternal SingletonInstance = new DbConnectionClosedConnecting(); // singleton object
|
||||
|
||||
private DbConnectionClosedConnecting() : base(ConnectionState.Connecting)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
|
||||
{
|
||||
connectionFactory.SetInnerConnectionTo(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
|
||||
}
|
||||
|
||||
internal override bool TryReplaceConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
return TryOpenConnection(outerConnection, connectionFactory, retry, userOptions);
|
||||
}
|
||||
|
||||
internal override bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
if (retry == null || !retry.Task.IsCompleted)
|
||||
{
|
||||
// retry is null if this is a synchronous call
|
||||
|
||||
// if someone calls Open or OpenAsync while in this state,
|
||||
// then the retry task will not be completed
|
||||
|
||||
throw ADP.ConnectionAlreadyOpen(State);
|
||||
}
|
||||
|
||||
// we are completing an asynchronous open
|
||||
Debug.Assert(retry.Task.Status == TaskStatus.RanToCompletion, "retry task must be completed successfully");
|
||||
DbConnectionInternal openConnection = retry.Task.Result;
|
||||
if (null == openConnection)
|
||||
{
|
||||
connectionFactory.SetInnerConnectionTo(outerConnection, this);
|
||||
throw ADP.InternalConnectionError(ADP.ConnectionError.GetConnectionReturnsNull);
|
||||
}
|
||||
connectionFactory.SetInnerConnectionEvent(outerConnection, openConnection);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DbConnectionClosedNeverOpened : DbConnectionClosed
|
||||
{
|
||||
// Closed Connection, Has Never Been Opened
|
||||
|
||||
internal static readonly DbConnectionInternal SingletonInstance = new DbConnectionClosedNeverOpened(); // singleton object
|
||||
|
||||
private DbConnectionClosedNeverOpened() : base(ConnectionState.Closed, false, true)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DbConnectionClosedPreviouslyOpened : DbConnectionClosed
|
||||
{
|
||||
// Closed Connection, Has Previously Been Opened
|
||||
|
||||
internal static readonly DbConnectionInternal SingletonInstance = new DbConnectionClosedPreviouslyOpened(); // singleton object
|
||||
|
||||
private DbConnectionClosedPreviouslyOpened() : base(ConnectionState.Closed, true, true)
|
||||
{
|
||||
}
|
||||
|
||||
internal override bool TryReplaceConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
return TryOpenConnection(outerConnection, connectionFactory, retry, userOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Common;
|
||||
using System.Threading;
|
||||
@@ -10,143 +9,8 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace System.Data.ProviderBase
|
||||
{
|
||||
internal abstract class DbConnectionFactory
|
||||
internal abstract partial class DbConnectionFactory
|
||||
{
|
||||
private Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> _connectionPoolGroups;
|
||||
private readonly List<DbConnectionPool> _poolsToRelease;
|
||||
private readonly List<DbConnectionPoolGroup> _poolGroupsToRelease;
|
||||
private readonly Timer _pruningTimer;
|
||||
|
||||
private const int PruningDueTime = 4 * 60 * 1000; // 4 minutes
|
||||
private const int PruningPeriod = 30 * 1000; // thirty seconds
|
||||
|
||||
|
||||
// s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to
|
||||
// a maximum of Environment.ProcessorCount at a time.
|
||||
private static uint s_pendingOpenNonPooledNext = 0;
|
||||
private static Task<DbConnectionInternal>[] s_pendingOpenNonPooled = new Task<DbConnectionInternal>[Environment.ProcessorCount];
|
||||
private static Task<DbConnectionInternal> s_completedTask;
|
||||
|
||||
protected DbConnectionFactory()
|
||||
{
|
||||
_connectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>();
|
||||
_poolsToRelease = new List<DbConnectionPool>();
|
||||
_poolGroupsToRelease = new List<DbConnectionPoolGroup>();
|
||||
_pruningTimer = CreatePruningTimer();
|
||||
}
|
||||
|
||||
|
||||
public abstract DbProviderFactory ProviderFactory
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
public void ClearAllPools()
|
||||
{
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
|
||||
foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups)
|
||||
{
|
||||
DbConnectionPoolGroup poolGroup = entry.Value;
|
||||
if (null != poolGroup)
|
||||
{
|
||||
poolGroup.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearPool(DbConnection connection)
|
||||
{
|
||||
ADP.CheckArgumentNull(connection, nameof(connection));
|
||||
|
||||
DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection);
|
||||
if (null != poolGroup)
|
||||
{
|
||||
poolGroup.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearPool(DbConnectionPoolKey key)
|
||||
{
|
||||
Debug.Assert(key != null, "key cannot be null");
|
||||
ADP.CheckArgumentNull(key.ConnectionString, nameof(key) + "." + nameof(key.ConnectionString));
|
||||
|
||||
DbConnectionPoolGroup poolGroup;
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
|
||||
if (connectionPoolGroups.TryGetValue(key, out poolGroup))
|
||||
{
|
||||
poolGroup.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions)
|
||||
{
|
||||
Debug.Assert(null != owningConnection, "null owningConnection?");
|
||||
Debug.Assert(null != poolGroup, "null poolGroup?");
|
||||
|
||||
DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions;
|
||||
DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo;
|
||||
DbConnectionPoolKey poolKey = poolGroup.PoolKey;
|
||||
|
||||
DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolKey, poolGroupProviderInfo, null, owningConnection, userOptions);
|
||||
if (null != newConnection)
|
||||
{
|
||||
newConnection.MakeNonPooledObject(owningConnection);
|
||||
}
|
||||
return newConnection;
|
||||
}
|
||||
|
||||
internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
|
||||
{
|
||||
Debug.Assert(null != pool, "null pool?");
|
||||
DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo;
|
||||
|
||||
DbConnectionInternal newConnection = CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningObject, userOptions);
|
||||
if (null != newConnection)
|
||||
{
|
||||
newConnection.MakePooledConnection(pool);
|
||||
}
|
||||
return newConnection;
|
||||
}
|
||||
|
||||
internal virtual DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo(DbConnectionOptions connectionOptions)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private Timer CreatePruningTimer()
|
||||
{
|
||||
TimerCallback callback = new TimerCallback(PruneConnectionPoolGroups);
|
||||
return new Timer(callback, null, PruningDueTime, PruningPeriod);
|
||||
}
|
||||
|
||||
protected DbConnectionOptions FindConnectionOptions(DbConnectionPoolKey key)
|
||||
{
|
||||
Debug.Assert(key != null, "key cannot be null");
|
||||
if (!string.IsNullOrEmpty(key.ConnectionString))
|
||||
{
|
||||
DbConnectionPoolGroup connectionPoolGroup;
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
|
||||
if (connectionPoolGroups.TryGetValue(key, out connectionPoolGroup))
|
||||
{
|
||||
return connectionPoolGroup.ConnectionOptions;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Task<DbConnectionInternal> GetCompletedTask()
|
||||
{
|
||||
Debug.Assert(Monitor.IsEntered(s_pendingOpenNonPooled), $"Expected {nameof(s_pendingOpenNonPooled)} lock to be held.");
|
||||
return s_completedTask ?? (s_completedTask = Task.FromResult<DbConnectionInternal>(null));
|
||||
}
|
||||
|
||||
internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, out DbConnectionInternal connection)
|
||||
{
|
||||
Debug.Assert(null != owningConnection, "null owningConnection?");
|
||||
@@ -309,254 +173,6 @@ namespace System.Data.ProviderBase
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup)
|
||||
{
|
||||
// if poolgroup is disabled, it will be replaced with a new entry
|
||||
|
||||
Debug.Assert(null != owningObject, "null owningObject?");
|
||||
Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
|
||||
|
||||
// It is possible that while the outer connection object has
|
||||
// been sitting around in a closed and unused state in some long
|
||||
// running app, the pruner may have come along and remove this
|
||||
// the pool entry from the master list. If we were to use a
|
||||
// pool entry in this state, we would create "unmanaged" pools,
|
||||
// which would be bad. To avoid this problem, we automagically
|
||||
// re-create the pool entry whenever it's disabled.
|
||||
|
||||
// however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work
|
||||
if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions))
|
||||
{
|
||||
// reusing existing pool option in case user originally used SetConnectionPoolOptions
|
||||
DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions;
|
||||
|
||||
// get the string to hash on again
|
||||
DbConnectionOptions connectionOptions = connectionPoolGroup.ConnectionOptions;
|
||||
Debug.Assert(null != connectionOptions, "prevent expansion of connectionString");
|
||||
|
||||
connectionPoolGroup = GetConnectionPoolGroup(connectionPoolGroup.PoolKey, poolOptions, ref connectionOptions);
|
||||
Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
|
||||
SetConnectionPoolGroup(owningObject, connectionPoolGroup);
|
||||
}
|
||||
DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this);
|
||||
return connectionPool;
|
||||
}
|
||||
|
||||
internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key.ConnectionString))
|
||||
{
|
||||
return (DbConnectionPoolGroup)null;
|
||||
}
|
||||
|
||||
DbConnectionPoolGroup connectionPoolGroup;
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
|
||||
if (!connectionPoolGroups.TryGetValue(key, out connectionPoolGroup) || (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)))
|
||||
{
|
||||
// If we can't find an entry for the connection string in
|
||||
// our collection of pool entries, then we need to create a
|
||||
// new pool entry and add it to our collection.
|
||||
|
||||
DbConnectionOptions connectionOptions = CreateConnectionOptions(key.ConnectionString, userConnectionOptions);
|
||||
if (null == connectionOptions)
|
||||
{
|
||||
throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing);
|
||||
}
|
||||
|
||||
if (null == userConnectionOptions)
|
||||
{ // we only allow one expansion on the connection string
|
||||
userConnectionOptions = connectionOptions;
|
||||
}
|
||||
|
||||
// We don't support connection pooling on Win9x
|
||||
if (null == poolOptions)
|
||||
{
|
||||
if (null != connectionPoolGroup)
|
||||
{
|
||||
// reusing existing pool option in case user originally used SetConnectionPoolOptions
|
||||
poolOptions = connectionPoolGroup.PoolGroupOptions;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: may return null for non-pooled connections
|
||||
poolOptions = CreateConnectionPoolGroupOptions(connectionOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DbConnectionPoolGroup newConnectionPoolGroup = new DbConnectionPoolGroup(connectionOptions, key, poolOptions);
|
||||
newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions);
|
||||
|
||||
lock (this)
|
||||
{
|
||||
connectionPoolGroups = _connectionPoolGroups;
|
||||
if (!connectionPoolGroups.TryGetValue(key, out connectionPoolGroup))
|
||||
{
|
||||
// build new dictionary with space for new connection string
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> newConnectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>(1 + connectionPoolGroups.Count);
|
||||
foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups)
|
||||
{
|
||||
newConnectionPoolGroups.Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
// lock prevents race condition with PruneConnectionPoolGroups
|
||||
newConnectionPoolGroups.Add(key, newConnectionPoolGroup);
|
||||
connectionPoolGroup = newConnectionPoolGroup;
|
||||
_connectionPoolGroups = newConnectionPoolGroups;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(!connectionPoolGroup.IsDisabled, "Disabled pool entry discovered");
|
||||
}
|
||||
}
|
||||
Debug.Assert(null != connectionPoolGroup, "how did we not create a pool entry?");
|
||||
Debug.Assert(null != userConnectionOptions, "how did we not have user connection options?");
|
||||
}
|
||||
else if (null == userConnectionOptions)
|
||||
{
|
||||
userConnectionOptions = connectionPoolGroup.ConnectionOptions;
|
||||
}
|
||||
return connectionPoolGroup;
|
||||
}
|
||||
|
||||
|
||||
private void PruneConnectionPoolGroups(object state)
|
||||
{
|
||||
// First, walk the pool release list and attempt to clear each
|
||||
// pool, when the pool is finally empty, we dispose of it. If the
|
||||
// pool isn't empty, it's because there are active connections or
|
||||
// distributed transactions that need it.
|
||||
lock (_poolsToRelease)
|
||||
{
|
||||
if (0 != _poolsToRelease.Count)
|
||||
{
|
||||
DbConnectionPool[] poolsToRelease = _poolsToRelease.ToArray();
|
||||
foreach (DbConnectionPool pool in poolsToRelease)
|
||||
{
|
||||
if (null != pool)
|
||||
{
|
||||
pool.Clear();
|
||||
|
||||
if (0 == pool.Count)
|
||||
{
|
||||
_poolsToRelease.Remove(pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, walk the pool entry release list and dispose of each
|
||||
// pool entry when it is finally empty. If the pool entry isn't
|
||||
// empty, it's because there are active pools that need it.
|
||||
lock (_poolGroupsToRelease)
|
||||
{
|
||||
if (0 != _poolGroupsToRelease.Count)
|
||||
{
|
||||
DbConnectionPoolGroup[] poolGroupsToRelease = _poolGroupsToRelease.ToArray();
|
||||
foreach (DbConnectionPoolGroup poolGroup in poolGroupsToRelease)
|
||||
{
|
||||
if (null != poolGroup)
|
||||
{
|
||||
int poolsLeft = poolGroup.Clear(); // may add entries to _poolsToRelease
|
||||
|
||||
if (0 == poolsLeft)
|
||||
{
|
||||
_poolGroupsToRelease.Remove(poolGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we walk through the collection of connection pool entries
|
||||
// and prune each one. This will cause any empty pools to be put
|
||||
// into the release list.
|
||||
lock (this)
|
||||
{
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
|
||||
Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> newConnectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>(connectionPoolGroups.Count);
|
||||
|
||||
foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups)
|
||||
{
|
||||
if (null != entry.Value)
|
||||
{
|
||||
Debug.Assert(!entry.Value.IsDisabled, "Disabled pool entry discovered");
|
||||
|
||||
// entries start active and go idle during prune if all pools are gone
|
||||
// move idle entries from last prune pass to a queue for pending release
|
||||
// otherwise process entry which may move it from active to idle
|
||||
if (entry.Value.Prune())
|
||||
{ // may add entries to _poolsToRelease
|
||||
QueuePoolGroupForRelease(entry.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
newConnectionPoolGroups.Add(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
_connectionPoolGroups = newConnectionPoolGroups;
|
||||
}
|
||||
}
|
||||
|
||||
internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing)
|
||||
{
|
||||
// Queue the pool up for release -- we'll clear it out and dispose
|
||||
// of it as the last part of the pruning timer callback so we don't
|
||||
// do it with the pool entry or the pool collection locked.
|
||||
Debug.Assert(null != pool, "null pool?");
|
||||
|
||||
// set the pool to the shutdown state to force all active
|
||||
// connections to be automatically disposed when they
|
||||
// are returned to the pool
|
||||
pool.Shutdown();
|
||||
|
||||
lock (_poolsToRelease)
|
||||
{
|
||||
if (clearing)
|
||||
{
|
||||
pool.Clear();
|
||||
}
|
||||
_poolsToRelease.Add(pool);
|
||||
}
|
||||
}
|
||||
|
||||
internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup)
|
||||
{
|
||||
Debug.Assert(null != poolGroup, "null poolGroup?");
|
||||
|
||||
lock (_poolGroupsToRelease)
|
||||
{
|
||||
_poolGroupsToRelease.Add(poolGroup);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
|
||||
{
|
||||
return CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningConnection);
|
||||
}
|
||||
|
||||
protected abstract DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection);
|
||||
|
||||
protected abstract DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous);
|
||||
|
||||
protected abstract DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options);
|
||||
|
||||
internal abstract DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection);
|
||||
|
||||
internal abstract DbConnectionInternal GetInnerConnection(DbConnection connection);
|
||||
|
||||
|
||||
internal abstract void PermissionDemand(DbConnection outerConnection);
|
||||
|
||||
internal abstract void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup);
|
||||
|
||||
internal abstract void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to);
|
||||
|
||||
internal abstract bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from);
|
||||
|
||||
internal abstract void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,168 +9,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace System.Data.ProviderBase
|
||||
{
|
||||
internal abstract class DbConnectionInternal
|
||||
internal abstract partial class DbConnectionInternal
|
||||
{
|
||||
internal static readonly StateChangeEventArgs StateChangeClosed = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
|
||||
internal static readonly StateChangeEventArgs StateChangeOpen = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
|
||||
|
||||
private readonly bool _allowSetConnectionString;
|
||||
private readonly bool _hidePassword;
|
||||
private readonly ConnectionState _state;
|
||||
|
||||
private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections)
|
||||
|
||||
private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only)
|
||||
private DbReferenceCollection _referenceCollection; // collection of objects that we need to notify in some way when we're being deactivated
|
||||
private int _pooledCount; // [usage must be thread safe] the number of times this object has been pushed into the pool less the number of times it's been popped (0 != inPool)
|
||||
|
||||
private bool _connectionIsDoomed; // true when the connection should no longer be used.
|
||||
private bool _cannotBePooled; // true when the connection should no longer be pooled.
|
||||
|
||||
private DateTime _createTime; // when the connection was created.
|
||||
|
||||
|
||||
#if DEBUG
|
||||
private int _activateCount; // debug only counter to verify activate/deactivates are in sync.
|
||||
#endif //DEBUG
|
||||
|
||||
protected DbConnectionInternal() : this(ConnectionState.Open, true, false)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor for internal connections
|
||||
internal DbConnectionInternal(ConnectionState state, bool hidePassword, bool allowSetConnectionString)
|
||||
{
|
||||
_allowSetConnectionString = allowSetConnectionString;
|
||||
_hidePassword = hidePassword;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
internal bool AllowSetConnectionString
|
||||
{
|
||||
get
|
||||
{
|
||||
return _allowSetConnectionString;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool CanBePooled
|
||||
{
|
||||
get
|
||||
{
|
||||
bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive);
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal bool IsConnectionDoomed
|
||||
{
|
||||
get
|
||||
{
|
||||
return _connectionIsDoomed;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsEmancipated
|
||||
{
|
||||
get
|
||||
{
|
||||
// NOTE: There are race conditions between PrePush, PostPop and this
|
||||
// property getter -- only use this while this object is locked;
|
||||
// (DbConnectionPool.Clear and ReclaimEmancipatedObjects
|
||||
// do this for us)
|
||||
|
||||
// The functionality is as follows:
|
||||
//
|
||||
// _pooledCount is incremented when the connection is pushed into the pool
|
||||
// _pooledCount is decremented when the connection is popped from the pool
|
||||
// _pooledCount is set to -1 when the connection is not pooled (just in case...)
|
||||
//
|
||||
// That means that:
|
||||
//
|
||||
// _pooledCount > 1 connection is in the pool multiple times (This should not happen)
|
||||
// _pooledCount == 1 connection is in the pool
|
||||
// _pooledCount == 0 connection is out of the pool
|
||||
// _pooledCount == -1 connection is not a pooled connection; we shouldn't be here for non-pooled connections.
|
||||
// _pooledCount < -1 connection out of the pool multiple times
|
||||
//
|
||||
// Now, our job is to return TRUE when the connection is out
|
||||
// of the pool and it's owning object is no longer around to
|
||||
// return it.
|
||||
|
||||
bool value = (_pooledCount < 1) && !_owningObject.IsAlive;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsInPool
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(_pooledCount <= 1 && _pooledCount >= -1, "Pooled count for object is invalid");
|
||||
return (_pooledCount == 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal object Owner
|
||||
{
|
||||
// We use a weak reference to the owning object so we can identify when
|
||||
// it has been garbage collected without thowing exceptions.
|
||||
get
|
||||
{
|
||||
return _owningObject.Target;
|
||||
}
|
||||
}
|
||||
|
||||
internal DbConnectionPool Pool
|
||||
{
|
||||
get
|
||||
{
|
||||
return _connectionPool;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal DbReferenceCollection ReferenceCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
return _referenceCollection;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract string ServerVersion
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
// this should be abstract but until it is added to all the providers virtual will have to do
|
||||
public virtual string ServerVersionNormalized
|
||||
{
|
||||
get
|
||||
{
|
||||
throw ADP.NotSupported();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldHidePassword
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hidePassword;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionState State
|
||||
{
|
||||
get
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract void Activate();
|
||||
|
||||
internal void ActivateConnection()
|
||||
@@ -187,26 +28,6 @@ namespace System.Data.ProviderBase
|
||||
Activate();
|
||||
}
|
||||
|
||||
internal void AddWeakReference(object value, int tag)
|
||||
{
|
||||
if (null == _referenceCollection)
|
||||
{
|
||||
_referenceCollection = CreateReferenceCollection();
|
||||
if (null == _referenceCollection)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.CreateReferenceCollectionReturnedNull);
|
||||
}
|
||||
}
|
||||
_referenceCollection.Add(value, tag);
|
||||
}
|
||||
|
||||
public abstract DbTransaction BeginTransaction(IsolationLevel il);
|
||||
|
||||
public virtual void ChangeDatabase(string value)
|
||||
{
|
||||
throw ADP.MethodNotImplemented();
|
||||
}
|
||||
|
||||
internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
|
||||
{
|
||||
// The implementation here is the implementation required for the
|
||||
@@ -304,245 +125,10 @@ namespace System.Data.ProviderBase
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void PrepareForReplaceConnection()
|
||||
{
|
||||
// By default, there is no preparation required
|
||||
}
|
||||
|
||||
protected virtual void PrepareForCloseConnection()
|
||||
{
|
||||
// By default, there is no preparation required
|
||||
}
|
||||
|
||||
protected virtual object ObtainAdditionalLocksForClose()
|
||||
{
|
||||
return null; // no additional locks in default implementation
|
||||
}
|
||||
|
||||
protected virtual void ReleaseAdditionalLocksForClose(object lockToken)
|
||||
{
|
||||
// no additional locks in default implementation
|
||||
}
|
||||
|
||||
protected virtual DbReferenceCollection CreateReferenceCollection()
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToConstructReferenceCollectionOnStaticObject);
|
||||
}
|
||||
|
||||
protected abstract void Deactivate();
|
||||
|
||||
internal void DeactivateConnection()
|
||||
{
|
||||
// Internal method called from the connection pooler so we don't expose
|
||||
// the Deactivate method publicly.
|
||||
|
||||
#if DEBUG
|
||||
int activateCount = Interlocked.Decrement(ref _activateCount);
|
||||
Debug.Assert(0 == activateCount, "activated multiple times?");
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
if (!_connectionIsDoomed && Pool.UseLoadBalancing)
|
||||
{
|
||||
// If we're not already doomed, check the connection's lifetime and
|
||||
// doom it if it's lifetime has elapsed.
|
||||
|
||||
DateTime now = DateTime.UtcNow;
|
||||
if ((now.Ticks - _createTime.Ticks) > Pool.LoadBalanceTimeout.Ticks)
|
||||
{
|
||||
DoNotPoolThisConnection();
|
||||
}
|
||||
}
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_connectionPool = null;
|
||||
_connectionIsDoomed = true;
|
||||
}
|
||||
|
||||
protected internal void DoNotPoolThisConnection()
|
||||
{
|
||||
_cannotBePooled = true;
|
||||
}
|
||||
|
||||
/// <devdoc>Ensure that this connection cannot be put back into the pool.</devdoc>
|
||||
protected internal void DoomThisConnection()
|
||||
{
|
||||
_connectionIsDoomed = true;
|
||||
}
|
||||
|
||||
|
||||
internal void MakeNonPooledObject(object owningObject)
|
||||
{
|
||||
// Used by DbConnectionFactory to indicate that this object IS NOT part of
|
||||
// a connection pool.
|
||||
|
||||
_connectionPool = null;
|
||||
_owningObject.Target = owningObject;
|
||||
_pooledCount = -1;
|
||||
}
|
||||
|
||||
internal void MakePooledConnection(DbConnectionPool connectionPool)
|
||||
{
|
||||
// Used by DbConnectionFactory to indicate that this object IS part of
|
||||
// a connection pool.
|
||||
_createTime = DateTime.UtcNow;
|
||||
|
||||
_connectionPool = connectionPool;
|
||||
}
|
||||
|
||||
internal void NotifyWeakReference(int message)
|
||||
{
|
||||
DbReferenceCollection referenceCollection = ReferenceCollection;
|
||||
if (null != referenceCollection)
|
||||
{
|
||||
referenceCollection.Notify(message);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
|
||||
{
|
||||
if (!TryOpenConnection(outerConnection, connectionFactory, null, null))
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
|
||||
}
|
||||
}
|
||||
|
||||
/// <devdoc>The default implementation is for the open connection objects, and
|
||||
/// it simply throws. Our private closed-state connection objects
|
||||
/// override this and do the correct thing.</devdoc>
|
||||
// User code should either override DbConnectionInternal.Activate when it comes out of the pool
|
||||
// or override DbConnectionFactory.CreateConnection when the connection is created for non-pooled connections
|
||||
internal virtual bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
throw ADP.ConnectionAlreadyOpen(State);
|
||||
}
|
||||
|
||||
internal virtual bool TryReplaceConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
throw ADP.MethodNotImplemented();
|
||||
}
|
||||
|
||||
protected bool TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions)
|
||||
{
|
||||
// ?->Connecting: prevent set_ConnectionString during Open
|
||||
if (connectionFactory.SetInnerConnectionFrom(outerConnection, DbConnectionClosedConnecting.SingletonInstance, this))
|
||||
{
|
||||
DbConnectionInternal openConnection = null;
|
||||
try
|
||||
{
|
||||
connectionFactory.PermissionDemand(outerConnection);
|
||||
if (!connectionFactory.TryGetConnection(outerConnection, retry, userOptions, this, out openConnection))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This should occur for all exceptions, even ADP.UnCatchableExceptions.
|
||||
connectionFactory.SetInnerConnectionTo(outerConnection, this);
|
||||
throw;
|
||||
}
|
||||
if (null == openConnection)
|
||||
{
|
||||
connectionFactory.SetInnerConnectionTo(outerConnection, this);
|
||||
throw ADP.InternalConnectionError(ADP.ConnectionError.GetConnectionReturnsNull);
|
||||
}
|
||||
connectionFactory.SetInnerConnectionEvent(outerConnection, openConnection);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void PrePush(object expectedOwner)
|
||||
{
|
||||
// Called by DbConnectionPool when we're about to be put into it's pool, we
|
||||
// take this opportunity to ensure ownership and pool counts are legit.
|
||||
|
||||
// IMPORTANT NOTE: You must have taken a lock on the object before
|
||||
// you call this method to prevent race conditions with Clear and
|
||||
// ReclaimEmancipatedObjects.
|
||||
|
||||
//3 // The following tests are retail assertions of things we can't allow to happen.
|
||||
if (null == expectedOwner)
|
||||
{
|
||||
if (null != _owningObject.Target)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner
|
||||
}
|
||||
}
|
||||
else if (_owningObject.Target != expectedOwner)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner
|
||||
}
|
||||
if (0 != _pooledCount)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.PushingObjectSecondTime); // pushing object onto stack a second time
|
||||
}
|
||||
_pooledCount++;
|
||||
_owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2%
|
||||
}
|
||||
|
||||
internal void PostPop(object newOwner)
|
||||
{
|
||||
// Called by DbConnectionPool right after it pulls this from it's pool, we
|
||||
// take this opportunity to ensure ownership and pool counts are legit.
|
||||
|
||||
Debug.Assert(!IsEmancipated, "pooled object not in pool");
|
||||
|
||||
// When another thread is clearing this pool, it
|
||||
// will doom all connections in this pool without prejudice which
|
||||
// causes the following assert to fire, which really mucks up stress
|
||||
// against checked bits. The assert is benign, so we're commenting
|
||||
// it out.
|
||||
//Debug.Assert(CanBePooled, "pooled object is not poolable");
|
||||
|
||||
// IMPORTANT NOTE: You must have taken a lock on the object before
|
||||
// you call this method to prevent race conditions with Clear and
|
||||
// ReclaimEmancipatedObjects.
|
||||
|
||||
if (null != _owningObject.Target)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner!
|
||||
}
|
||||
_owningObject.Target = newOwner;
|
||||
_pooledCount--;
|
||||
//3 // The following tests are retail assertions of things we can't allow to happen.
|
||||
if (null != Pool)
|
||||
{
|
||||
if (0 != _pooledCount)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectInPoolMoreThanOnce); // popping object off stack with multiple pooledCount
|
||||
}
|
||||
}
|
||||
else if (-1 != _pooledCount)
|
||||
{
|
||||
throw ADP.InternalError(ADP.InternalErrorCode.NonPooledObjectUsedMoreThanOnce); // popping object off stack with multiple pooledCount
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveWeakReference(object value)
|
||||
{
|
||||
DbReferenceCollection referenceCollection = ReferenceCollection;
|
||||
if (null != referenceCollection)
|
||||
{
|
||||
referenceCollection.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, will check if the underlying connection is still actually alive
|
||||
/// </summary>
|
||||
/// <param name="throwOnException">If true an exception will be thrown if the connection is dead instead of returning true\false
|
||||
/// (this allows the caller to have the real reason that the connection is not alive (e.g. network error, etc))</param>
|
||||
/// <returns>True if the connection is still alive, otherwise false (If not overridden, then always true)</returns>
|
||||
internal virtual bool IsConnectionAlive(bool throwOnException = false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,9 +382,11 @@ namespace System.Data.ProviderBase
|
||||
}
|
||||
|
||||
private Timer CreateCleanupTimer()
|
||||
{
|
||||
return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait));
|
||||
}
|
||||
=> ADP.UnsafeCreateTimer(
|
||||
new TimerCallback(CleanupCallback),
|
||||
null,
|
||||
_cleanupWait,
|
||||
_cleanupWait);
|
||||
|
||||
private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
|
||||
{
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.ProviderBase
|
||||
{
|
||||
// set_ConnectionString calls DbConnectionFactory.GetConnectionPoolGroup
|
||||
// when not found a new pool entry is created and potentially added
|
||||
// DbConnectionPoolGroup starts in the Active state
|
||||
|
||||
// Open calls DbConnectionFactory.GetConnectionPool
|
||||
// if the existing pool entry is Disabled, GetConnectionPoolGroup is called for a new entry
|
||||
// DbConnectionFactory.GetConnectionPool calls DbConnectionPoolGroup.GetConnectionPool
|
||||
|
||||
// DbConnectionPoolGroup.GetConnectionPool will return pool for the current identity
|
||||
// or null if identity is restricted or pooling is disabled or state is disabled at time of add
|
||||
// state changes are Active->Active, Idle->Active
|
||||
|
||||
// DbConnectionFactory.PruneConnectionPoolGroups calls Prune
|
||||
// which will QueuePoolForRelease on all empty pools
|
||||
// and once no pools remain, change state from Active->Idle->Disabled
|
||||
// Once Disabled, factory can remove its reference to the pool entry
|
||||
|
||||
internal sealed class DbConnectionPoolGroup
|
||||
{
|
||||
private readonly DbConnectionOptions _connectionOptions;
|
||||
private readonly DbConnectionPoolKey _poolKey;
|
||||
private readonly DbConnectionPoolGroupOptions _poolGroupOptions;
|
||||
private ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool> _poolCollection;
|
||||
|
||||
private int _state; // see PoolGroupState* below
|
||||
|
||||
private DbConnectionPoolGroupProviderInfo _providerInfo;
|
||||
|
||||
// always lock this before changing _state, we don't want to move out of the 'Disabled' state
|
||||
// PoolGroupStateUninitialized = 0;
|
||||
private const int PoolGroupStateActive = 1; // initial state, GetPoolGroup from cache, connection Open
|
||||
private const int PoolGroupStateIdle = 2; // all pools are pruned via Clear
|
||||
private const int PoolGroupStateDisabled = 4; // factory pool entry pruning method
|
||||
|
||||
internal DbConnectionPoolGroup(DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions)
|
||||
{
|
||||
Debug.Assert(null != connectionOptions, "null connection options");
|
||||
|
||||
_connectionOptions = connectionOptions;
|
||||
_poolKey = key;
|
||||
_poolGroupOptions = poolGroupOptions;
|
||||
|
||||
// always lock this object before changing state
|
||||
// HybridDictionary does not create any sub-objects until add
|
||||
// so it is safe to use for non-pooled connection as long as
|
||||
// we check _poolGroupOptions first
|
||||
_poolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
|
||||
_state = PoolGroupStateActive;
|
||||
}
|
||||
|
||||
internal DbConnectionOptions ConnectionOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return _connectionOptions;
|
||||
}
|
||||
}
|
||||
|
||||
internal DbConnectionPoolKey PoolKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return _poolKey;
|
||||
}
|
||||
}
|
||||
|
||||
internal DbConnectionPoolGroupProviderInfo ProviderInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return _providerInfo;
|
||||
}
|
||||
set
|
||||
{
|
||||
_providerInfo = value;
|
||||
if (null != value)
|
||||
{
|
||||
_providerInfo.PoolGroup = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return (PoolGroupStateDisabled == _state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal DbConnectionPoolGroupOptions PoolGroupOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return _poolGroupOptions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal int Clear()
|
||||
{
|
||||
// must be multi-thread safe with competing calls by Clear and Prune via background thread
|
||||
// will return the number of connections in the group after clearing has finished
|
||||
|
||||
// First, note the old collection and create a new collection to be used
|
||||
ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool> oldPoolCollection = null;
|
||||
lock (this)
|
||||
{
|
||||
if (_poolCollection.Count > 0)
|
||||
{
|
||||
oldPoolCollection = _poolCollection;
|
||||
_poolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
|
||||
}
|
||||
}
|
||||
|
||||
// Then, if a new collection was created, release the pools from the old collection
|
||||
if (oldPoolCollection != null)
|
||||
{
|
||||
foreach (var entry in oldPoolCollection)
|
||||
{
|
||||
DbConnectionPool pool = entry.Value;
|
||||
if (pool != null)
|
||||
{
|
||||
DbConnectionFactory connectionFactory = pool.ConnectionFactory;
|
||||
connectionFactory.QueuePoolForRelease(pool, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, return the pool collection count - this may be non-zero if something was added while we were clearing
|
||||
return _poolCollection.Count;
|
||||
}
|
||||
|
||||
internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactory)
|
||||
{
|
||||
// When this method returns null it indicates that the connection
|
||||
// factory should not use pooling.
|
||||
|
||||
// We don't support connection pooling on Win9x;
|
||||
// PoolGroupOptions will only be null when we're not supposed to pool
|
||||
// connections.
|
||||
DbConnectionPool pool = null;
|
||||
if (null != _poolGroupOptions)
|
||||
{
|
||||
DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity;
|
||||
|
||||
if (_poolGroupOptions.PoolByIdentity)
|
||||
{
|
||||
// if we're pooling by identity (because integrated security is
|
||||
// being used for these connections) then we need to go out and
|
||||
// search for the connectionPool that matches the current identity.
|
||||
|
||||
currentIdentity = DbConnectionPoolIdentity.GetCurrent();
|
||||
|
||||
// If the current token is restricted in some way, then we must
|
||||
// not attempt to pool these connections.
|
||||
if (currentIdentity.IsRestricted)
|
||||
{
|
||||
currentIdentity = null;
|
||||
}
|
||||
}
|
||||
if (null != currentIdentity)
|
||||
{
|
||||
if (!_poolCollection.TryGetValue(currentIdentity, out pool))
|
||||
{ // find the pool
|
||||
DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions);
|
||||
|
||||
// optimistically create pool, but its callbacks are delayed until after actual add
|
||||
DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo);
|
||||
|
||||
lock (this)
|
||||
{
|
||||
// Did someone already add it to the list?
|
||||
if (!_poolCollection.TryGetValue(currentIdentity, out pool))
|
||||
{
|
||||
if (MarkPoolGroupAsActive())
|
||||
{
|
||||
// If we get here, we know for certain that we there isn't
|
||||
// a pool that matches the current identity, so we have to
|
||||
// add the optimistically created one
|
||||
newPool.Startup(); // must start pool before usage
|
||||
bool addResult = _poolCollection.TryAdd(currentIdentity, newPool);
|
||||
Debug.Assert(addResult, "No other pool with current identity should exist at this point");
|
||||
pool = newPool;
|
||||
newPool = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// else pool entry has been disabled so don't create new pools
|
||||
Debug.Assert(PoolGroupStateDisabled == _state, "state should be disabled");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// else found an existing pool to use instead
|
||||
Debug.Assert(PoolGroupStateActive == _state, "state should be active since a pool exists and lock holds");
|
||||
}
|
||||
}
|
||||
|
||||
if (null != newPool)
|
||||
{
|
||||
// don't need to call connectionFactory.QueuePoolForRelease(newPool) because
|
||||
// pool callbacks were delayed and no risk of connections being created
|
||||
newPool.Shutdown();
|
||||
}
|
||||
}
|
||||
// the found pool could be in any state
|
||||
}
|
||||
}
|
||||
|
||||
if (null == pool)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// keep the pool entry state active when not pooling
|
||||
MarkPoolGroupAsActive();
|
||||
}
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
private bool MarkPoolGroupAsActive()
|
||||
{
|
||||
// when getting a connection, make the entry active if it was idle (but not disabled)
|
||||
// must always lock this before calling
|
||||
|
||||
if (PoolGroupStateIdle == _state)
|
||||
{
|
||||
_state = PoolGroupStateActive;
|
||||
}
|
||||
return (PoolGroupStateActive == _state);
|
||||
}
|
||||
|
||||
internal bool Prune()
|
||||
{
|
||||
// must only call from DbConnectionFactory.PruneConnectionPoolGroups on background timer thread
|
||||
// must lock(DbConnectionFactory._connectionPoolGroups.SyncRoot) before calling ReadyToRemove
|
||||
// to avoid conflict with DbConnectionFactory.CreateConnectionPoolGroup replacing pool entry
|
||||
lock (this)
|
||||
{
|
||||
if (_poolCollection.Count > 0)
|
||||
{
|
||||
var newPoolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
|
||||
|
||||
foreach (var entry in _poolCollection)
|
||||
{
|
||||
DbConnectionPool pool = entry.Value;
|
||||
if (pool != null)
|
||||
{
|
||||
// Actually prune the pool if there are no connections in the pool and no errors occurred.
|
||||
// Empty pool during pruning indicates zero or low activity, but
|
||||
// an error state indicates the pool needs to stay around to
|
||||
// throttle new connection attempts.
|
||||
if ((!pool.ErrorOccurred) && (0 == pool.Count))
|
||||
{
|
||||
// Order is important here. First we remove the pool
|
||||
// from the collection of pools so no one will try
|
||||
// to use it while we're processing and finally we put the
|
||||
// pool into a list of pools to be released when they
|
||||
// are completely empty.
|
||||
DbConnectionFactory connectionFactory = pool.ConnectionFactory;
|
||||
|
||||
connectionFactory.QueuePoolForRelease(pool, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
newPoolCollection.TryAdd(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
_poolCollection = newPoolCollection;
|
||||
}
|
||||
|
||||
// must be pruning thread to change state and no connections
|
||||
// otherwise pruning thread risks making entry disabled soon after user calls ClearPool
|
||||
if (0 == _poolCollection.Count)
|
||||
{
|
||||
if (PoolGroupStateActive == _state)
|
||||
{
|
||||
_state = PoolGroupStateIdle;
|
||||
}
|
||||
else if (PoolGroupStateIdle == _state)
|
||||
{
|
||||
_state = PoolGroupStateDisabled;
|
||||
}
|
||||
}
|
||||
return (PoolGroupStateDisabled == _state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,282 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Data.ProviderBase
|
||||
{
|
||||
internal abstract class DbReferenceCollection
|
||||
{
|
||||
private struct CollectionEntry
|
||||
{
|
||||
private int _tag; // information about the reference
|
||||
private WeakReference _weak; // the reference itself.
|
||||
|
||||
public void NewTarget(int tag, object target)
|
||||
{
|
||||
Debug.Assert(!HasTarget, "Entry already has a valid target");
|
||||
Debug.Assert(tag != 0, "Bad tag");
|
||||
Debug.Assert(target != null, "Invalid target");
|
||||
|
||||
if (_weak == null)
|
||||
{
|
||||
_weak = new WeakReference(target, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_weak.Target = target;
|
||||
}
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
public void RemoveTarget()
|
||||
{
|
||||
_tag = 0;
|
||||
}
|
||||
|
||||
public bool HasTarget
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((_tag != 0) && (_weak.IsAlive));
|
||||
}
|
||||
}
|
||||
|
||||
public int Tag
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tag;
|
||||
}
|
||||
}
|
||||
|
||||
public object Target
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_tag == 0 ? null : _weak.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const int LockPollTime = 100; // Time to wait (in ms) between attempting to get the _itemLock
|
||||
private const int DefaultCollectionSize = 20; // Default size for the collection, and the amount to grow every time the collection is full
|
||||
private CollectionEntry[] _items; // The collection of items we are keeping track of
|
||||
private readonly object _itemLock; // Used to synchronize access to the _items collection
|
||||
private int _optimisticCount; // (#ItemsAdded - #ItemsRemoved) - This estimates the number of items that we *should* have (but doesn't take into account item targets being GC'd)
|
||||
private int _lastItemIndex; // Location of the last item in _items
|
||||
private volatile bool _isNotifying; // Indicates that the collection is currently being notified (and, therefore, about to be cleared)
|
||||
|
||||
protected DbReferenceCollection()
|
||||
{
|
||||
_items = new CollectionEntry[DefaultCollectionSize];
|
||||
_itemLock = new object();
|
||||
_optimisticCount = 0;
|
||||
_lastItemIndex = 0;
|
||||
}
|
||||
|
||||
public abstract void Add(object value, int tag);
|
||||
|
||||
protected void AddItem(object value, int tag)
|
||||
{
|
||||
Debug.Assert(null != value && 0 != tag, "AddItem with null value or 0 tag");
|
||||
bool itemAdded = false;
|
||||
|
||||
lock (_itemLock)
|
||||
{
|
||||
// Try to find a free spot
|
||||
for (int i = 0; i <= _lastItemIndex; ++i)
|
||||
{
|
||||
if (_items[i].Tag == 0)
|
||||
{
|
||||
_items[i].NewTarget(tag, value);
|
||||
Debug.Assert(_items[i].HasTarget, "missing expected target");
|
||||
itemAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No free spots, can we just add on to the end?
|
||||
if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length))
|
||||
{
|
||||
_lastItemIndex++;
|
||||
_items[_lastItemIndex].NewTarget(tag, value);
|
||||
itemAdded = true;
|
||||
}
|
||||
|
||||
// If no free spots and no space at the end, try to find a dead item
|
||||
if (!itemAdded)
|
||||
{
|
||||
for (int i = 0; i <= _lastItemIndex; ++i)
|
||||
{
|
||||
if (!_items[i].HasTarget)
|
||||
{
|
||||
_items[i].NewTarget(tag, value);
|
||||
Debug.Assert(_items[i].HasTarget, "missing expected target");
|
||||
itemAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing was free, then resize and add to the end
|
||||
if (!itemAdded)
|
||||
{
|
||||
Array.Resize<CollectionEntry>(ref _items, _items.Length * 2);
|
||||
_lastItemIndex++;
|
||||
_items[_lastItemIndex].NewTarget(tag, value);
|
||||
}
|
||||
|
||||
_optimisticCount++;
|
||||
}
|
||||
}
|
||||
|
||||
internal T FindItem<T>(int tag, Func<T, bool> filterMethod) where T : class
|
||||
{
|
||||
bool lockObtained = false;
|
||||
try
|
||||
{
|
||||
TryEnterItemLock(ref lockObtained);
|
||||
if (lockObtained)
|
||||
{
|
||||
if (_optimisticCount > 0)
|
||||
{
|
||||
// Loop through the items
|
||||
for (int counter = 0; counter <= _lastItemIndex; counter++)
|
||||
{
|
||||
// Check tag (should be easiest and quickest)
|
||||
if (_items[counter].Tag == tag)
|
||||
{
|
||||
// NOTE: Check if the returned value is null twice may seem wasteful, but this if for performance
|
||||
// Since checking for null twice is cheaper than calling both HasTarget and Target OR always attempting to typecast
|
||||
object value = _items[counter].Target;
|
||||
if (value != null)
|
||||
{
|
||||
// Make sure the item has the correct type and passes the filtering
|
||||
T tempItem = value as T;
|
||||
if ((tempItem != null) && (filterMethod(tempItem)))
|
||||
{
|
||||
return tempItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitItemLockIfNeeded(lockObtained);
|
||||
}
|
||||
|
||||
// If we got to here, then no item was found, so return null
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Notify(int message)
|
||||
{
|
||||
bool lockObtained = false;
|
||||
try
|
||||
{
|
||||
TryEnterItemLock(ref lockObtained);
|
||||
if (lockObtained)
|
||||
{
|
||||
try
|
||||
{
|
||||
_isNotifying = true;
|
||||
|
||||
// Loop through each live item and notify it
|
||||
if (_optimisticCount > 0)
|
||||
{
|
||||
for (int index = 0; index <= _lastItemIndex; ++index)
|
||||
{
|
||||
object value = _items[index].Target; // checks tag & gets target
|
||||
if (null != value)
|
||||
{
|
||||
NotifyItem(message, _items[index].Tag, value);
|
||||
_items[index].RemoveTarget();
|
||||
}
|
||||
Debug.Assert(!_items[index].HasTarget, "Unexpected target after notifying");
|
||||
}
|
||||
_optimisticCount = 0;
|
||||
}
|
||||
|
||||
// Shrink collection (if needed)
|
||||
if (_items.Length > 100)
|
||||
{
|
||||
_lastItemIndex = 0;
|
||||
_items = new CollectionEntry[DefaultCollectionSize];
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isNotifying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitItemLockIfNeeded(lockObtained);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void NotifyItem(int message, int tag, object value);
|
||||
|
||||
public abstract void Remove(object value);
|
||||
|
||||
protected void RemoveItem(object value)
|
||||
{
|
||||
Debug.Assert(null != value, "RemoveItem with null");
|
||||
|
||||
bool lockObtained = false;
|
||||
try
|
||||
{
|
||||
TryEnterItemLock(ref lockObtained);
|
||||
|
||||
if (lockObtained)
|
||||
{
|
||||
// Find the value, and then remove the target from our collection
|
||||
if (_optimisticCount > 0)
|
||||
{
|
||||
for (int index = 0; index <= _lastItemIndex; ++index)
|
||||
{
|
||||
if (value == _items[index].Target)
|
||||
{ // checks tag & gets target
|
||||
_items[index].RemoveTarget();
|
||||
_optimisticCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitItemLockIfNeeded(lockObtained);
|
||||
}
|
||||
}
|
||||
|
||||
// This is polling lock that will abandon getting the lock if _isNotifying is set to true
|
||||
private void TryEnterItemLock(ref bool lockObtained)
|
||||
{
|
||||
// Assume that we couldn't take the lock
|
||||
lockObtained = false;
|
||||
// Keep trying to take the lock until either we've taken it, or the collection is being notified
|
||||
while ((!_isNotifying) && (!lockObtained))
|
||||
{
|
||||
Monitor.TryEnter(_itemLock, LockPollTime, ref lockObtained);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExitItemLockIfNeeded(bool lockObtained)
|
||||
{
|
||||
if (lockObtained)
|
||||
{
|
||||
Monitor.Exit(_itemLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
//
|
||||
// Class used to manage timeouts in complex system operations.
|
||||
//
|
||||
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.ProviderBase
|
||||
{
|
||||
// Purpose:
|
||||
// Manages determining and tracking timeouts
|
||||
//
|
||||
// Intended use:
|
||||
// Call StartXXXXTimeout() to get a timer with the given expiration point
|
||||
// Get remaining time in appropriate format to pass to subsystem timeouts
|
||||
// Check for timeout via IsExpired for checks in managed code.
|
||||
// Simply abandon to GC when done.
|
||||
internal class TimeoutTimer
|
||||
{
|
||||
//-------------------
|
||||
// Fields
|
||||
//-------------------
|
||||
private long _timerExpire;
|
||||
private bool _isInfiniteTimeout;
|
||||
|
||||
//-------------------
|
||||
// Timeout-setting methods
|
||||
//-------------------
|
||||
|
||||
// Get a new timer that will expire in the given number of seconds
|
||||
// For input, a value of zero seconds indicates infinite timeout
|
||||
internal static TimeoutTimer StartSecondsTimeout(int seconds)
|
||||
{
|
||||
//--------------------
|
||||
// Preconditions: None (seconds must conform to SetTimeoutSeconds requirements)
|
||||
|
||||
//--------------------
|
||||
// Method body
|
||||
var timeout = new TimeoutTimer();
|
||||
timeout.SetTimeoutSeconds(seconds);
|
||||
|
||||
//---------------------
|
||||
// Postconditions
|
||||
Debug.Assert(timeout != null); // Need a valid timeouttimer if no error
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
// Get a new timer that will expire in the given number of milliseconds
|
||||
// No current need to support infinite milliseconds timeout
|
||||
internal static TimeoutTimer StartMillisecondsTimeout(long milliseconds)
|
||||
{
|
||||
//--------------------
|
||||
// Preconditions
|
||||
Debug.Assert(0 <= milliseconds);
|
||||
|
||||
//--------------------
|
||||
// Method body
|
||||
var timeout = new TimeoutTimer();
|
||||
timeout._timerExpire = checked(ADP.TimerCurrent() + (milliseconds * TimeSpan.TicksPerMillisecond));
|
||||
timeout._isInfiniteTimeout = false;
|
||||
|
||||
//---------------------
|
||||
// Postconditions
|
||||
Debug.Assert(timeout != null); // Need a valid timeouttimer if no error
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
//-------------------
|
||||
// Methods for changing timeout
|
||||
//-------------------
|
||||
|
||||
internal void SetTimeoutSeconds(int seconds)
|
||||
{
|
||||
//--------------------
|
||||
// Preconditions
|
||||
Debug.Assert(0 <= seconds || InfiniteTimeout == seconds); // no need to support negative seconds at present
|
||||
|
||||
//--------------------
|
||||
// Method body
|
||||
if (InfiniteTimeout == seconds)
|
||||
{
|
||||
_isInfiniteTimeout = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stash current time + timeout
|
||||
_timerExpire = checked(ADP.TimerCurrent() + ADP.TimerFromSeconds(seconds));
|
||||
_isInfiniteTimeout = false;
|
||||
}
|
||||
//---------------------
|
||||
// Postconditions:None
|
||||
}
|
||||
|
||||
//-------------------
|
||||
// Timeout info properties
|
||||
//-------------------
|
||||
|
||||
// Indicator for infinite timeout when starting a timer
|
||||
internal static readonly long InfiniteTimeout = 0;
|
||||
|
||||
// Is this timer in an expired state?
|
||||
internal bool IsExpired
|
||||
{
|
||||
get
|
||||
{
|
||||
return !IsInfinite && ADP.TimerHasExpired(_timerExpire);
|
||||
}
|
||||
}
|
||||
|
||||
// is this an infinite-timeout timer?
|
||||
internal bool IsInfinite
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isInfiniteTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
// Special accessor for TimerExpire for use when thunking to legacy timeout methods.
|
||||
internal long LegacyTimerExpire
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_isInfiniteTimeout) ? Int64.MaxValue : _timerExpire;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns milliseconds remaining trimmed to zero for none remaining
|
||||
// and long.MaxValue for infinite
|
||||
// This method should be preferred for internal calculations that are not
|
||||
// yet common enough to code into the TimeoutTimer class itself.
|
||||
internal long MillisecondsRemaining
|
||||
{
|
||||
get
|
||||
{
|
||||
//-------------------
|
||||
// Preconditions: None
|
||||
|
||||
//-------------------
|
||||
// Method Body
|
||||
long milliseconds;
|
||||
if (_isInfiniteTimeout)
|
||||
{
|
||||
milliseconds = long.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
milliseconds = ADP.TimerRemainingMilliseconds(_timerExpire);
|
||||
if (0 > milliseconds)
|
||||
{
|
||||
milliseconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Postconditions
|
||||
Debug.Assert(0 <= milliseconds); // This property guarantees no negative return values
|
||||
|
||||
return milliseconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
external/corefx/src/System.Data.Odbc/src/MatchingRefApiCompatBaseline.txt
vendored
Normal file
6
external/corefx/src/System.Data.Odbc/src/MatchingRefApiCompatBaseline.txt
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Exposed publicly only in implementation for serialization compat
|
||||
TypesMustExist : Type 'System.Data.Odbc.ODBC32' does not exist in the implementation but it does exist in the contract.
|
||||
|
||||
# Cannot be exposed in the ref yet as it is new API that doesn't exist in netfx
|
||||
MembersMustExist : Member 'System.Data.Odbc.OdbcParameter.Offset.get()' does not exist in the implementation but it does exist in the contract.
|
||||
MembersMustExist : Member 'System.Data.Odbc.OdbcParameter.Offset.set(System.Int32)' does not exist in the implementation but it does exist in the contract.
|
||||
1013
external/corefx/src/System.Data.Odbc/src/Resources/System.Data.Odbc.OdbcMetaData.xml
vendored
Normal file
1013
external/corefx/src/System.Data.Odbc/src/Resources/System.Data.Odbc.OdbcMetaData.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,9 @@
|
||||
<Compile Include="$(CommonPath)\System\Data\Common\AdapterUtil.cs">
|
||||
<Link>Common\System\Data\Common\AdapterUtil.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\Common\AdapterUtil.Drivers.cs">
|
||||
<Link>System\Data\Common\AdapterUtil.Drivers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Common\System\Data\Common\AdapterUtil.Odbc.cs" />
|
||||
<Compile Include="Common\System\Data\Common\DbConnectionOptions.cs" />
|
||||
<Compile Include="$(CommonPath)\System\Data\Common\DbConnectionOptions.Common.cs">
|
||||
@@ -49,24 +52,41 @@
|
||||
<Compile Include="Common\System\Data\Common\SafeNativeMethods.cs" />
|
||||
<Compile Include="Common\System\Data\DataStorage.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbBuffer.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbMetaDataFactory.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\TimeoutTimer.cs" />
|
||||
<Compile Include="$(CommonPath)\System\Data\Common\FieldNameLookup.cs">
|
||||
<Link>Common\System\Data\ProviderBase\FieldNameLookup.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\Common\BasicFieldNameLookup.cs">
|
||||
<Link>Common\System\Data\ProviderBase\BasicFieldNameLookup.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\DbConnectionInternal.cs">
|
||||
<Link>System\Data\ProviderBase\DbConnectionInternal.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\DbConnectionFactory.cs">
|
||||
<Link>System\Data\ProviderBase\DbConnectionFactory.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\DbConnectionPoolGroup.cs">
|
||||
<Link>System\Data\ProviderBase\DbConnectionPoolGroup.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\TimeoutTimer.cs">
|
||||
<Link>System\Data\ProviderBase\TimeoutTimer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\DbReferenceCollection.cs">
|
||||
<Link>System\Data\ProviderBase\DbReferenceCollection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\DbMetaDataFactory.cs">
|
||||
<Link>System\Data\ProviderBase\DbMetaDataFactory.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\Data\ProviderBase\DbConnectionClosed.cs">
|
||||
<Link>System\Data\ProviderBase\DbConnectionClosed.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionClosed.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionFactory.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionInternal.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionPool.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionPoolGroup.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionPoolIdentity.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionPoolOptions.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbConnectionPoolProviderInfo.cs" />
|
||||
<Compile Include="Common\System\Data\ProviderBase\DbReferenceCollection.cs" />
|
||||
<Compile Include="System\Data\Odbc\DbDataRecord.cs" />
|
||||
<Compile Include="System\Data\Odbc\Odbc32.cs" />
|
||||
<Compile Include="System\Data\Odbc\OdbcCommand.cs">
|
||||
@@ -179,6 +199,11 @@
|
||||
<ItemGroup Condition="$(TargetGroup.StartsWith('netcoreapp'))">
|
||||
<Reference Include="System.Transactions.Local" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\System.Data.Odbc.OdbcMetaData.xml">
|
||||
<LogicalName>System.Data.Odbc.OdbcMetaData.xml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DatabaseSetupInstructions.md" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.Data.Common;
|
||||
using System.Data.ProviderBase;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace System.Data.Odbc
|
||||
{
|
||||
@@ -50,6 +52,36 @@ namespace System.Data.Odbc
|
||||
return new OdbcConnectionPoolGroupProviderInfo();
|
||||
}
|
||||
|
||||
protected override DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory)
|
||||
{
|
||||
Debug.Assert(internalConnection != null, "internalConnection may not be null.");
|
||||
cacheMetaDataFactory = false;
|
||||
|
||||
OdbcConnection odbcOuterConnection = ((OdbcConnectionOpen)internalConnection).OuterConnection;
|
||||
Debug.Assert(odbcOuterConnection != null, "outer connection may not be null.");
|
||||
|
||||
// get the DBMS Name
|
||||
object driverName = null;
|
||||
string stringValue = odbcOuterConnection.GetInfoStringUnhandled(ODBC32.SQL_INFO.DRIVER_NAME);
|
||||
if (stringValue != null)
|
||||
{
|
||||
driverName = stringValue;
|
||||
}
|
||||
|
||||
Stream XMLStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("System.Data.Odbc.OdbcMetaData.xml");
|
||||
cacheMetaDataFactory = true;
|
||||
|
||||
|
||||
Debug.Assert(XMLStream != null, "XMLstream may not be null.");
|
||||
|
||||
String versionString = odbcOuterConnection.GetInfoStringUnhandled(ODBC32.SQL_INFO.DBMS_VER);
|
||||
|
||||
return new OdbcMetaDataFactory(XMLStream,
|
||||
versionString,
|
||||
versionString,
|
||||
odbcOuterConnection);
|
||||
}
|
||||
|
||||
internal override DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection)
|
||||
{
|
||||
OdbcConnection c = (connection as OdbcConnection);
|
||||
|
||||
@@ -175,6 +175,22 @@ namespace System.Data.Odbc
|
||||
|
||||
partial void RepairInnerConnection();
|
||||
|
||||
override public DataTable GetSchema()
|
||||
{
|
||||
return this.GetSchema(DbMetaDataCollectionNames.MetaDataCollections, null);
|
||||
}
|
||||
|
||||
override public DataTable GetSchema(string collectionName)
|
||||
{
|
||||
return this.GetSchema(collectionName, null);
|
||||
}
|
||||
|
||||
override public DataTable GetSchema(string collectionName, string[] restrictionValues)
|
||||
{
|
||||
// NOTE: This is virtual because not all providers may choose to support
|
||||
// returning schema data
|
||||
return InnerConnection.GetSchema(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues);
|
||||
}
|
||||
|
||||
internal void NotifyWeakReference(int message)
|
||||
{
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Data.Odbc.Tests
|
||||
{
|
||||
|
||||
37
external/corefx/src/System.Data.Odbc/tests/OdbcConnectionSchemaTests.cs
vendored
Normal file
37
external/corefx/src/System.Data.Odbc/tests/OdbcConnectionSchemaTests.cs
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace System.Data.Odbc.Tests
|
||||
{
|
||||
public class OdbcConnectionSchemaTests
|
||||
{
|
||||
[CheckConnStrSetupFact]
|
||||
public void TestConnectionSchemaOnOpenConnection()
|
||||
{
|
||||
string connectionString = DataTestUtility.OdbcConnStr;
|
||||
|
||||
using (OdbcConnection connection = new OdbcConnection(connectionString))
|
||||
{
|
||||
connection.GetSchema();
|
||||
connection.Open();
|
||||
DataTable schema = connection.GetSchema();
|
||||
Assert.NotNull(schema);
|
||||
|
||||
DataTable tableSchema = connection.GetSchema("Tables");
|
||||
Assert.NotNull(tableSchema);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestConnectionSchemaOnNonOpenConnection()
|
||||
{
|
||||
using (OdbcConnection connection = new OdbcConnection(string.Empty))
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => connection.GetSchema());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
<Compile Include="Helpers.cs" />
|
||||
<Compile Include="IntegrationTestBase.cs" />
|
||||
<Compile Include="CommandBuilderTests.cs" />
|
||||
<Compile Include="OdbcConnectionSchemaTests.cs" />
|
||||
<Compile Include="ReaderTests.cs" />
|
||||
<Compile Include="SmokeTest.cs" />
|
||||
<Compile Include="TestCommon\DataTestUtility.cs" />
|
||||
@@ -50,4 +51,4 @@
|
||||
<Compile Include="ConnectionStrings.Unix.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user