25dbd7179841ce314e68672ea1ae0e1bc3d58a68
[mono.git] / mcs / class / referencesource / System.Data / System / Data / Odbc / OdbcTransaction.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="OdbcTransaction.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 //------------------------------------------------------------------------------
8
9 using System;
10 using System.Data;
11 using System.Data.Common;
12 using System.Threading;
13
14 namespace System.Data.Odbc
15 {
16     public sealed class OdbcTransaction : DbTransaction {
17         private OdbcConnection _connection;
18         private IsolationLevel _isolevel = IsolationLevel.Unspecified;
19         private OdbcConnectionHandle _handle;
20
21         internal OdbcTransaction(OdbcConnection connection, IsolationLevel isolevel, OdbcConnectionHandle handle) {
22             OdbcConnection.VerifyExecutePermission();
23             
24             _connection = connection;
25             _isolevel   = isolevel;
26             _handle = handle;
27         }
28
29         new public OdbcConnection Connection { // MDAC 66655
30             get {
31                 return _connection;
32             }
33         }
34
35         override protected DbConnection DbConnection { // MDAC 66655
36             get {
37                 return Connection;
38             }
39         }
40
41         override public IsolationLevel IsolationLevel {
42             get {
43                 OdbcConnection connection = _connection;
44                 if (null == connection ) {
45                     throw ADP.TransactionZombied(this);
46                 }
47
48                 //We need to query for the case where the user didn't set the isolevel
49                 //BeginTransaction(), but we should also query to see if the driver
50                 //"rolled" the level to a higher supported one...
51                 if(IsolationLevel.Unspecified == _isolevel) {
52                     //Get the isolation level
53                     int sql_iso= connection .GetConnectAttr(ODBC32.SQL_ATTR.TXN_ISOLATION, ODBC32.HANDLER.THROW);
54                     switch((ODBC32.SQL_TRANSACTION)sql_iso) {
55                     case ODBC32.SQL_TRANSACTION.READ_UNCOMMITTED:
56                         _isolevel = IsolationLevel.ReadUncommitted;
57                         break;
58                     case ODBC32.SQL_TRANSACTION.READ_COMMITTED:
59                         _isolevel = IsolationLevel.ReadCommitted;
60                         break;
61                     case ODBC32.SQL_TRANSACTION.REPEATABLE_READ:
62                         _isolevel = IsolationLevel.RepeatableRead;
63                         break;
64                     case ODBC32.SQL_TRANSACTION.SERIALIZABLE:
65                         _isolevel = IsolationLevel.Serializable;
66                         break;
67                     case ODBC32.SQL_TRANSACTION.SNAPSHOT:
68                         _isolevel = IsolationLevel.Snapshot;
69                         break;
70                     default:
71                         throw ODBC.NoMappingForSqlTransactionLevel(sql_iso);
72                     };
73                 }
74                 return _isolevel;
75             }
76         }
77
78         override public void Commit() {
79             OdbcConnection.ExecutePermission.Demand(); // MDAC 81476
80
81             OdbcConnection connection = _connection;
82             if (null == connection) {
83                 throw ADP.TransactionZombied(this);
84             }
85
86             connection.CheckState(ADP.CommitTransaction); // MDAC 68289
87
88             //Note: SQLEndTran success if not actually in a transaction, so we have to throw
89             //since the IDbTransaciton spec indicates this is an error for the managed packages
90             if(null == _handle) {
91                 throw ODBC.NotInTransaction();
92             }
93             
94             ODBC32.RetCode retcode = _handle.CompleteTransaction(ODBC32.SQL_COMMIT);
95             if (retcode == ODBC32.RetCode.ERROR)  {
96                 //If an error has occurred, we will throw an exception in HandleError,
97                 //and leave the transaction active for the user to retry
98                 connection.HandleError(_handle, retcode);
99             }
100
101             //Transaction is complete...
102             connection.LocalTransaction = null;
103             _connection = null;
104             _handle = null;
105             
106         }
107
108         protected override void Dispose(bool disposing) {
109             if (disposing) {
110                 OdbcConnectionHandle handle = _handle;
111                 _handle = null;
112                 if (null != handle){
113                     try{
114                         ODBC32.RetCode retcode = handle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
115                         if (retcode == ODBC32.RetCode.ERROR) {
116                             //don't throw an exception here, but trace it so it can be logged
117                             if (_connection != null) {
118                                 Exception e = _connection.HandleErrorNoThrow(handle, retcode);
119                                 ADP.TraceExceptionWithoutRethrow(e);                               
120                             }
121                         }
122                     }
123                     catch (Exception e){
124                         // 
125                         if (!ADP.IsCatchableExceptionType(e)) {
126                             throw;
127                         }
128                     }
129                 }
130                 if (_connection != null) {
131                     if (_connection.IsOpen) {
132                         _connection.LocalTransaction = null;
133                     }
134                 }
135                 _connection = null;
136                 _isolevel = IsolationLevel.Unspecified;
137             }
138             base.Dispose(disposing);
139         }
140
141         override public void Rollback() {
142             OdbcConnection connection = _connection;
143             if (null == connection) {
144                 throw ADP.TransactionZombied(this);
145             }
146             connection.CheckState(ADP.RollbackTransaction); // MDAC 68289
147
148             //Note: SQLEndTran success if not actually in a transaction, so we have to throw
149             //since the IDbTransaciton spec indicates this is an error for the managed packages
150             if(null == _handle) {
151                 throw ODBC.NotInTransaction();
152             }
153
154             ODBC32.RetCode retcode = _handle.CompleteTransaction(ODBC32.SQL_ROLLBACK);
155             if (retcode == ODBC32.RetCode.ERROR) {
156                 //If an error has occurred, we will throw an exception in HandleError,
157                 //and leave the transaction active for the user to retry
158                 connection.HandleError(_handle, retcode);
159             }
160             connection.LocalTransaction = null;
161             _connection = null;
162             _handle = null;
163         }
164     }
165 }
166