Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlConnectionFactory.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnectionFactory.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.SqlClient
10 {
11     using System;
12     using System.Data.Common;
13     using System.Data.ProviderBase;
14     using System.Collections.Specialized;
15     using System.Configuration;
16     using System.Diagnostics;
17     using System.IO;
18     using System.Runtime.Versioning;
19
20     using Microsoft.SqlServer.Server;
21
22     
23     sealed internal class SqlConnectionFactory : DbConnectionFactory {
24         private SqlConnectionFactory() : base(SqlPerformanceCounters.SingletonInstance) {}
25
26         public static readonly SqlConnectionFactory SingletonInstance = new SqlConnectionFactory();
27         private const string _metaDataXml          = "MetaDataXml";
28
29         override public DbProviderFactory ProviderFactory {
30             get {
31                 return SqlClientFactory.Instance;
32             }
33         }
34
35         override protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection) {
36             return CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningConnection, userOptions: null);
37         }
38
39         override protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) {
40             SqlConnectionString opt = (SqlConnectionString)options;
41             SqlConnectionPoolKey key = (SqlConnectionPoolKey) poolKey;
42             SqlInternalConnection result = null;
43             SessionData recoverySessionData = null;
44             SqlConnection sqlOwningConnection = owningConnection as SqlConnection;
45             bool applyTransientFaultHandling = sqlOwningConnection != null ? sqlOwningConnection._applyTransientFaultHandling : false;
46
47             SqlConnectionString userOpt = null;
48             if (userOptions != null) {
49                 userOpt = (SqlConnectionString)userOptions;
50             }
51             else if (sqlOwningConnection != null) {
52                 userOpt = (SqlConnectionString)(sqlOwningConnection.UserConnectionOptions);                
53             }
54
55             if (sqlOwningConnection != null) {
56                 recoverySessionData = sqlOwningConnection._recoverySessionData;
57             }
58
59             if (opt.ContextConnection) {
60                 result = GetContextConnection(opt, poolGroupProviderInfo);
61             }
62             else {
63                 bool redirectedUserInstance       = false;
64                 DbConnectionPoolIdentity identity = null;
65
66                 // Pass DbConnectionPoolIdentity to SqlInternalConnectionTds if using integrated security.
67                 // Used by notifications.
68                 if (opt.IntegratedSecurity || opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
69                     if (pool != null) {
70                         identity = pool.Identity;
71                     }
72                     else {
73                         identity = DbConnectionPoolIdentity.GetCurrent();
74                     }
75                 }
76
77                 // FOLLOWING IF BLOCK IS ENTIRELY FOR SSE USER INSTANCES
78                 // If "user instance=true" is in the connection string, we're using SSE user instances
79                 if (opt.UserInstance) {
80                     // opt.DataSource is used to create the SSE connection
81                     redirectedUserInstance = true;
82                     string instanceName;
83
84                     if ( (null == pool) || 
85                          (null != pool && pool.Count <= 0) ) { // Non-pooled or pooled and no connections in the pool.
86
87                         SqlInternalConnectionTds sseConnection = null;
88                         try {
89                             // What about a failure - throw?  YES!
90                             // 
91
92
93
94                             SqlConnectionString sseopt = new SqlConnectionString(opt, opt.DataSource, true /* user instance=true */, false /* set Enlist = false */);
95                             sseConnection = new SqlInternalConnectionTds(identity, sseopt, key.Credential, null, "", null, false, applyTransientFaultHandling: applyTransientFaultHandling);
96                             // NOTE: Retrieve <UserInstanceName> here. This user instance name will be used below to connect to the Sql Express User Instance.
97                             instanceName = sseConnection.InstanceName;
98
99                             if (!instanceName.StartsWith("\\\\.\\", StringComparison.Ordinal)) {
100                                 throw SQL.NonLocalSSEInstance();
101                             }
102
103                             if (null != pool) { // Pooled connection - cache result
104                                 SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo;
105                                 // No lock since we are already in creation mutex
106                                 providerInfo.InstanceName = instanceName;
107                             }
108                         }
109                         finally {
110                             if (null != sseConnection) {
111                                 sseConnection.Dispose();
112                             }
113                         }
114                     }
115                     else { // Cached info from pool.
116                         SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo;
117                         // No lock since we are already in creation mutex
118                         instanceName = providerInfo.InstanceName;
119                     }
120
121                     // NOTE: Here connection option opt is cloned to set 'instanceName=<UserInstanceName>' that was
122                     //       retrieved from the previous SSE connection. For this UserInstance connection 'Enlist=True'.
123                     // options immutable - stored in global hash - don't modify
124                     opt = new SqlConnectionString(opt, instanceName, false /* user instance=false */, null /* do not modify the Enlist value */);
125                     poolGroupProviderInfo = null; // null so we do not pass to constructor below...
126                 }
127                 result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, pool, key.AccessToken, applyTransientFaultHandling: applyTransientFaultHandling);
128             }
129             return result;
130         }
131
132         protected override DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous) {
133             Debug.Assert(!ADP.IsEmpty(connectionString), "empty connectionString");
134             SqlConnectionString result = new SqlConnectionString(connectionString);
135             return result;
136         }
137
138         override internal DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){
139             DbConnectionPoolProviderInfo providerInfo = null;
140
141             if (((SqlConnectionString) connectionOptions).UserInstance) {
142                 providerInfo = new SqlConnectionPoolProviderInfo();
143             }
144
145             return providerInfo;
146         }
147
148         override protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions( DbConnectionOptions connectionOptions ) {
149             SqlConnectionString opt = (SqlConnectionString)connectionOptions;
150
151             DbConnectionPoolGroupOptions poolingOptions = null;
152
153             if (!opt.ContextConnection && opt.Pooling) {    // never pool context connections.
154                 int connectionTimeout = opt.ConnectTimeout;
155
156                 if ((0 < connectionTimeout) && (connectionTimeout < Int32.MaxValue/1000))
157                     connectionTimeout *= 1000;
158                 else if (connectionTimeout >= Int32.MaxValue/1000)
159                     connectionTimeout = Int32.MaxValue;
160
161                 poolingOptions = new DbConnectionPoolGroupOptions(
162                                                     opt.IntegratedSecurity || opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated,
163                                                     opt.MinPoolSize,
164                                                     opt.MaxPoolSize,
165                                                     connectionTimeout,
166                                                     opt.LoadBalanceTimeout,
167                                                     opt.Enlist);
168             }
169             return poolingOptions;
170         }
171
172         // SxS (VSDD 545786): metadata files are opened from <.NetRuntimeFolder>\CONFIG\<metadatafilename.xml>
173         // this operation is safe in SxS because the file is opened in read-only mode and each NDP runtime accesses its own copy of the metadata
174         // under the runtime folder.
175         [ResourceExposure(ResourceScope.None)]
176         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
177         override protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory){
178             Debug.Assert (internalConnection != null, "internalConnection may not be null.");
179             cacheMetaDataFactory = false;
180
181             if (internalConnection is SqlInternalConnectionSmi) {
182                 throw SQL.NotAvailableOnContextConnection();
183             }
184
185             Stream XMLStream =null;
186 #if !NO_CONFIGURATION
187             NameValueCollection settings = (NameValueCollection)PrivilegedConfigurationManager.GetSection("system.data.sqlclient");
188             if (settings != null){
189                 string [] values = settings.GetValues(_metaDataXml);
190                 if (values != null) {
191                     XMLStream = ADP.GetXmlStreamFromValues(values, _metaDataXml);
192                 }
193             }
194 #endif
195
196             // if the xml was not obtained from machine.config use the embedded XML resource
197             if (XMLStream == null){
198                 XMLStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("System.Data.SqlClient.SqlMetaData.xml");
199                 cacheMetaDataFactory = true;
200             }
201             Debug.Assert (XMLStream != null,"XMLstream may not be null.");
202
203             return new SqlMetaDataFactory (XMLStream,
204                                           internalConnection.ServerVersion,
205                                           internalConnection.ServerVersion); //internalConnection.ServerVersionNormalized);
206
207         }
208
209         override internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) {
210             return new SqlConnectionPoolGroupProviderInfo((SqlConnectionString)connectionOptions);
211         }
212
213
214         internal static SqlConnectionString FindSqlConnectionOptions(SqlConnectionPoolKey key) {
215             SqlConnectionString connectionOptions = (SqlConnectionString )SingletonInstance.FindConnectionOptions(key);
216             if (null == connectionOptions) {
217                 connectionOptions = new SqlConnectionString(key.ConnectionString);
218             }
219             if (connectionOptions.IsEmpty) {
220                 throw ADP.NoConnectionString();
221             }
222             return connectionOptions;
223         }
224
225         private SqlInternalConnectionSmi GetContextConnection(SqlConnectionString options, object providerInfo) {
226             SmiContext smiContext = SmiContextFactory.Instance.GetCurrentContext();
227
228             SqlInternalConnectionSmi result = (SqlInternalConnectionSmi)smiContext.GetContextValue((int)SmiContextFactory.ContextKey.Connection);
229
230             // context connections are automatically re-useable if they exist unless they've been doomed.
231             if (null == result || result.IsConnectionDoomed) {
232                 if (null != result) {
233                     result.Dispose();   // A doomed connection is a messy thing.  Dispose of it promptly in nearest receptacle.
234                 }
235
236                 result = new SqlInternalConnectionSmi(options, smiContext);
237                 smiContext.SetContextValue((int)SmiContextFactory.ContextKey.Connection, result);
238             }
239
240             result.Activate();
241
242             return result;
243         }
244
245         override internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection) {
246             SqlConnection c = (connection as SqlConnection);
247             if (null != c) {
248                 return c.PoolGroup;
249             }
250             return null;
251         }
252
253         override internal DbConnectionInternal GetInnerConnection(DbConnection connection) {
254             SqlConnection c = (connection as SqlConnection);
255             if (null != c) {
256                 return c.InnerConnection;
257             }
258             return null;
259         }
260
261         override protected int GetObjectId(DbConnection connection) {
262             SqlConnection c = (connection as SqlConnection);
263             if (null != c) {
264                 return c.ObjectID;
265             }
266             return 0;
267         }
268
269         override internal void PermissionDemand(DbConnection outerConnection) {
270             SqlConnection c = (outerConnection as SqlConnection);
271             if (null != c) {
272                 c.PermissionDemand();
273             }
274         }
275
276         override internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup) {
277             SqlConnection c = (outerConnection as SqlConnection);
278             if (null != c) {
279                 c.PoolGroup = poolGroup;
280             }
281         }
282
283         override internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to) {
284             SqlConnection c = (owningObject as SqlConnection);
285             if (null != c) {
286                 c.SetInnerConnectionEvent(to);
287             }
288         }
289
290         override internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) {
291             SqlConnection c = (owningObject as SqlConnection);
292             if (null != c) {
293                 return c.SetInnerConnectionFrom(to, from);
294             }
295             return false;
296         }
297
298         override internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to) {
299             SqlConnection c = (owningObject as SqlConnection);
300             if (null != c) {
301                 c.SetInnerConnectionTo(to);
302             }
303         }
304
305     }
306
307     sealed internal class SqlPerformanceCounters : DbConnectionPoolCounters {
308         private const string CategoryName = ".NET Data Provider for SqlServer";
309         private const string CategoryHelp = "Counters for System.Data.SqlClient";
310
311         public static readonly SqlPerformanceCounters SingletonInstance = new SqlPerformanceCounters();
312
313 #if !MOBILE
314         [System.Diagnostics.PerformanceCounterPermissionAttribute(System.Security.Permissions.SecurityAction.Assert, PermissionAccess=PerformanceCounterPermissionAccess.Write, MachineName=".", CategoryName=CategoryName)]
315         private SqlPerformanceCounters() : base (CategoryName, CategoryHelp) {
316         }
317 #endif
318     }
319 }
320