//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // Microsoft // Microsoft //------------------------------------------------------------------------------ using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using SysTx = System.Transactions; namespace System.Data.Odbc { [DefaultEvent("InfoMessage")] public sealed partial class OdbcConnection : DbConnection, ICloneable { private int connectionTimeout = ADP.DefaultConnectionTimeout; private OdbcInfoMessageEventHandler infoMessageEventHandler; private WeakReference weakTransaction; private OdbcConnectionHandle _connectionHandle; private ConnectionState _extraState; // extras, like Executing and Fetching, that we add to the State. public OdbcConnection(string connectionString) : this() { ConnectionString = connectionString; } private OdbcConnection(OdbcConnection connection) : this() { // Clone CopyFrom(connection); connectionTimeout = connection.connectionTimeout; } internal OdbcConnectionHandle ConnectionHandle { get { return _connectionHandle; } set { Debug.Assert(null == _connectionHandle, "reopening a connection?"); _connectionHandle = value; } } [ DefaultValue(""), Editor("Microsoft.VSDesigner.Data.Odbc.Design.OdbcConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute RecommendedAsConfigurable(true), #pragma warning restore 618 SettingsBindableAttribute(true), RefreshProperties(RefreshProperties.All), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.OdbcConnection_ConnectionString), ] override public string ConnectionString { get { return ConnectionString_Get(); } set { ConnectionString_Set(value); } } [ DefaultValue(ADP.DefaultConnectionTimeout), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.OdbcConnection_ConnectionTimeout), ] new public int ConnectionTimeout { get { return connectionTimeout; } set { if (value < 0) throw ODBC.NegativeArgument(); if (IsOpen) throw ODBC.CantSetPropertyOnOpenConnection(); connectionTimeout = value; } } [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(Res.OdbcConnection_Database), ] override public string Database { get { if (IsOpen && !ProviderInfo.NoCurrentCatalog) { //Note: CURRENT_CATALOG may not be supported by the current driver. In which //case we ignore any error (without throwing), and just return string.empty. //As we really don't want people to have to have try/catch around simple properties return GetConnectAttrString(ODBC32.SQL_ATTR.CURRENT_CATALOG); } //Database is not available before open, and its not worth parsing the //connection string over. return String.Empty; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(Res.OdbcConnection_DataSource), ] override public string DataSource { get { if (IsOpen) { // note: This will return an empty string if the driver keyword was used to connect // see ODBC3.0 Programmers Reference, SQLGetInfo // return GetInfoStringUnhandled(ODBC32.SQL_INFO.SERVER_NAME, true); } return String.Empty; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(Res.OdbcConnection_ServerVersion), ] override public string ServerVersion { get { return InnerConnection.ServerVersion; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(Res.DbConnection_State), ] override public ConnectionState State { get { return InnerConnection.State; } } internal OdbcConnectionPoolGroupProviderInfo ProviderInfo { get { Debug.Assert(null != this.PoolGroup, "PoolGroup must never be null when accessing ProviderInfo"); return (OdbcConnectionPoolGroupProviderInfo)this.PoolGroup.ProviderInfo; } } internal ConnectionState InternalState { get { return (this.State | _extraState); } } internal bool IsOpen { get { return (InnerConnection is OdbcConnectionOpen); } } internal OdbcTransaction LocalTransaction { get { OdbcTransaction result = null; if (null != weakTransaction) { result = ((OdbcTransaction)weakTransaction.Target); } return result; } set { weakTransaction = null; if (null != value) { weakTransaction = new WeakReference((OdbcTransaction)value); } } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(Res.OdbcConnection_Driver), ] public string Driver { get { if(IsOpen) { if (ProviderInfo.DriverName == null) { ProviderInfo.DriverName = GetInfoStringUnhandled(ODBC32.SQL_INFO.DRIVER_NAME); } return ProviderInfo.DriverName; } return ADP.StrEmpty; } } internal bool IsV3Driver { get { if (ProviderInfo.DriverVersion == null) { ProviderInfo.DriverVersion = GetInfoStringUnhandled(ODBC32.SQL_INFO.DRIVER_ODBC_VER); // protected against null and index out of range. Number cannot be bigger than 99 if (ProviderInfo.DriverVersion != null && ProviderInfo.DriverVersion.Length>=2) { try { // mdac 89269: driver may return malformatted string ProviderInfo.IsV3Driver = (int.Parse(ProviderInfo.DriverVersion.Substring(0,2), CultureInfo.InvariantCulture) >= 3); } catch (System.FormatException e) { ProviderInfo.IsV3Driver = false; ADP.TraceExceptionWithoutRethrow(e); } } else { ProviderInfo.DriverVersion = ""; } } return ProviderInfo.IsV3Driver; } } [ ResCategoryAttribute(Res.DataCategory_InfoMessage), ResDescriptionAttribute(Res.DbConnection_InfoMessage), ] public event OdbcInfoMessageEventHandler InfoMessage { add { infoMessageEventHandler += value; } remove { infoMessageEventHandler -= value; } } internal char EscapeChar(string method) { CheckState(method); if (!ProviderInfo.HasEscapeChar) { string escapeCharString; escapeCharString = GetInfoStringUnhandled(ODBC32.SQL_INFO.SEARCH_PATTERN_ESCAPE); Debug.Assert((escapeCharString.Length <= 1), "Can't handle multichar quotes"); ProviderInfo.EscapeChar = (escapeCharString.Length==1)?escapeCharString[0]:QuoteChar(method)[0]; } return ProviderInfo.EscapeChar; } internal string QuoteChar(string method) { CheckState(method); if (!ProviderInfo.HasQuoteChar) { string quoteCharString; quoteCharString = GetInfoStringUnhandled(ODBC32.SQL_INFO.IDENTIFIER_QUOTE_CHAR); Debug.Assert((quoteCharString.Length <= 1), "Can't handle multichar quotes"); ProviderInfo.QuoteChar = (1==quoteCharString.Length)?quoteCharString:"\0"; } return ProviderInfo.QuoteChar; } new public OdbcTransaction BeginTransaction() { return BeginTransaction(IsolationLevel.Unspecified); } new public OdbcTransaction BeginTransaction(IsolationLevel isolevel) { return (OdbcTransaction)InnerConnection.BeginTransaction(isolevel); } private void RollbackDeadTransaction() { WeakReference weak = weakTransaction; if ((null != weak) && !weak.IsAlive) { weakTransaction = null; ConnectionHandle.CompleteTransaction(ODBC32.SQL_ROLLBACK); } } override public void ChangeDatabase(string value) { InnerConnection.ChangeDatabase(value); } internal void CheckState(string method) { ConnectionState state = InternalState; if (ConnectionState.Open != state) { throw ADP.OpenConnectionRequired(method, state); // MDAC 68323 } } object ICloneable.Clone() { OdbcConnection clone = new OdbcConnection(this); Bid.Trace(" %d#, clone=%d#\n", ObjectID, clone.ObjectID); return clone; } internal bool ConnectionIsAlive(Exception innerException) { if (IsOpen) { if (!ProviderInfo.NoConnectionDead) { int isDead = GetConnectAttr(ODBC32.SQL_ATTR.CONNECTION_DEAD, ODBC32.HANDLER.IGNORE); if (ODBC32.SQL_CD_TRUE == isDead) { Close(); throw ADP.ConnectionIsDisabled(innerException); } } // else connection is still alive or attribute not supported return true; } return false; } new public OdbcCommand CreateCommand() { return new OdbcCommand(String.Empty, this); } internal OdbcStatementHandle CreateStatementHandle() { return new OdbcStatementHandle(ConnectionHandle); } override public void Close() { InnerConnection.CloseConnection(this, ConnectionFactory); OdbcConnectionHandle connectionHandle = _connectionHandle; if (null != connectionHandle) { _connectionHandle = null; // If there is a pending transaction, automatically rollback. WeakReference weak = this.weakTransaction; if (null != weak) { this.weakTransaction = null; IDisposable transaction = weak.Target as OdbcTransaction; if ((null != transaction) && weak.IsAlive) { transaction.Dispose(); } // else transaction will be rolled back when handle is disposed } connectionHandle.Dispose(); } } private void DisposeMe(bool disposing) { // MDAC 65459 } public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) { EnlistDistributedTransactionHelper(transaction); } internal string GetConnectAttrString(ODBC32.SQL_ATTR attribute) { string value = ""; Int32 cbActual = 0; byte[] buffer = new byte[100]; OdbcConnectionHandle connectionHandle = ConnectionHandle; if (null != connectionHandle) { ODBC32.RetCode retcode = connectionHandle.GetConnectionAttribute(attribute, buffer, out cbActual); if (buffer.Length+2 <= cbActual) { // 2 bytes for unicode null-termination character // retry with cbActual because original buffer was too small buffer = new byte[cbActual + 2]; retcode = connectionHandle.GetConnectionAttribute(attribute, buffer, out cbActual); } if ((ODBC32.RetCode.SUCCESS == retcode) || (ODBC32.RetCode.SUCCESS_WITH_INFO == retcode)) { value = Encoding.Unicode.GetString(buffer, 0, Math.Min(cbActual, buffer.Length)); } else if (retcode == ODBC32.RetCode.ERROR) { string sqlstate = GetDiagSqlState(); if (("HYC00" == sqlstate) || ("HY092" == sqlstate) || ("IM001" == sqlstate)) { FlagUnsupportedConnectAttr(attribute); } // not throwing errors if not supported or other failure } } return value; } internal int GetConnectAttr(ODBC32.SQL_ATTR attribute, ODBC32.HANDLER handler) { Int32 retval = -1; Int32 cbActual = 0; byte[] buffer = new byte[4]; OdbcConnectionHandle connectionHandle = ConnectionHandle; if (null != connectionHandle) { ODBC32.RetCode retcode = connectionHandle.GetConnectionAttribute(attribute, buffer, out cbActual); if ((ODBC32.RetCode.SUCCESS == retcode) || (ODBC32.RetCode.SUCCESS_WITH_INFO == retcode)) { retval = BitConverter.ToInt32(buffer, 0); } else { if (retcode == ODBC32.RetCode.ERROR) { string sqlstate = GetDiagSqlState(); if (("HYC00" == sqlstate) || ("HY092" == sqlstate) || ("IM001" == sqlstate)) { FlagUnsupportedConnectAttr(attribute); } } if (handler == ODBC32.HANDLER.THROW) { this.HandleError(connectionHandle, retcode); } } } return retval; } private string GetDiagSqlState () { OdbcConnectionHandle connectionHandle = ConnectionHandle; string sqlstate; connectionHandle.GetDiagnosticField(out sqlstate); return sqlstate; } internal ODBC32.RetCode GetInfoInt16Unhandled(ODBC32.SQL_INFO info, out Int16 resultValue) { byte[] buffer = new byte[2]; ODBC32.RetCode retcode = ConnectionHandle.GetInfo1(info, buffer); resultValue = BitConverter.ToInt16(buffer, 0); return retcode; } internal ODBC32.RetCode GetInfoInt32Unhandled(ODBC32.SQL_INFO info, out Int32 resultValue) { byte[] buffer = new byte[4]; ODBC32.RetCode retcode = ConnectionHandle.GetInfo1(info, buffer); resultValue = BitConverter.ToInt32(buffer, 0); return retcode; } private Int32 GetInfoInt32Unhandled(ODBC32.SQL_INFO infotype) { byte[] buffer = new byte[4]; ConnectionHandle.GetInfo1(infotype, buffer); return BitConverter.ToInt32(buffer, 0); } internal string GetInfoStringUnhandled(ODBC32.SQL_INFO info) { return GetInfoStringUnhandled(info, false); } private string GetInfoStringUnhandled(ODBC32.SQL_INFO info, bool handleError) { //SQLGetInfo string value = null; Int16 cbActual = 0; byte[] buffer = new byte[100]; OdbcConnectionHandle connectionHandle = ConnectionHandle; if (null != connectionHandle) { ODBC32.RetCode retcode = connectionHandle.GetInfo2(info, buffer, out cbActual); if (buffer.Length < cbActual-2) { // 2 bytes for unicode null-termination character // retry with cbActual because original buffer was too small buffer = new byte[cbActual + 2]; retcode = connectionHandle.GetInfo2(info, buffer, out cbActual); } if (retcode == ODBC32.RetCode.SUCCESS || retcode == ODBC32.RetCode.SUCCESS_WITH_INFO) { value = Encoding.Unicode.GetString(buffer, 0, Math.Min(cbActual, buffer.Length)); } else if (handleError) { this.HandleError(ConnectionHandle, retcode); } } else if (handleError) { value = ""; } return value; } // non-throwing HandleError internal Exception HandleErrorNoThrow(OdbcHandle hrHandle, ODBC32.RetCode retcode) { Debug.Assert(retcode!=ODBC32.RetCode.INVALID_HANDLE, "retcode must never be ODBC32.RetCode.INVALID_HANDLE"); switch(retcode) { case ODBC32.RetCode.SUCCESS: break; case ODBC32.RetCode.SUCCESS_WITH_INFO: { //Optimize to only create the event objects and obtain error info if //the user is really interested in retriveing the events... if (infoMessageEventHandler != null) { OdbcErrorCollection errors = ODBC32.GetDiagErrors(null, hrHandle, retcode); errors.SetSource(this.Driver); OnInfoMessage(new OdbcInfoMessageEventArgs(errors)); } break; } default: OdbcException e = OdbcException.CreateException(ODBC32.GetDiagErrors(null, hrHandle, retcode), retcode); if (e != null) { e.Errors.SetSource(this.Driver); } ConnectionIsAlive(e); // this will close and throw if the connection is dead return (Exception)e; } return null; } internal void HandleError(OdbcHandle hrHandle, ODBC32.RetCode retcode) { Exception e = HandleErrorNoThrow(hrHandle, retcode); switch(retcode) { case ODBC32.RetCode.SUCCESS: case ODBC32.RetCode.SUCCESS_WITH_INFO: Debug.Assert(null == e, "success exception"); break; default: Debug.Assert(null != e, "failure without exception"); throw e; } } override public void Open() { InnerConnection.OpenConnection(this, ConnectionFactory); // SQLBUDT #276132 - need to manually enlist in some cases, because // native ODBC doesn't know about SysTx transactions. if (ADP.NeedManualEnlistment()) { EnlistTransaction(SysTx.Transaction.Current); } } private void OnInfoMessage(OdbcInfoMessageEventArgs args) { if (null != infoMessageEventHandler) { try { infoMessageEventHandler(this, args); } catch (Exception e) { // if (!ADP.IsCatchableOrSecurityExceptionType(e)) { throw; } ADP.TraceExceptionWithoutRethrow(e); } } } static public void ReleaseObjectPool() { (new OdbcPermission(PermissionState.Unrestricted)).Demand(); OdbcEnvironment.ReleaseObjectPool(); } internal OdbcTransaction SetStateExecuting(string method, OdbcTransaction transaction) { // MDAC 69003 if (null != weakTransaction) { // transaction may exist OdbcTransaction weak = (weakTransaction.Target as OdbcTransaction); if (transaction != weak) { // transaction doesn't exist if (null == transaction) { // transaction exists throw ADP.TransactionRequired(method); } if (this!= transaction.Connection) { // transaction can't have come from this connection throw ADP.TransactionConnectionMismatch(); } // if transaction is zombied, we don't know the original connection transaction = null; // MDAC 69264 } } else if (null != transaction) { // no transaction started if (null != transaction.Connection) { // transaction can't have come from this connection throw ADP.TransactionConnectionMismatch(); } // if transaction is zombied, we don't know the original connection transaction = null; // MDAC 69264 } ConnectionState state = InternalState; if (ConnectionState.Open != state) { NotifyWeakReference(OdbcReferenceCollection.Recover); // recover for a potentially finalized reader state = InternalState; if (ConnectionState.Open != state) { if (0 != (ConnectionState.Fetching & state)) { throw ADP.OpenReaderExists(); } throw ADP.OpenConnectionRequired(method, state); } } return transaction; } // This adds a type to the list of types that are supported by the driver // (don't need to know that for all the types) // internal void SetSupportedType (ODBC32.SQL_TYPE sqltype) { ODBC32.SQL_CVT sqlcvt; switch (sqltype) { case ODBC32.SQL_TYPE.NUMERIC: { sqlcvt = ODBC32.SQL_CVT.NUMERIC; break; } case ODBC32.SQL_TYPE.WCHAR: { sqlcvt = ODBC32.SQL_CVT.WCHAR; break; } case ODBC32.SQL_TYPE.WVARCHAR: { sqlcvt = ODBC32.SQL_CVT.WVARCHAR; break; } case ODBC32.SQL_TYPE.WLONGVARCHAR: { sqlcvt = ODBC32.SQL_CVT.WLONGVARCHAR; break; } default: // other types are irrelevant at this time return; } ProviderInfo.TestedSQLTypes |= (int)sqlcvt; ProviderInfo.SupportedSQLTypes |= (int)sqlcvt; } internal void FlagRestrictedSqlBindType(ODBC32.SQL_TYPE sqltype) { ODBC32.SQL_CVT sqlcvt; switch (sqltype) { case ODBC32.SQL_TYPE.NUMERIC: { sqlcvt = ODBC32.SQL_CVT.NUMERIC; break; } case ODBC32.SQL_TYPE.DECIMAL: { sqlcvt = ODBC32.SQL_CVT.DECIMAL; break; } default: // other types are irrelevant at this time return; } ProviderInfo.RestrictedSQLBindTypes |= (int)sqlcvt; } internal void FlagUnsupportedConnectAttr (ODBC32.SQL_ATTR Attribute) { switch (Attribute) { case ODBC32.SQL_ATTR.CURRENT_CATALOG: ProviderInfo.NoCurrentCatalog = true; break; case ODBC32.SQL_ATTR.CONNECTION_DEAD: ProviderInfo.NoConnectionDead = true; break; default: Debug.Assert (false, "Can't flag unknown Attribute"); break; } } internal void FlagUnsupportedStmtAttr (ODBC32.SQL_ATTR Attribute) { switch (Attribute) { case ODBC32.SQL_ATTR.QUERY_TIMEOUT: ProviderInfo.NoQueryTimeout = true; break; case (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE: ProviderInfo.NoSqlSoptSSNoBrowseTable = true; break; case (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS: ProviderInfo.NoSqlSoptSSHiddenColumns = true; break; default: Debug.Assert (false, "Can't flag unknown Attribute"); break; } } internal void FlagUnsupportedColAttr (ODBC32.SQL_DESC v3FieldId, ODBC32.SQL_COLUMN v2FieldId) { if (IsV3Driver) { switch ( v3FieldId){ case (ODBC32.SQL_DESC)ODBC32.SQL_CA_SS.COLUMN_KEY: // SSS_WARNINGS_OFF ProviderInfo.NoSqlCASSColumnKey = true; break; // SSS_WARNINGS_ON default: Debug.Assert (false, "Can't flag unknown Attribute"); break; } } else { switch ( v2FieldId) { default: Debug.Assert (false, "Can't flag unknown Attribute"); break; } } } internal Boolean SQLGetFunctions(ODBC32.SQL_API odbcFunction) { //SQLGetFunctions ODBC32.RetCode retcode; Int16 fExists; Debug.Assert ((Int16) odbcFunction != 0,"SQL_API_ALL_FUNCTIONS is not supported"); OdbcConnectionHandle connectionHandle = ConnectionHandle; if (null != connectionHandle) { retcode = connectionHandle.GetFunctions(odbcFunction, out fExists); } else { Debug.Assert (false, "GetFunctions called and ConnectionHandle is null (connection is disposed?)"); throw ODBC.ConnectionClosed(); } if(retcode != ODBC32.RetCode.SUCCESS) this.HandleError(connectionHandle, retcode); if (fExists == 0){ return false; } else { return true; } } internal bool TestTypeSupport (ODBC32.SQL_TYPE sqltype){ ODBC32.SQL_CONVERT sqlconvert; ODBC32.SQL_CVT sqlcvt; // we need to convert the sqltype to sqlconvert and sqlcvt first // switch (sqltype) { case ODBC32.SQL_TYPE.NUMERIC: { sqlconvert = ODBC32.SQL_CONVERT.NUMERIC; sqlcvt = ODBC32.SQL_CVT.NUMERIC; break; } case ODBC32.SQL_TYPE.WCHAR: { sqlconvert = ODBC32.SQL_CONVERT.CHAR; sqlcvt = ODBC32.SQL_CVT.WCHAR; break; } case ODBC32.SQL_TYPE.WVARCHAR: { sqlconvert = ODBC32.SQL_CONVERT.VARCHAR; sqlcvt = ODBC32.SQL_CVT.WVARCHAR; break; } case ODBC32.SQL_TYPE.WLONGVARCHAR: { sqlconvert = ODBC32.SQL_CONVERT.LONGVARCHAR; sqlcvt = ODBC32.SQL_CVT.WLONGVARCHAR; break; } default: Debug.Assert(false, "Testing that sqltype is currently not supported"); return false; } // now we can check if we have already tested that type // if not we need to do so if (0 == (ProviderInfo.TestedSQLTypes & (int)sqlcvt)) { int flags; flags = GetInfoInt32Unhandled((ODBC32.SQL_INFO)sqlconvert); flags = flags & (int)sqlcvt; ProviderInfo.TestedSQLTypes |= (int)sqlcvt; ProviderInfo.SupportedSQLTypes |= flags; } // now check if the type is supported and return the result // return (0 != (ProviderInfo.SupportedSQLTypes & (int)sqlcvt)); } internal bool TestRestrictedSqlBindType (ODBC32.SQL_TYPE sqltype){ ODBC32.SQL_CVT sqlcvt; switch (sqltype) { case ODBC32.SQL_TYPE.NUMERIC: { sqlcvt = ODBC32.SQL_CVT.NUMERIC; break; } case ODBC32.SQL_TYPE.DECIMAL: { sqlcvt = ODBC32.SQL_CVT.DECIMAL; break; } default: Debug.Assert(false, "Testing that sqltype is currently not supported"); return false; } return (0 != (ProviderInfo.RestrictedSQLBindTypes & (int)sqlcvt)); } // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355) [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel); try { DbTransaction transaction = InnerConnection.BeginTransaction(isolationLevel); // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and // subsequently leaves open the possibility that the outer connection could be GC'ed before the DbTransaction // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable // until the completion of BeginTransaction with KeepAlive GC.KeepAlive(this); return transaction; } finally { Bid.ScopeLeave(ref hscp); } } internal OdbcTransaction Open_BeginTransaction(IsolationLevel isolevel) { OdbcConnection.ExecutePermission.Demand(); CheckState(ADP.BeginTransaction); // MDAC 68323 RollbackDeadTransaction(); if ((null != this.weakTransaction) && this.weakTransaction.IsAlive) { // regression from Dispose/Finalize work throw ADP.ParallelTransactionsNotSupported(this); } //Use the default for unspecified. switch(isolevel) { case IsolationLevel.Unspecified: case IsolationLevel.ReadUncommitted: case IsolationLevel.ReadCommitted: case IsolationLevel.RepeatableRead: case IsolationLevel.Serializable: case IsolationLevel.Snapshot: break; case IsolationLevel.Chaos: throw ODBC.NotSupportedIsolationLevel(isolevel); default: throw ADP.InvalidIsolationLevel(isolevel); }; //Start the transaction OdbcConnectionHandle connectionHandle = ConnectionHandle; ODBC32.RetCode retcode = connectionHandle.BeginTransaction(ref isolevel); if (retcode == ODBC32.RetCode.ERROR) { HandleError(connectionHandle, retcode); } OdbcTransaction transaction = new OdbcTransaction(this, isolevel, connectionHandle); this.weakTransaction = new WeakReference(transaction); // MDAC 69188 return transaction; } internal void Open_ChangeDatabase(string value) { OdbcConnection.ExecutePermission.Demand(); CheckState(ADP.ChangeDatabase); // Database name must not be null, empty or whitspace if ((null == value) || (0 == value.Trim().Length)) { // MDAC 62679 throw ADP.EmptyDatabaseName(); } if (1024 < value.Length*2+2) { throw ADP.DatabaseNameTooLong(); } RollbackDeadTransaction(); //Set the database OdbcConnectionHandle connectionHandle = ConnectionHandle; ODBC32.RetCode retcode = connectionHandle.SetConnectionAttribute3(ODBC32.SQL_ATTR.CURRENT_CATALOG, value, checked((Int32)value.Length*2)); if (retcode != ODBC32.RetCode.SUCCESS) { HandleError(connectionHandle, retcode); } } internal void Open_EnlistTransaction(SysTx.Transaction transaction) { OdbcConnection.VerifyExecutePermission(); if ((null != this.weakTransaction) && this.weakTransaction.IsAlive) { throw ADP.LocalTransactionPresent(); } SysTx.IDtcTransaction oleTxTransaction = ADP.GetOletxTransaction(transaction); OdbcConnectionHandle connectionHandle = ConnectionHandle; ODBC32.RetCode retcode; if (null == oleTxTransaction) { retcode = connectionHandle.SetConnectionAttribute2(ODBC32.SQL_ATTR.SQL_COPT_SS_ENLIST_IN_DTC, (IntPtr) ODBC32.SQL_DTC_DONE, ODBC32.SQL_IS_PTR); } else { retcode = connectionHandle.SetConnectionAttribute4(ODBC32.SQL_ATTR.SQL_COPT_SS_ENLIST_IN_DTC, oleTxTransaction, ODBC32.SQL_IS_PTR); } if (retcode != ODBC32.RetCode.SUCCESS) { HandleError(connectionHandle, retcode); } // Tell the base class about our enlistment ((OdbcConnectionOpen)InnerConnection).EnlistedTransaction = transaction; } internal string Open_GetServerVersion() { //SQLGetInfo - SQL_DBMS_VER return GetInfoStringUnhandled(ODBC32.SQL_INFO.DBMS_VER, true); } } }