File: System\Data\Odbc\OdbcTransaction.cs
Web Access
Project: src\src\libraries\System.Data.Odbc\src\System.Data.Odbc.csproj (System.Data.Odbc)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Data.Common;
 
namespace System.Data.Odbc
{
    public sealed class OdbcTransaction : DbTransaction
    {
        private OdbcConnection? _connection;
        private IsolationLevel _isolevel = IsolationLevel.Unspecified;
        private OdbcConnectionHandle? _handle;
 
        internal OdbcTransaction(OdbcConnection connection, IsolationLevel isolevel, OdbcConnectionHandle handle)
        {
            _connection = connection;
            _isolevel = isolevel;
            _handle = handle;
        }
 
        public new OdbcConnection? Connection
        { // MDAC 66655
            get
            {
                return _connection;
            }
        }
 
        protected override DbConnection? DbConnection
        { // MDAC 66655
            get
            {
                return Connection;
            }
        }
 
        public override IsolationLevel IsolationLevel
        {
            get
            {
                OdbcConnection? connection = _connection;
                if (null == connection)
                {
                    throw ADP.TransactionZombied(this);
                }
 
                //We need to query for the case where the user didn't set the isolevel
                //BeginTransaction(), but we should also query to see if the driver
                //"rolled" the level to a higher supported one...
                if (IsolationLevel.Unspecified == _isolevel)
                {
                    //Get the isolation level
                    int sql_iso = connection.GetConnectAttr(ODBC32.SQL_ATTR.TXN_ISOLATION, ODBC32.HANDLER.THROW);
                    _isolevel = (ODBC32.SQL_TRANSACTION)sql_iso switch
                    {
                        ODBC32.SQL_TRANSACTION.READ_UNCOMMITTED => IsolationLevel.ReadUncommitted,
                        ODBC32.SQL_TRANSACTION.READ_COMMITTED => IsolationLevel.ReadCommitted,
                        ODBC32.SQL_TRANSACTION.REPEATABLE_READ => IsolationLevel.RepeatableRead,
                        ODBC32.SQL_TRANSACTION.SERIALIZABLE => IsolationLevel.Serializable,
                        ODBC32.SQL_TRANSACTION.SNAPSHOT => IsolationLevel.Snapshot,
                        _ => throw ODBC.NoMappingForSqlTransactionLevel(sql_iso),
                    };
                }
                return _isolevel;
            }
        }
 
        public override void Commit()
        {
            OdbcConnection? connection = _connection;
            if (null == connection)
            {
                throw ADP.TransactionZombied(this);
            }
 
            connection.CheckState(ADP.CommitTransaction); // MDAC 68289
 
            //Note: SQLEndTran success if not actually in a transaction, so we have to throw
            //since the IDbTransaction spec indicates this is an error for the managed packages
            if (null == _handle)
            {
                throw ODBC.NotInTransaction();
            }
 
            ODBC32.SQLRETURN retcode = _handle.CompleteTransaction(ODBC32.SQL_COMMIT);
            if (retcode == ODBC32.SQLRETURN.ERROR)
            {
                //If an error has occurred, we will throw an exception in HandleError,
                //and leave the transaction active for the user to retry
                connection.HandleError(_handle, retcode);
            }
 
            //Transaction is complete...
            connection.LocalTransaction = null;
            _connection = null;
            _handle = null;
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                OdbcConnectionHandle? handle = _handle;
                _handle = null;
                if (null != handle)
                {
                    try
                    {
                        ODBC32.SQLRETURN retcode = handle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
                        if (retcode == ODBC32.SQLRETURN.ERROR)
                        {
                            //don't throw an exception here, but trace it so it can be logged
                            if (_connection != null)
                            {
                                Exception e = _connection.HandleErrorNoThrow(handle, retcode)!;
                                ADP.TraceExceptionWithoutRethrow(e);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        //
                        if (!ADP.IsCatchableExceptionType(e))
                        {
                            throw;
                        }
                    }
                }
                if (_connection != null)
                {
                    if (_connection.IsOpen)
                    {
                        _connection.LocalTransaction = null;
                    }
                }
                _connection = null;
                _isolevel = IsolationLevel.Unspecified;
            }
            base.Dispose(disposing);
        }
 
        public override void Rollback()
        {
            OdbcConnection? connection = _connection;
            if (null == connection)
            {
                throw ADP.TransactionZombied(this);
            }
            connection.CheckState(ADP.RollbackTransaction); // MDAC 68289
 
            //Note: SQLEndTran success if not actually in a transaction, so we have to throw
            //since the IDbTransaction spec indicates this is an error for the managed packages
            if (null == _handle)
            {
                throw ODBC.NotInTransaction();
            }
 
            ODBC32.SQLRETURN retcode = _handle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
            if (retcode == ODBC32.SQLRETURN.ERROR)
            {
                //If an error has occurred, we will throw an exception in HandleError,
                //and leave the transaction active for the user to retry
                connection.HandleError(_handle, retcode);
            }
            connection.LocalTransaction = null;
            _connection = null;
            _handle = null;
        }
    }
}