1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbConnection.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.Generic;
13 using System.ComponentModel;
15 using System.Data.Common;
16 using System.Data.ProviderBase;
17 using System.Diagnostics;
18 using System.Diagnostics.CodeAnalysis;
19 using System.Globalization;
21 using System.Runtime.InteropServices;
22 using System.Security;
23 using System.Security.Permissions;
25 using System.Threading;
26 using SysTx = System.Transactions;
28 // wraps the OLEDB IDBInitialize interface which represents a connection
29 // Notes about connection pooling
30 // 1. Connection pooling isn't supported on Win95
31 // 2. Only happens if we use the IDataInitialize or IDBPromptInitialize interfaces
32 // it won't happen if you directly create the provider and set its properties
33 // 3. First call on IDBInitialize must be Initialize, can't QI for any other interfaces before that
34 [DefaultEvent("InfoMessage")]
35 public sealed partial class OleDbConnection : DbConnection, ICloneable, IDbConnection {
37 static private readonly object EventInfoMessage = new object();
39 public OleDbConnection(string connectionString) : this() {
40 ConnectionString = connectionString;
43 private OleDbConnection(OleDbConnection connection) : this() { // Clone
49 #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
50 RecommendedAsConfigurable(true),
51 #pragma warning restore 618
52 SettingsBindableAttribute(true),
53 RefreshProperties(RefreshProperties.All),
54 ResCategoryAttribute(Res.DataCategory_Data),
55 Editor("Microsoft.VSDesigner.Data.ADO.Design.OleDbConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
56 ResDescriptionAttribute(Res.OleDbConnection_ConnectionString),
58 override public string ConnectionString {
60 return ConnectionString_Get();
63 ConnectionString_Set(value);
67 private OleDbConnectionString OleDbConnectionStringValue {
68 get { return (OleDbConnectionString)ConnectionOptions; }
72 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
73 ResDescriptionAttribute(Res.OleDbConnection_ConnectionTimeout),
75 override public int ConnectionTimeout {
78 Bid.ScopeEnter(out hscp, "<oledb.OleDbConnection.get_ConnectionTimeout|API> %d#\n", ObjectID);
82 value = GetDataSourceValue(OleDbPropertySetGuid.DBInit, ODB.DBPROP_INIT_TIMEOUT);
85 OleDbConnectionString constr = this.OleDbConnectionStringValue;
86 value = (null != constr) ? constr.ConnectTimeout : ADP.DefaultConnectionTimeout;
89 return Convert.ToInt32(value, CultureInfo.InvariantCulture);
92 return ADP.DefaultConnectionTimeout;
96 Bid.ScopeLeave(ref hscp);
102 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
103 ResDescriptionAttribute(Res.OleDbConnection_Database),
105 override public string Database {
108 Bid.ScopeEnter(out hscp, "<oledb.OleDbConnection.get_Database|API> %d#\n", ObjectID);
110 OleDbConnectionString constr = (OleDbConnectionString)UserConnectionOptions;
111 object value = (null != constr) ? constr.InitialCatalog : ADP.StrEmpty;
112 if ((null != value) && !((string)value).StartsWith(DbConnectionOptions.DataDirectory, StringComparison.OrdinalIgnoreCase)) {
113 OleDbConnectionInternal connection = GetOpenConnection();
114 if (null != connection) {
115 if (connection.HasSession) {
116 value = GetDataSourceValue(OleDbPropertySetGuid.DataSource, ODB.DBPROP_CURRENTCATALOG);
119 value = GetDataSourceValue(OleDbPropertySetGuid.DBInit, ODB.DBPROP_INIT_CATALOG);
123 constr = this.OleDbConnectionStringValue;
124 value = (null != constr) ? constr.InitialCatalog : ADP.StrEmpty;
127 return Convert.ToString(value, CultureInfo.InvariantCulture);
130 Bid.ScopeLeave(ref hscp);
137 ResDescriptionAttribute(Res.OleDbConnection_DataSource),
139 override public string DataSource {
142 Bid.ScopeEnter(out hscp, "<oledb.OleDbConnection.get_DataSource|API> %d#\n", ObjectID);
144 OleDbConnectionString constr = (OleDbConnectionString)UserConnectionOptions;
145 object value = (null != constr) ? constr.DataSource : ADP.StrEmpty;
146 if ((null != value) && !((string)value).StartsWith(DbConnectionOptions.DataDirectory, StringComparison.OrdinalIgnoreCase)) {
148 value = GetDataSourceValue(OleDbPropertySetGuid.DBInit, ODB.DBPROP_INIT_DATASOURCE);
149 if ((null == value) || ((value is string) && (0 == (value as string).Length))) {
150 value = GetDataSourceValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_DATASOURCENAME); // MDAC 76248
154 constr = this.OleDbConnectionStringValue;
155 value = (null != constr) ? constr.DataSource : ADP.StrEmpty;
158 return Convert.ToString(value, CultureInfo.InvariantCulture);
161 Bid.ScopeLeave(ref hscp);
166 internal bool IsOpen {
167 get { return (null != GetOpenConnection()); }
170 internal OleDbTransaction LocalTransaction {
172 OleDbConnectionInternal openConnection = GetOpenConnection();
174 if (null != openConnection) {
175 openConnection.LocalTransaction = value;
182 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
183 ResCategoryAttribute(Res.DataCategory_Data),
184 ResDescriptionAttribute(Res.OleDbConnection_Provider),
186 public String Provider {
188 Bid.Trace("<oledb.OleDbConnection.get_Provider|API> %d#\n", ObjectID);
189 OleDbConnectionString constr = this.OleDbConnectionStringValue;
190 string value = ((null != constr) ? constr.ConvertValueToString(ODB.Provider, null) : null);
191 return ((null != value) ? value : ADP.StrEmpty);
195 internal OleDbConnectionPoolGroupProviderInfo ProviderInfo {
197 return (OleDbConnectionPoolGroupProviderInfo)PoolGroup.ProviderInfo;
202 ResDescriptionAttribute(Res.OleDbConnection_ServerVersion),
204 override public string ServerVersion { // MDAC 55481
206 return InnerConnection.ServerVersion;
212 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
213 ResDescriptionAttribute(Res.DbConnection_State),
215 override public ConnectionState State {
217 return InnerConnection.State;
222 EditorBrowsableAttribute(EditorBrowsableState.Advanced),
224 public void ResetState() { // MDAC 58606
226 Bid.ScopeEnter(out hscp, "<oledb.OleDbCommand.ResetState|API> %d#\n", ObjectID);
229 object value = GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_CONNECTIONSTATUS);
230 if (value is Int32) {
231 int connectionStatus = (int) value;
232 switch (connectionStatus) {
233 case ODB.DBPROPVAL_CS_UNINITIALIZED: // provider closed on us
234 case ODB.DBPROPVAL_CS_COMMUNICATIONFAILURE: // broken connection
235 GetOpenConnection().DoomThisConnection();
236 NotifyWeakReference(OleDbReferenceCollection.Canceling); // MDAC 71435
240 case ODB.DBPROPVAL_CS_INITIALIZED: // everything is okay
243 default: // have to assume everything is okay
244 Debug.Assert(false, "Unknown 'Connection Status' value " + connectionStatus.ToString("G", CultureInfo.InvariantCulture));
251 Bid.ScopeLeave(ref hscp);
256 ResCategoryAttribute(Res.DataCategory_InfoMessage),
257 ResDescriptionAttribute(Res.DbConnection_InfoMessage),
259 public event OleDbInfoMessageEventHandler InfoMessage {
261 Events.AddHandler(EventInfoMessage, value);
264 Events.RemoveHandler(EventInfoMessage, value);
268 internal UnsafeNativeMethods.ICommandText ICommandText() {
269 Debug.Assert(null != GetOpenConnection(), "ICommandText closed");
270 return GetOpenConnection().ICommandText();
273 private IDBPropertiesWrapper IDBProperties() {
274 Debug.Assert(null != GetOpenConnection(), "IDBProperties closed");
275 return GetOpenConnection().IDBProperties();
278 internal IOpenRowsetWrapper IOpenRowset() {
279 Debug.Assert(null != GetOpenConnection(), "IOpenRowset closed");
280 return GetOpenConnection().IOpenRowset();
283 internal int SqlSupport() {
284 Debug.Assert(null != this.OleDbConnectionStringValue, "no OleDbConnectionString SqlSupport");
285 return this.OleDbConnectionStringValue.GetSqlSupport(this);
288 internal bool SupportMultipleResults() {
289 Debug.Assert(null != this.OleDbConnectionStringValue, "no OleDbConnectionString SupportMultipleResults");
290 return this.OleDbConnectionStringValue.GetSupportMultipleResults(this);
293 internal bool SupportIRow(OleDbCommand cmd) { // MDAC 72902
294 Debug.Assert(null != this.OleDbConnectionStringValue, "no OleDbConnectionString SupportIRow");
295 return this.OleDbConnectionStringValue.GetSupportIRow(this, cmd);
298 internal int QuotedIdentifierCase() { // MDAC 67385
299 Debug.Assert(null != this.OleDbConnectionStringValue, "no OleDbConnectionString QuotedIdentifierCase");
301 int quotedIdentifierCase;
302 object value = GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_QUOTEDIDENTIFIERCASE);
303 if (value is Int32) {// not OleDbPropertyStatus
304 quotedIdentifierCase = (int) value;
307 quotedIdentifierCase = -1;
309 return quotedIdentifierCase;
312 new public OleDbTransaction BeginTransaction() {
313 return BeginTransaction(IsolationLevel.Unspecified);
316 new public OleDbTransaction BeginTransaction(IsolationLevel isolationLevel) {
317 return (OleDbTransaction)InnerConnection.BeginTransaction(isolationLevel);
320 override public void ChangeDatabase(string value) {
321 OleDbConnection.ExecutePermission.Demand();
324 Bid.ScopeEnter(out hscp, "<oledb.OleDbConnection.ChangeDatabase|API> %d#, value='%ls'\n", ObjectID, value);
326 CheckStateOpen(ADP.ChangeDatabase);
327 if ((null == value) || (0 == value.Trim().Length)) { // MDAC 62679
328 throw ADP.EmptyDatabaseName();
330 SetDataSourcePropertyValue(OleDbPropertySetGuid.DataSource, ODB.DBPROP_CURRENTCATALOG, ODB.Current_Catalog, true, value);
333 Bid.ScopeLeave(ref hscp);
337 internal void CheckStateOpen(string method) {
338 ConnectionState state = State;
339 if (ConnectionState.Open != state) {
340 throw ADP.OpenConnectionRequired(method, state);
344 object ICloneable.Clone() {
345 OleDbConnection clone = new OleDbConnection(this);
346 Bid.Trace("<oledb.OleDbConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
350 override public void Close() {
351 InnerConnection.CloseConnection(this, ConnectionFactory);
352 // does not require GC.KeepAlive(this) because of OnStateChange
355 new public OleDbCommand CreateCommand() {
356 return new OleDbCommand("", this);
359 private void DisposeMe(bool disposing) { // MDAC 65459
360 if (disposing) { // release mananged objects
362 // release the object pool in design-mode so that
363 // native MDAC can be properly released during shutdown
364 OleDbConnection.ReleaseObjectPool();
370 // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
371 [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
372 override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
375 Bid.ScopeEnter(out hscp, "<prov.OleDbConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
378 DbTransaction transaction = InnerConnection.BeginTransaction(isolationLevel);
380 // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and
381 // subsequently leaves open the possibility that the outer connection could be GC'ed before the DbTransaction
382 // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
383 // until the completion of BeginTransaction with KeepAlive
389 Bid.ScopeLeave(ref hscp);
393 public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) {
394 EnlistDistributedTransactionHelper(transaction);
397 internal object GetDataSourcePropertyValue(Guid propertySet, int propertyID) {
398 OleDbConnectionInternal connection = GetOpenConnection();
399 return connection.GetDataSourcePropertyValue(propertySet, propertyID);
402 internal object GetDataSourceValue(Guid propertySet, int propertyID) {
403 object value = GetDataSourcePropertyValue(propertySet, propertyID);
404 if ((value is OleDbPropertyStatus) || Convert.IsDBNull(value)) {
410 private OleDbConnectionInternal GetOpenConnection() {
411 DbConnectionInternal innerConnection = InnerConnection;
412 return (innerConnection as OleDbConnectionInternal);
415 internal void GetLiteralQuotes(string method, out string quotePrefix, out string quoteSuffix) {
416 CheckStateOpen(method);
417 OleDbConnectionPoolGroupProviderInfo info = ProviderInfo;
418 if (info.HasQuoteFix) {
419 quotePrefix = info.QuotePrefix;
420 quoteSuffix = info.QuoteSuffix;
423 OleDbConnectionInternal connection = GetOpenConnection();
424 quotePrefix = connection.GetLiteralInfo(ODB.DBLITERAL_QUOTE_PREFIX);
425 quoteSuffix = connection.GetLiteralInfo(ODB.DBLITERAL_QUOTE_SUFFIX);
426 if (null == quotePrefix) {
429 if (null == quoteSuffix) {
430 quoteSuffix = quotePrefix;
432 info.SetQuoteFix(quotePrefix, quoteSuffix);
436 public DataTable GetOleDbSchemaTable(Guid schema, object[] restrictions) { // MDAC 61846
437 OleDbConnection.ExecutePermission.Demand();
440 Bid.ScopeEnter(out hscp, "<oledb.OleDbConnection.GetOleDbSchemaTable|API> %d#, schema=%ls, restrictions\n", ObjectID, schema);
442 CheckStateOpen(ADP.GetOleDbSchemaTable);
443 OleDbConnectionInternal connection = GetOpenConnection();
445 if (OleDbSchemaGuid.DbInfoLiterals == schema) {
446 if ((null == restrictions) || (0 == restrictions.Length)) {
447 return connection.BuildInfoLiterals();
449 throw ODB.InvalidRestrictionsDbInfoLiteral("restrictions");
451 else if (OleDbSchemaGuid.SchemaGuids == schema) {
452 if ((null == restrictions) || (0 == restrictions.Length)) {
453 return connection.BuildSchemaGuids();
455 throw ODB.InvalidRestrictionsSchemaGuids("restrictions");
457 else if (OleDbSchemaGuid.DbInfoKeywords == schema) {
458 if ((null == restrictions) || (0 == restrictions.Length)) {
459 return connection.BuildInfoKeywords();
461 throw ODB.InvalidRestrictionsDbInfoKeywords("restrictions");
464 if (connection.SupportSchemaRowset(schema)) {
465 return connection.GetSchemaRowset(schema, restrictions);
468 using(IDBSchemaRowsetWrapper wrapper = connection.IDBSchemaRowset()) {
469 if (null == wrapper.Value) {
470 throw ODB.SchemaRowsetsNotSupported(Provider); // MDAC 72689
473 throw ODB.NotSupportedSchemaTable(schema, this); // MDAC 63279
477 Bid.ScopeLeave(ref hscp);
481 internal DataTable GetSchemaRowset(Guid schema, object[] restrictions) {
482 Debug.Assert(null != GetOpenConnection(), "GetSchemaRowset closed");
483 return GetOpenConnection().GetSchemaRowset(schema, restrictions);
486 internal bool HasLiveReader(OleDbCommand cmd) {
488 OleDbConnectionInternal openConnection = GetOpenConnection();
490 if (null != openConnection) {
491 result = openConnection.HasLiveReader(cmd);
496 internal void OnInfoMessage(UnsafeNativeMethods.IErrorInfo errorInfo, OleDbHResult errorCode) {
497 OleDbInfoMessageEventHandler handler = (OleDbInfoMessageEventHandler) Events[EventInfoMessage];
498 if (null != handler) {
500 OleDbException exception = OleDbException.CreateException(errorInfo, errorCode, null);
501 OleDbInfoMessageEventArgs e = new OleDbInfoMessageEventArgs(exception);
503 Bid.Trace("<oledb.OledbConnection.OnInfoMessage|API|INFO> %d#, Message='%ls'\n", ObjectID, e.Message);
507 catch (Exception e) { // eat the exception
509 if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
513 ADP.TraceExceptionWithoutRethrow(e);
518 OleDbException exception = OleDbException.CreateException(errorInfo, errorCode, null);
519 Bid.Trace("<oledb.OledbConnection.OnInfoMessage|API|INFO> %d#, Message='%ls'\n", ObjectID, exception.Message);
524 override public void Open() {
525 InnerConnection.OpenConnection(this, ConnectionFactory);
527 // SQLBUDT #276132 - need to manually enlist in some cases, because
528 // native OLE DB doesn't know about SysTx transactions.
529 if ((0!=(ODB.DBPROPVAL_OS_TXNENLISTMENT & ((OleDbConnectionString)(this.ConnectionOptions)).OleDbServices))
530 && ADP.NeedManualEnlistment()) {
531 GetOpenConnection().EnlistTransactionInternal(SysTx.Transaction.Current);
535 internal void SetDataSourcePropertyValue(Guid propertySet, int propertyID, string description, bool required, object value) {
536 CheckStateOpen(ADP.SetProperties);
538 using(IDBPropertiesWrapper idbProperties = IDBProperties()) {
539 using(DBPropSet propSet = DBPropSet.CreateProperty(propertySet, propertyID, required, value)) {
541 Bid.Trace("<oledb.IDBProperties.SetProperties|API|OLEDB> %d#\n", ObjectID);
542 hr = idbProperties.Value.SetProperties(propSet.PropertySetCount, propSet);
543 Bid.Trace("<oledb.IDBProperties.SetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr);
546 Exception e = OleDbConnection.ProcessResults(hr, null, this);
547 if (OleDbHResult.DB_E_ERRORSOCCURRED == hr) {
549 StringBuilder builder = new StringBuilder();
550 Debug.Assert(1 == propSet.PropertySetCount, "too many PropertySets");
552 tagDBPROP[] dbprops = propSet.GetPropertySet(0, out propertySet);
553 Debug.Assert(1 == dbprops.Length, "too many Properties");
555 ODB.PropsetSetFailure(builder, description, dbprops[0].dwStatus);
557 e = ODB.PropsetSetFailure(builder.ToString(), e);
564 SafeNativeMethods.Wrapper.ClearErrorInfo();
570 internal bool SupportSchemaRowset(Guid schema) {
571 return GetOpenConnection().SupportSchemaRowset(schema);
574 internal OleDbTransaction ValidateTransaction(OleDbTransaction transaction, string method) {
575 return GetOpenConnection().ValidateTransaction(transaction, method);
578 static internal Exception ProcessResults(OleDbHResult hresult, OleDbConnection connection, object src) {
579 if ((0 <= (int)hresult) && ((null == connection) || (null == connection.Events[EventInfoMessage]))) {
580 SafeNativeMethods.Wrapper.ClearErrorInfo();
584 // ErrorInfo object is to be checked regardless the hresult returned by the function called
586 UnsafeNativeMethods.IErrorInfo errorInfo = null;
587 OleDbHResult hr = UnsafeNativeMethods.GetErrorInfo(0, out errorInfo); // 0 - IErrorInfo exists, 1 - no IErrorInfo
588 if ((OleDbHResult.S_OK == hr) && (null != errorInfo)) {
596 e = OleDbException.CreateException(errorInfo, hresult, null);
599 if (OleDbHResult.DB_E_OBJECTOPEN == hresult) {
600 e = ADP.OpenReaderExists(e);
603 ResetState(connection);
605 else if (null != connection) {
606 connection.OnInfoMessage(errorInfo, hresult);
609 Bid.Trace("<oledb.OledbConnection|WARN|INFO> ErrorInfo available, but not connection %08X{HRESULT}\n", hresult);
611 Marshal.ReleaseComObject(errorInfo);
613 else if (0 < hresult) {
614 // @devnote: OnInfoMessage with no ErrorInfo
615 Bid.Trace("<oledb.OledbConnection|ERR|INFO> ErrorInfo not available %08X{HRESULT}\n", hresult);
617 else if ((int)hresult < 0) {
618 e = ODB.NoErrorInformation((null != connection) ? connection.Provider : null, hresult, null); // OleDbException
620 ResetState(connection);
623 ADP.TraceExceptionAsReturnValue(e);
628 // @devnote: should be multithread safe
629 static public void ReleaseObjectPool() {
630 (new OleDbPermission(PermissionState.Unrestricted)).Demand();
633 Bid.ScopeEnter(out hscp, "<oledb.OleDbConnection.ReleaseObjectPool|API>\n");
635 OleDbConnectionString.ReleaseObjectPool();
636 OleDbConnectionInternal.ReleaseObjectPool();
637 OleDbConnectionFactory.SingletonInstance.ClearAllPools();
640 Bid.ScopeLeave(ref hscp);
644 static private void ResetState(OleDbConnection connection) {
645 if (null != connection) {
646 connection.ResetState();