1 //------------------------------------------------------------------------------
2 // <copyright file="oledbconnectionstring.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System
.Data
.OleDb
{
12 using System
.Collections
;
13 using System
.Collections
.Generic
;
14 using System
.Collections
.Specialized
;
16 using System
.Data
.Common
;
17 using System
.Diagnostics
;
18 using System
.Globalization
;
20 using System
.Security
;
21 using System
.Security
.Permissions
;
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";
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
]));
105 string udlConnectionString
= null;
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
{
142 Debug
.Assert(!ADP
.IsEmpty(this[KEY
.Provider
]), "no Provider");
143 return this[KEY
.Provider
];
147 internal int OleDbServices
{
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");
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
;
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;
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;
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
)]
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;
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
;
260 UDL
._Pool
= udlcache
;
265 if (null != 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:
282 // ; Everything after this line is an OLE DB initstring
284 string connectionString
= null;
285 Exception failure
= null;
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();
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
);
312 if (!ADP
.IsCatchableExceptionType(e
)) {
316 throw ADP
.UdlFileError(e
);
318 if (null != 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
);
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
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
);
385 builder
.Append(_oledbServices
.ToString(CultureInfo
.InvariantCulture
));
387 builder
.Append(connectionString
);
388 connectionString
= builder
.ToString();
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;