Copy from 72246 to trunk
[mono.git] / mcs / class / Mono.Data.Sqlite / Mono.Data.Sqlite_2.0 / SQLiteTransaction.cs
1 //
2 // Mono.Data.Sqlite.SQLiteTransaction.cs
3 //
4 // Author(s):
5 //   Robert Simpson (robert@blackcastlesoft.com)
6 //
7 // Adapted and modified for the Mono Project by
8 //   Marek Habersack (grendello@gmail.com)
9 //
10 //
11 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2007 Marek Habersack
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 /********************************************************
35  * ADO.NET 2.0 Data Provider for Sqlite Version 3.X
36  * Written by Robert Simpson (robert@blackcastlesoft.com)
37  * 
38  * Released to the public domain, use at your own risk!
39  ********************************************************/
40 #if NET_2_0
41 namespace Mono.Data.Sqlite
42 {
43   using System;
44   using System.Data;
45   using System.Data.Common;
46
47   /// <summary>
48   /// Sqlite implementation of DbTransaction.
49   /// </summary>
50   public class SqliteTransaction : DbTransaction
51   {
52     /// <summary>
53     /// The connection to which this transaction is bound
54     /// </summary>
55     internal SqliteConnection _cnn;
56     internal long _version; // Matches the version of the connection
57
58     /// <summary>
59     /// Constructs the transaction object, binding it to the supplied connection
60     /// </summary>
61     /// <param name="connection">The connection to open a transaction on</param>
62     /// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param>
63     internal SqliteTransaction(SqliteConnection connection, bool deferredLock)
64     {
65       _cnn = connection;
66       _version = _cnn._version;
67
68       if (_cnn._transactionLevel++ == 0)
69       {
70         try
71         {
72           using (SqliteCommand cmd = _cnn.CreateCommand())
73           {
74             if (!deferredLock)
75               cmd.CommandText = "BEGIN IMMEDIATE";
76             else
77               cmd.CommandText = "BEGIN";
78
79             cmd.ExecuteNonQuery();
80           }
81         }
82         catch (SqliteException)
83         {
84           _cnn._transactionLevel--;
85           _cnn = null;
86           throw;
87         }
88       }
89     }
90
91     /// <summary>
92     /// Commits the current transaction.
93     /// </summary>
94     public override void Commit()
95     {
96       IsValid(true);
97
98       if (--_cnn._transactionLevel == 0)
99       {
100         try
101         {
102           using (SqliteCommand cmd = _cnn.CreateCommand())
103           {
104             cmd.CommandText = "COMMIT";
105             cmd.ExecuteNonQuery();
106           }
107         }
108         finally
109         {
110           _cnn = null;
111         }
112       }
113       else
114       {
115         _cnn = null;
116       }
117     }
118
119     /// <summary>
120     /// Returns the underlying connection to which this transaction applies.
121     /// </summary>
122     public new SqliteConnection Connection
123     {
124       get { return _cnn; }
125     }
126
127     /// <summary>
128     /// Forwards to the local Connection property
129     /// </summary>
130     protected override DbConnection DbConnection
131     {
132       get { return Connection; }
133     }
134
135     /// <summary>
136     /// Disposes the transaction.  If it is currently active, any changes are rolled back.
137     /// </summary>
138     protected override void Dispose(bool disposing)
139     {
140       if (IsValid(false))
141         Rollback();
142
143       _cnn = null;
144
145       base.Dispose(disposing);
146     }
147
148     /// <summary>
149     /// Gets the isolation level of the transaction.  Sqlite only supports Serializable transactions.
150     /// </summary>
151     public override IsolationLevel IsolationLevel
152     {
153       get { return IsolationLevel.Serializable; }
154     }
155
156     /// <summary>
157     /// Rolls back the active transaction.
158     /// </summary>
159     public override void Rollback()
160     {
161       IsValid(true);
162
163       try
164       {
165         using (SqliteCommand cmd = _cnn.CreateCommand())
166         {
167           cmd.CommandText = "ROLLBACK";
168           cmd.ExecuteNonQuery();
169         }
170         _cnn._transactionLevel = 0;
171       }
172       finally
173       {
174         _cnn = null;
175       }
176     }
177
178     internal bool IsValid(bool throwError)
179     {
180       if (_cnn == null)
181       {
182         if (throwError == true) throw new ArgumentNullException("No connection associated with this transaction");
183         else return false;
184       }
185
186       if (_cnn._transactionLevel == 0)
187       {
188         if (throwError == true) throw new SqliteException((int)SqliteErrorCode.Misuse, "No transaction is active on this connection");
189         else return false;
190       }
191       if (_cnn._version != _version)
192       {
193         if (throwError == true) throw new SqliteException((int)SqliteErrorCode.Misuse, "The connection was closed and re-opened, changes were rolled back");
194         else return false;
195       }
196       if (_cnn.State != ConnectionState.Open)
197       {
198         if (throwError == true) throw new SqliteException((int)SqliteErrorCode.Misuse, "Connection was closed");
199         else return false;
200       }
201
202       return true;
203     }
204   }
205 }
206 #endif