2009-05-13 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlTransactionCallbacks.cs
1 // NpgsqlTransactionCallbacks.cs
2 //
3 // Author:
4 //  Josh Cooley <jbnpgsql@tuxinthebox.net>
5 //
6 // Copyright (C) 2007, The Npgsql Development Team
7 //
8 // Permission to use, copy, modify, and distribute this software and its
9 // documentation for any purpose, without fee, and without a written
10 // agreement is hereby granted, provided that the above copyright notice
11 // and this paragraph and the following two paragraphs appear in all copies.
12 // 
13 // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
14 // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
15 // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
16 // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
17 // THE POSSIBILITY OF SUCH DAMAGE.
18 // 
19 // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
20 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21 // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
23 // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24
25 using System;
26 using System.Data;
27
28 namespace Npgsql
29 {
30         internal interface INpgsqlTransactionCallbacks : IDisposable
31         {
32                 string GetName();
33                 void PrepareTransaction();
34                 void CommitTransaction();
35                 void RollbackTransaction();
36         }
37
38         internal class NpgsqlTransactionCallbacks : MarshalByRefObject, INpgsqlTransactionCallbacks
39         {
40                 private NpgsqlConnection _connection;
41                 private readonly string _connectionString;
42                 private bool _closeConnectionRequired;
43                 private bool _prepared;
44                 private readonly string _txName = Guid.NewGuid().ToString();
45
46                 private static readonly String CLASSNAME = "NpgsqlTransactionCallbacks";
47
48                 public NpgsqlTransactionCallbacks(NpgsqlConnection connection)
49                 {
50                         _connection = connection;
51                         _connectionString = _connection.ConnectionString;
52                         _connection.Disposed += new EventHandler(_connection_Disposed);
53                 }
54
55                 private void _connection_Disposed(object sender, EventArgs e)
56                 {
57                         // TODO: what happens if this is called from another thread?
58                         // connections should not be shared across threads while in a transaction
59                         _connection.Disposed -= new EventHandler(_connection_Disposed);
60                         _connection = null;
61                 }
62
63                 private NpgsqlConnection GetConnection()
64                 {
65                         if (_connection == null || (_connection.FullState & ConnectionState.Open) != ConnectionState.Open)
66                         {
67                                 _connection = new NpgsqlConnection(_connectionString);
68                                 _connection.Open();
69                                 _closeConnectionRequired = true;
70                                 return _connection;
71                         }
72                         else
73                         {
74                                 return _connection;
75                         }
76                 }
77
78                 #region INpgsqlTransactionCallbacks Members
79
80                 public string GetName()
81                 {
82                         return _txName;
83                 }
84
85                 public void CommitTransaction()
86                 {
87                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CommitTransaction");
88                         NpgsqlConnection connection = GetConnection();
89                         NpgsqlCommand command = null;
90                         if (_prepared)
91                         {
92                                 command = new NpgsqlCommand(string.Format("COMMIT PREPARED '{0}'", _txName), connection);
93                         }
94                         else
95                         {
96                                 command = new NpgsqlCommand("COMMIT", connection);
97                         }
98                         command.ExecuteBlind();
99                 }
100
101                 public void PrepareTransaction()
102                 {
103             if (!_prepared)
104             {
105                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "PrepareTransaction");
106                 NpgsqlConnection connection = GetConnection();
107                 NpgsqlCommand command = new NpgsqlCommand(string.Format("PREPARE TRANSACTION '{0}'", _txName), connection);
108                 command.ExecuteBlind();
109                 _prepared = true;
110             }
111                 }
112
113                 public void RollbackTransaction()
114                 {
115                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RollbackTransaction");
116                         NpgsqlConnection connection = GetConnection();
117                         NpgsqlCommand command = null;
118                         if (_prepared)
119                         {
120                                 command = new NpgsqlCommand(string.Format("ROLLBACK PREPARED '{0}'", _txName), connection);
121                         }
122                         else
123                         {
124                                 command = new NpgsqlCommand("ROLLBACK", connection);
125                         }
126                         command.ExecuteBlind();
127                 }
128
129                 #endregion
130
131                 #region IDisposable Members
132
133                 public void Dispose()
134                 {
135                         if (_closeConnectionRequired)
136                         {
137                                 _connection.Close();
138                         }
139                         _closeConnectionRequired = false;
140                 }
141
142                 #endregion
143         }
144 }