2 // System.Data.Odbc.OdbcTransaction
5 // Brian Ritchie (brianlritchie@hotmail.com)
7 // Copyright (C) Brian Ritchie, 2002
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Data.Common;
33 using System.Globalization;
35 namespace System.Data.Odbc
38 public sealed class OdbcTransaction : DbTransaction, IDisposable
40 public sealed class OdbcTransaction : MarshalByRefObject, IDbTransaction
43 private bool disposed;
44 private OdbcConnection connection;
45 private IsolationLevel isolationlevel;
48 internal OdbcTransaction (OdbcConnection conn, IsolationLevel isolationlevel)
50 // Set Auto-commit (102) to false
51 SetAutoCommit (conn, false);
52 // Handle isolation level
53 OdbcIsolationLevel lev = OdbcIsolationLevel.ReadCommitted;
54 OdbcConnectionAttribute attr = OdbcConnectionAttribute.TransactionIsolation;
55 switch (isolationlevel) {
56 case IsolationLevel.ReadUncommitted:
57 lev = OdbcIsolationLevel.ReadUncommitted;
59 case IsolationLevel.ReadCommitted:
60 lev = OdbcIsolationLevel.ReadCommitted;
62 case IsolationLevel.RepeatableRead:
63 lev = OdbcIsolationLevel.RepeatableRead;
65 case IsolationLevel.Serializable:
66 lev = OdbcIsolationLevel.Serializable;
69 case IsolationLevel.Snapshot:
70 // badly broken on MS:
71 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=305736
72 lev = OdbcIsolationLevel.Snapshot;
74 // SQL_ATTR_TXN_ISOLATION can be used to set all other isolation
75 // levels except for SQL_TXN_SS_SNAPSHOT. If you want to use snapshot
76 // isolation, you must set SQL_TXN_SS_SNAPSHOT through
77 // SQL_COPT_SS_TXN_ISOLATION. However, you can retrieve the
78 // isolation level by using either SQL_ATTR_TXN_ISOLATION or
79 // SQL_COPT_SS_TXN_ISOLATION.
81 // http://msdn2.microsoft.com/en-us/library/ms131709.aspx
82 attr = OdbcConnectionAttribute.CoptTransactionIsolation;
85 case IsolationLevel.Unspecified:
86 // when isolationlevel is not specified, then use
87 // default isolation level of the driver and
88 // lazy initialize it in the IsolationLevel property
91 case IsolationLevel.Chaos:
92 throw new ArgumentOutOfRangeException ("IsolationLevel",
93 string.Format (CultureInfo.CurrentCulture,
94 "The IsolationLevel enumeration " +
95 "value, {0}, is not supported by " +
96 "the .Net Framework Odbc Data " +
97 "Provider.", (int) isolationlevel));
101 throw new ArgumentOutOfRangeException ("IsolationLevel",
102 string.Format (CultureInfo.CurrentCulture,
103 "The IsolationLevel enumeration value, {0}, is invalid.",
104 (int) isolationlevel));
106 throw new ArgumentException (string.Format (
107 CultureInfo.InvariantCulture,
108 "Not supported isolationlevel - {0}",
113 // only change isolation level if it was explictly set
114 if (isolationlevel != IsolationLevel.Unspecified) {
115 // mbd: Getting the return code of the second call to SQLSetConnectAttr is missing from original code!
116 OdbcReturn ret = libodbc.SQLSetConnectAttr (conn.hDbc,
117 attr, (IntPtr) lev, 0);
118 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
119 throw conn.CreateOdbcException (OdbcHandleType.Dbc, conn.hDbc);
121 this.isolationlevel = isolationlevel;
126 // Set Auto-commit (102) connection attribute
127 // [MonoTODO]: nice to have before svn: define libodbc.SQL_IS_UINTEGER = -5
128 private static void SetAutoCommit (OdbcConnection conn, bool isAuto)
130 OdbcReturn ret = libodbc.SQLSetConnectAttr (conn.hDbc,
131 OdbcConnectionAttribute.AutoCommit,
132 (IntPtr) (isAuto ? 1 : 0), -5);
133 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
134 throw conn.CreateOdbcException (OdbcHandleType.Dbc, conn.hDbc);
137 private static IsolationLevel GetIsolationLevel (OdbcConnection conn)
141 OdbcReturn ret = libodbc.SQLGetConnectAttr (conn.hDbc,
142 OdbcConnectionAttribute.TransactionIsolation,
143 out lev, 0, out length);
144 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
145 throw conn.CreateOdbcException (OdbcHandleType.Dbc, conn.hDbc);
146 return MapOdbcIsolationLevel ((OdbcIsolationLevel) lev);
149 private static IsolationLevel MapOdbcIsolationLevel (OdbcIsolationLevel odbcLevel)
151 IsolationLevel isoLevel = IsolationLevel.Unspecified;
154 case OdbcIsolationLevel.ReadUncommitted:
155 isoLevel = IsolationLevel.ReadUncommitted;
157 case OdbcIsolationLevel.ReadCommitted:
158 isoLevel = IsolationLevel.ReadCommitted;
160 case OdbcIsolationLevel.RepeatableRead:
161 isoLevel = IsolationLevel.RepeatableRead;
163 case OdbcIsolationLevel.Serializable:
164 isoLevel = IsolationLevel.Serializable;
167 case OdbcIsolationLevel.Snapshot:
168 isoLevel = IsolationLevel.Snapshot;
172 throw new NotSupportedException (string.Format (
173 CultureInfo.InvariantCulture,
174 "Isolation level {0} is not supported.",
181 #region Implementation of IDisposable
186 void Dispose (bool disposing)
189 if (disposing && isOpen)
195 void IDisposable.Dispose ()
198 GC.SuppressFinalize (this);
201 #endregion Implementation of IDisposable
203 #region Implementation of IDbTransaction
212 throw ExceptionHelper.TransactionNotUsable (GetType ());
214 if (connection.transaction == this) {
215 OdbcReturn ret = libodbc.SQLEndTran ((short) OdbcHandleType.Dbc, connection.hDbc, 0);
216 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
217 throw connection.CreateOdbcException (OdbcHandleType.Dbc, connection.hDbc);
218 SetAutoCommit (connection, true); // restore default auto-commit
219 connection.transaction = null;
223 throw new InvalidOperationException ();
233 throw ExceptionHelper.TransactionNotUsable (GetType ());
235 if (connection.transaction == this) {
236 OdbcReturn ret = libodbc.SQLEndTran ((short) OdbcHandleType.Dbc, connection.hDbc, 1);
237 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
238 throw connection.CreateOdbcException (OdbcHandleType.Dbc, connection.hDbc);
239 SetAutoCommit (connection, true); // restore default auto-commit
240 connection.transaction = null;
244 throw new InvalidOperationException ();
248 protected override DbConnection DbConnection {
254 IDbConnection IDbTransaction.Connection {
266 IsolationLevel IsolationLevel {
269 throw ExceptionHelper.TransactionNotUsable (GetType ());
271 if (isolationlevel == IsolationLevel.Unspecified)
272 isolationlevel = GetIsolationLevel (Connection);
273 return isolationlevel;
277 #endregion Implementation of IDbTransaction
279 #region Public Instance Properties
281 public new OdbcConnection Connection {
287 #endregion Public Instance Properties