-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSQLiteTransaction2.cs
More file actions
executable file
·276 lines (236 loc) · 8.93 KB
/
Copy pathSQLiteTransaction2.cs
File metadata and controls
executable file
·276 lines (236 loc) · 8.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Robert Simpson (robert@blackcastlesoft.com)
*
* Released to the public domain, use at your own risk!
********************************************************/
namespace System.Data.SQLite
{
using System;
using System.Threading;
///////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// SQLite implementation of DbTransaction that does support nested transactions.
/// </summary>
public sealed class SQLiteTransaction2 : SQLiteTransaction
{
/// <summary>
/// The original transaction level for the associated connection
/// when this transaction was created (i.e. begun).
/// </summary>
private int _beginLevel;
/// <summary>
/// The SAVEPOINT name for this transaction, if any. This will
/// only be non-null if this transaction is a nested one.
/// </summary>
private string _savePointName;
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Constructs the transaction object, binding it to the supplied connection
/// </summary>
/// <param name="connection">The connection to open a transaction on</param>
/// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param>
internal SQLiteTransaction2(SQLiteConnection connection, bool deferredLock)
: base(connection, deferredLock)
{
// do nothing.
}
///////////////////////////////////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
throw new ObjectDisposedException(typeof(SQLiteTransaction2).Name);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Disposes the transaction. If it is currently active, any changes are rolled back.
/// </summary>
protected override void Dispose(bool disposing)
{
try
{
if (!disposed)
{
if (disposing)
{
////////////////////////////////////
// dispose managed resources here...
////////////////////////////////////
if (IsValid(false))
{
IssueRollback(false);
}
}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
}
}
finally
{
base.Dispose(disposing);
//
// NOTE: Everything should be fully disposed at this point.
//
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Commits the current transaction.
/// </summary>
public override void Commit()
{
CheckDisposed();
SQLiteConnection.Check(_cnn);
IsValid(true);
if (_beginLevel == 0)
{
using (SQLiteCommand cmd = _cnn.CreateCommand())
{
cmd.CommandText = "COMMIT;";
cmd.ExecuteNonQuery();
}
_cnn._transactionLevel = 0;
_cnn = null;
}
else
{
using (SQLiteCommand cmd = _cnn.CreateCommand())
{
if (String.IsNullOrEmpty(_savePointName))
throw new SQLiteException("Cannot commit, unknown SAVEPOINT");
cmd.CommandText = String.Format(
"RELEASE {0};", _savePointName);
cmd.ExecuteNonQuery();
}
_cnn._transactionLevel--;
_cnn = null;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Attempts to start a transaction. An exception will be thrown if the transaction cannot
/// be started for any reason.
/// </summary>
/// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param>
protected override void Begin(
bool deferredLock
)
{
int transactionLevel;
if ((transactionLevel = _cnn._transactionLevel++) == 0)
{
try
{
using (SQLiteCommand cmd = _cnn.CreateCommand())
{
if (!deferredLock)
cmd.CommandText = "BEGIN IMMEDIATE;";
else
cmd.CommandText = "BEGIN;";
cmd.ExecuteNonQuery();
_beginLevel = transactionLevel;
}
}
catch (SQLiteException)
{
_cnn._transactionLevel--;
_cnn = null;
throw;
}
}
else
{
try
{
using (SQLiteCommand cmd = _cnn.CreateCommand())
{
_savePointName = GetSavePointName();
cmd.CommandText = String.Format(
"SAVEPOINT {0};", _savePointName);
cmd.ExecuteNonQuery();
_beginLevel = transactionLevel;
}
}
catch (SQLiteException)
{
_cnn._transactionLevel--;
_cnn = null;
throw;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Issue a ROLLBACK command against the database connection,
/// optionally re-throwing any caught exception.
/// </summary>
/// <param name="throwError">
/// Non-zero to re-throw caught exceptions.
/// </param>
protected override void IssueRollback(bool throwError)
{
SQLiteConnection cnn = Interlocked.Exchange(ref _cnn, null);
if (cnn != null)
{
if (_beginLevel == 0)
{
try
{
using (SQLiteCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = "ROLLBACK;";
cmd.ExecuteNonQuery();
}
cnn._transactionLevel = 0;
}
catch
{
if (throwError)
throw;
}
}
else
{
try
{
using (SQLiteCommand cmd = cnn.CreateCommand())
{
if (String.IsNullOrEmpty(_savePointName))
throw new SQLiteException("Cannot rollback, unknown SAVEPOINT");
cmd.CommandText = String.Format(
"ROLLBACK TO {0};", _savePointName);
cmd.ExecuteNonQuery();
}
cnn._transactionLevel--;
}
catch
{
if (throwError)
throw;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Constructs the name of a new savepoint for this transaction. It
/// should only be called from the constructor of this class.
/// </summary>
/// <returns>
/// The name of the new savepoint -OR- null if it cannot be constructed.
/// </returns>
private string GetSavePointName()
{
int sequence = ++_cnn._transactionSequence;
return String.Format(
"sqlite_dotnet_savepoint_{0}", sequence);
}
}
}