Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data / System / Data / OleDb / oledbconnectionstring.cs
blobe1406cfc2a1f10cd1f7824c44dfb11aed1c0379f
1 //------------------------------------------------------------------------------
2 // <copyright file="oledbconnectionstring.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 //------------------------------------------------------------------------------
9 namespace System.Data.OleDb {
11 using System;
12 using System.Collections;
13 using System.Collections.Generic;
14 using System.Collections.Specialized;
15 using System.Data;
16 using System.Data.Common;
17 using System.Diagnostics;
18 using System.Globalization;
19 using System.IO;
20 using System.Security;
21 using System.Security.Permissions;
22 using System.Text;
23 using Microsoft.Win32;
24 using System.Runtime.Versioning;
26 internal struct SchemaSupport {
27 internal Guid _schemaRowset;
28 internal int _restrictions;
31 internal sealed class OleDbConnectionString : DbConnectionOptions {
32 // instances of this class are intended to be immutable, i.e readonly
33 // used by pooling classes so it is much easier to verify correctness
34 // when not worried about the class being modified during execution
36 internal static class KEY {
37 internal const string Asynchronous_Processing = "asynchronous processing";
38 internal const string Connect_Timeout = "connect timeout";
39 internal const string Data_Provider = "data provider";
40 internal const string Data_Source = "data source";
41 internal const string Extended_Properties = "extended properties";
42 internal const string File_Name = "file name";
43 internal const string Initial_Catalog = "initial catalog";
44 internal const string Ole_DB_Services = "ole db services";
45 internal const string Persist_Security_Info = "persist security info";
46 internal const string Prompt = "prompt";
47 internal const string Provider = "provider";
48 internal const string RemoteProvider = "remote provider";
49 internal const string WindowHandle = "window handle";
52 // registry key and dword value entry for udl pooling
53 private static class UDL {
54 internal const string Header = "\xfeff[oledb]\r\n; Everything after this line is an OLE DB initstring\r\n";
55 internal const string Location = "SOFTWARE\\Microsoft\\DataAccess\\Udl Pooling";
56 internal const string Pooling = "Cache Size";
58 static internal volatile bool _PoolSizeInit;
59 static internal int _PoolSize;
61 static internal volatile Dictionary<string,string> _Pool;
62 static internal object _PoolLock = new object();
65 private static class VALUES {
66 internal const string NoPrompt = "noprompt";
69 // set during ctor
70 internal readonly bool PossiblePrompt;
71 internal readonly string ActualConnectionString; // cached value passed to GetDataSource
73 private readonly string _expandedConnectionString;
75 internal SchemaSupport[] _schemaSupport;
77 internal int _sqlSupport;
78 internal bool _supportMultipleResults;
79 internal bool _supportIRow;
80 internal bool _hasSqlSupport;
81 internal bool _hasSupportMultipleResults, _hasSupportIRow;
83 private int _oledbServices;
85 // these are cached delegates (per unique connectionstring)
86 internal UnsafeNativeMethods.IUnknownQueryInterface DangerousDataSourceIUnknownQueryInterface;
87 internal UnsafeNativeMethods.IDBInitializeInitialize DangerousIDBInitializeInitialize;
88 internal UnsafeNativeMethods.IDBCreateSessionCreateSession DangerousIDBCreateSessionCreateSession;
89 internal UnsafeNativeMethods.IDBCreateCommandCreateCommand DangerousIDBCreateCommandCreateCommand;
91 // since IDBCreateCommand interface may not be supported for a particular provider (only IOpenRowset)
92 // we cache that fact rather than call QueryInterface on every call to Open
93 internal bool HaveQueriedForCreateCommand;
95 // SxS: if user specifies a value for "File Name=" (UDL) in connection string, OleDbConnectionString will load the connection string
96 // from the UDL file. The UDL file is opened as FileMode.Open, FileAccess.Read, FileShare.Read, allowing concurrent access to it.
97 [ResourceExposure(ResourceScope.None)]
98 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
99 internal OleDbConnectionString(string connectionString, bool validate) : base(connectionString) {
100 string prompt = this[KEY.Prompt];
101 PossiblePrompt = ((!ADP.IsEmpty(prompt) && (0 != String.Compare(prompt, VALUES.NoPrompt, StringComparison.OrdinalIgnoreCase)))
102 || !ADP.IsEmpty(this[KEY.WindowHandle]));
104 if (!IsEmpty) {
105 string udlConnectionString = null;
106 if (!validate) {
107 int position = 0;
108 string udlFileName = null;
109 _expandedConnectionString = ExpandDataDirectories(ref udlFileName, ref position);
111 if (!ADP.IsEmpty(udlFileName)) { // fail via new FileStream vs. GetFullPath
112 udlFileName = ADP.GetFullPath(udlFileName); // MDAC 82833
114 if (null != udlFileName) {
115 udlConnectionString = LoadStringFromStorage(udlFileName);
117 if (!ADP.IsEmpty(udlConnectionString)) {
118 _expandedConnectionString = _expandedConnectionString.Substring(0, position) + udlConnectionString + ';' + _expandedConnectionString.Substring(position);
122 if (validate || ADP.IsEmpty(udlConnectionString)) {
123 ActualConnectionString = ValidateConnectionString(connectionString);
128 internal int ConnectTimeout {
129 get { return base.ConvertValueToInt32(KEY.Connect_Timeout, ADP.DefaultConnectionTimeout); }
132 internal string DataSource {
133 get { return base.ConvertValueToString(KEY.Data_Source, ADP.StrEmpty); }
136 internal string InitialCatalog {
137 get { return base.ConvertValueToString(KEY.Initial_Catalog, ADP.StrEmpty); }
140 internal string Provider {
141 get {
142 Debug.Assert(!ADP.IsEmpty(this[KEY.Provider]), "no Provider");
143 return this[KEY.Provider];
147 internal int OleDbServices {
148 get {
149 return _oledbServices;
153 internal SchemaSupport[] SchemaSupport { // OleDbConnection.GetSchemaRowsetInformation
154 get { return _schemaSupport; }
155 set { _schemaSupport = value; }
158 protected internal override System.Security.PermissionSet CreatePermissionSet() {
159 System.Security.PermissionSet permissionSet;
160 if (PossiblePrompt) {
161 permissionSet = new NamedPermissionSet("FullTrust");
163 else {
164 permissionSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None);
165 permissionSet.AddPermission(new OleDbPermission(this));
167 return permissionSet;
170 protected internal override string Expand() {
171 if (null != _expandedConnectionString) {
172 return _expandedConnectionString;
174 else {
175 return base.Expand();
179 internal int GetSqlSupport(OleDbConnection connection) {
180 int sqlSupport = _sqlSupport;
181 if (!_hasSqlSupport) {
182 object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_SQLSUPPORT);
183 if (value is Int32) { // not OleDbPropertyStatus
184 sqlSupport = (int) value;
186 _sqlSupport = sqlSupport;
187 _hasSqlSupport = true;
189 return sqlSupport;
192 internal bool GetSupportIRow(OleDbConnection connection, OleDbCommand command) {
193 bool supportIRow = _supportIRow;
194 if (!_hasSupportIRow) {
195 object value = command.GetPropertyValue(OleDbPropertySetGuid.Rowset, ODB.DBPROP_IRow);
197 // SQLOLEDB always returns VARIANT_FALSE for DBPROP_IROW, so base the answer on existance
198 supportIRow = !(value is OleDbPropertyStatus);
199 _supportIRow = supportIRow;
200 _hasSupportIRow = true;
202 return supportIRow;
205 internal bool GetSupportMultipleResults(OleDbConnection connection) {
206 bool supportMultipleResults = _supportMultipleResults;
207 if (!_hasSupportMultipleResults) {
208 object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_MULTIPLERESULTS);
209 if (value is Int32) {// not OleDbPropertyStatus
210 supportMultipleResults = (ODB.DBPROPVAL_MR_NOTSUPPORTED != (int) value);
212 _supportMultipleResults = supportMultipleResults;
213 _hasSupportMultipleResults = true;
215 return supportMultipleResults;
218 static private int UdlPoolSize { // MDAC 69925
219 // SxS: UdpPoolSize reads registry value to get the pool size
220 [ResourceExposure(ResourceScope.None)]
221 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
222 get {
223 int poolsize = UDL._PoolSize;
224 if (!UDL._PoolSizeInit) {
225 object value = ADP.LocalMachineRegistryValue(UDL.Location, UDL.Pooling);
226 if (value is Int32) {
227 poolsize = (int) value;
228 poolsize = ((0 < poolsize) ? poolsize : 0);
229 UDL._PoolSize = poolsize;
231 UDL._PoolSizeInit = true;
233 return poolsize;
237 [ResourceExposure(ResourceScope.Machine)]
238 [ResourceConsumption(ResourceScope.Machine)]
239 static private string LoadStringFromStorage(string udlfilename) {
240 string udlConnectionString = null;
241 Dictionary<string,string> udlcache = UDL._Pool;
243 if ((null == udlcache) || !udlcache.TryGetValue(udlfilename, out udlConnectionString)) {
244 udlConnectionString = LoadStringFromFileStorage(udlfilename);
245 if (null != udlConnectionString) {
246 Debug.Assert(!ADP.IsEmpty(udlfilename), "empty filename didn't fail");
248 if (0 < UdlPoolSize) {
249 Debug.Assert(udlfilename == ADP.GetFullPath(udlfilename), "only cache full path filenames"); // MDAC 82833
251 if (null == udlcache) {
252 udlcache = new Dictionary<string,string>();
253 udlcache[udlfilename] = udlConnectionString;
255 lock(UDL._PoolLock) {
256 if (null != UDL._Pool) {
257 udlcache = UDL._Pool;
259 else {
260 UDL._Pool = udlcache;
261 udlcache = null;
265 if (null != udlcache) {
266 lock(udlcache) {
267 udlcache[udlfilename] = udlConnectionString;
273 return udlConnectionString;
276 [ResourceExposure(ResourceScope.Machine)]
277 [ResourceConsumption(ResourceScope.Machine)]
278 static private string LoadStringFromFileStorage(string udlfilename) {
279 // Microsoft Data Link File Format
280 // The first two lines of a .udl file must have exactly the following contents in order to work properly:
281 // [oledb]
282 // ; Everything after this line is an OLE DB initstring
284 string connectionString = null;
285 Exception failure = null;
286 try {
287 int hdrlength = ADP.CharSize*UDL.Header.Length;
288 using(FileStream fstream = new FileStream(udlfilename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
289 long length = fstream.Length;
290 if (length < hdrlength || (0 != length%ADP.CharSize)) {
291 failure = ADP.InvalidUDL();
293 else {
294 byte[] bytes = new Byte[hdrlength];
295 int count = fstream.Read(bytes, 0, bytes.Length);
296 if (count < hdrlength) {
297 failure = ADP.InvalidUDL();
299 else if (System.Text.Encoding.Unicode.GetString(bytes, 0, hdrlength) != UDL.Header) {
300 failure = ADP.InvalidUDL();
302 else { // please verify header before allocating memory block for connection string
303 bytes = new Byte[length - hdrlength];
304 count = fstream.Read(bytes, 0, bytes.Length);
305 connectionString = System.Text.Encoding.Unicode.GetString(bytes, 0, count);
310 catch(Exception e) {
312 if (!ADP.IsCatchableExceptionType(e)) {
313 throw;
316 throw ADP.UdlFileError(e);
318 if (null != failure) {
319 throw failure;
321 return connectionString.Trim();
324 [ResourceExposure(ResourceScope.None)] // reads OleDbServices value for the provider
325 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
326 private string ValidateConnectionString(string connectionString) {
327 if (ConvertValueToBoolean(KEY.Asynchronous_Processing, false)) {
328 throw ODB.AsynchronousNotSupported();
331 int connectTimeout = ConvertValueToInt32(KEY.Connect_Timeout, 0);
332 if (connectTimeout < 0) {
333 throw ADP.InvalidConnectTimeoutValue();
336 string progid = ConvertValueToString(KEY.Data_Provider, null); // MDAC 71923
337 if (null != progid) {
338 progid = progid.Trim();
339 if (0 < progid.Length) { // don't fail on empty 'Data Provider' value
340 ValidateProvider(progid);
343 progid = ConvertValueToString(KEY.RemoteProvider, null); // MDAC 71923
344 if (null != progid) {
345 progid = progid.Trim();
346 if (0 < progid.Length) { // don't fail on empty 'Data Provider' value
347 ValidateProvider(progid);
350 progid = ConvertValueToString(KEY.Provider, ADP.StrEmpty).Trim();
351 ValidateProvider(progid); // will fail on empty 'Provider' value
353 // SQLBU VSTS 59322: initialize to default
354 // If the value is not provided in connection string and OleDbServices registry key has not been set by the provider,
355 // the default for the provider is -1 (all services are ON).
356 // our default is -13, we turn off ODB.DBPROPVAL_OS_AGR_AFTERSESSION and ODB.DBPROPVAL_OS_CLIENTCURSOR flags
357 _oledbServices = DbConnectionStringDefaults.OleDbServices;
359 bool hasOleDBServices = (base.ContainsKey(KEY.Ole_DB_Services) && !ADP.IsEmpty((string)base[KEY.Ole_DB_Services]));
360 if (!hasOleDBServices) { // don't touch registry if they have OLE DB Services
361 string classid = (string) ADP.ClassesRootRegistryValue(progid + "\\CLSID", String.Empty);
362 if ((null != classid) && (0 < classid.Length)) {
363 // CLSID detection of 'Microsoft OLE DB Provider for ODBC Drivers'
364 Guid classidProvider = new Guid(classid);
365 if (ODB.CLSID_MSDASQL == classidProvider) {
366 throw ODB.MSDASQLNotSupported();
368 object tmp = ADP.ClassesRootRegistryValue("CLSID\\{" + classidProvider.ToString("D", CultureInfo.InvariantCulture) + "}", ODB.OLEDB_SERVICES);
369 if (null != tmp) {
371 // @devnote: some providers like MSDataShape don't have the OLEDB_SERVICES value
372 // the MSDataShape provider doesn't support the 'Ole Db Services' keyword
373 // hence, if the value doesn't exist - don't prepend to string
374 try {
375 _oledbServices = (int)tmp;
377 catch(InvalidCastException e) {
378 ADP.TraceExceptionWithoutRethrow(e);
380 _oledbServices &= ~(ODB.DBPROPVAL_OS_AGR_AFTERSESSION | ODB.DBPROPVAL_OS_CLIENTCURSOR); // NT 347436, MDAC 58606
382 StringBuilder builder = new StringBuilder();
383 builder.Append(KEY.Ole_DB_Services);
384 builder.Append("=");
385 builder.Append(_oledbServices.ToString(CultureInfo.InvariantCulture));
386 builder.Append(";");
387 builder.Append(connectionString);
388 connectionString = builder.ToString();
392 else {
393 // SQLBU VSTS 59322: parse the Ole Db Services value from connection string
394 _oledbServices = ConvertValueToInt32(KEY.Ole_DB_Services, DbConnectionStringDefaults.OleDbServices);
397 return connectionString;
400 internal static bool IsMSDASQL(string progid) {
401 return (("msdasql" == progid) || progid.StartsWith("msdasql.", StringComparison.Ordinal) || ("microsoft ole db provider for odbc drivers" == progid));
404 static private void ValidateProvider(string progid) {
405 if (ADP.IsEmpty(progid)) {
406 throw ODB.NoProviderSpecified();
408 if (ODB.MaxProgIdLength <= progid.Length) { // MDAC 63151
409 throw ODB.InvalidProviderSpecified();
411 progid = progid.ToLower(CultureInfo.InvariantCulture);
412 if (IsMSDASQL(progid)) {
413 // fail msdasql even if not on the machine.
414 throw ODB.MSDASQLNotSupported();
418 static internal void ReleaseObjectPool() {
419 UDL._PoolSizeInit = false;
420 UDL._Pool = null;