/src/Npgsql/NpgsqlTransaction.cs

https://bitbucket.org/gencer/npgsql2 · C# · 282 lines · 169 code · 52 blank · 61 comment · 30 complexity · edacfdebecb14bc2b06f2b36c5827106 MD5 · raw file

  1. // created on 17/11/2002 at 19:04
  2. // Npgsql.NpgsqlTransaction.cs
  3. //
  4. // Author:
  5. // Francisco Jr. (fxjrlists@yahoo.com.br)
  6. //
  7. // Copyright (C) 2002 The Npgsql Development Team
  8. // npgsql-general@gborg.postgresql.org
  9. // http://gborg.postgresql.org/project/npgsql/projdisplay.php
  10. //
  11. // Permission to use, copy, modify, and distribute this software and its
  12. // documentation for any purpose, without fee, and without a written
  13. // agreement is hereby granted, provided that the above copyright notice
  14. // and this paragraph and the following two paragraphs appear in all copies.
  15. //
  16. // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
  17. // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
  18. // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
  19. // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
  20. // THE POSSIBILITY OF SUCH DAMAGE.
  21. //
  22. // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  23. // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  24. // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
  25. // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
  26. // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  27. using System;
  28. using System.Data;
  29. using System.Data.Common;
  30. using System.Reflection;
  31. using System.Resources;
  32. using System.Text;
  33. using System.Threading;
  34. namespace Npgsql
  35. {
  36. /// <summary>
  37. /// Represents a transaction to be made in a PostgreSQL database. This class cannot be inherited.
  38. /// </summary>
  39. public sealed class NpgsqlTransaction : DbTransaction
  40. {
  41. private static readonly String CLASSNAME = MethodBase.GetCurrentMethod().DeclaringType.Name;
  42. private static readonly ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType);
  43. private NpgsqlConnection _conn = null;
  44. private readonly IsolationLevel _isolation = IsolationLevel.ReadCommitted;
  45. private bool _disposed = false;
  46. internal NpgsqlTransaction(NpgsqlConnection conn)
  47. : this(conn, IsolationLevel.ReadCommitted)
  48. {
  49. }
  50. internal NpgsqlTransaction(NpgsqlConnection conn, IsolationLevel isolation)
  51. {
  52. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
  53. _conn = conn;
  54. _isolation = isolation;
  55. StringBuilder commandText = new StringBuilder("BEGIN; SET TRANSACTION ISOLATION LEVEL ");
  56. if (isolation == IsolationLevel.RepeatableRead)
  57. {
  58. commandText.Append("REPEATABLE READ");
  59. }
  60. else if ((isolation == IsolationLevel.Serializable) ||
  61. (isolation == IsolationLevel.Snapshot))
  62. {
  63. commandText.Append("SERIALIZABLE");
  64. }
  65. else
  66. {
  67. // Set isolation level default to read committed.
  68. _isolation = IsolationLevel.ReadCommitted;
  69. commandText.Append("READ COMMITTED");
  70. }
  71. commandText.Append(";");
  72. NpgsqlCommand command = new NpgsqlCommand(commandText.ToString(), conn.Connector);
  73. command.ExecuteBlind();
  74. _conn.Connector.Transaction = this;
  75. }
  76. /// <summary>
  77. /// Gets the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>
  78. /// object associated with the transaction, or a null reference if the
  79. /// transaction is no longer valid.
  80. /// </summary>
  81. /// <value>The <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>
  82. /// object associated with the transaction.</value>
  83. public new NpgsqlConnection Connection
  84. {
  85. get { return _conn; }
  86. }
  87. protected override DbConnection DbConnection
  88. {
  89. get { return Connection; }
  90. }
  91. /// <summary>
  92. /// Specifies the <see cref="System.Data.IsolationLevel">IsolationLevel</see> for this transaction.
  93. /// </summary>
  94. /// <value>The <see cref="System.Data.IsolationLevel">IsolationLevel</see> for this transaction.
  95. /// The default is <b>ReadCommitted</b>.</value>
  96. public override IsolationLevel IsolationLevel
  97. {
  98. get
  99. {
  100. if (_conn == null)
  101. {
  102. throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
  103. }
  104. return _isolation;
  105. }
  106. }
  107. protected override void Dispose(bool disposing)
  108. {
  109. if (disposing && this._conn != null)
  110. {
  111. if (_conn.Connector.Transaction != null)
  112. {
  113. if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) != 0)
  114. {
  115. // can't count on Rollback working if the thread has been aborted
  116. // need to copy since Cancel will set it to null
  117. NpgsqlConnection conn = _conn;
  118. Cancel();
  119. // must close connection since transaction hasn't been rolled back
  120. conn.Close();
  121. }
  122. else
  123. {
  124. this.Rollback();
  125. }
  126. }
  127. this._disposed = true;
  128. }
  129. base.Dispose(disposing);
  130. }
  131. /// <summary>
  132. /// Commits the database transaction.
  133. /// </summary>
  134. public override void Commit()
  135. {
  136. CheckDisposed();
  137. if (_conn == null)
  138. {
  139. throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
  140. }
  141. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Commit");
  142. NpgsqlCommand command = new NpgsqlCommand("COMMIT", _conn.Connector);
  143. command.ExecuteBlind();
  144. _conn.Connector.Transaction = null;
  145. _conn = null;
  146. }
  147. /// <summary>
  148. /// Rolls back a transaction from a pending state.
  149. /// </summary>
  150. public override void Rollback()
  151. {
  152. CheckDisposed();
  153. if (_conn == null)
  154. {
  155. throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
  156. }
  157. NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Rollback");
  158. NpgsqlCommand command = new NpgsqlCommand("ROLLBACK", _conn.Connector);
  159. command.ExecuteBlind();
  160. _conn.Connector.Transaction = null;
  161. _conn = null;
  162. }
  163. /// <summary>
  164. /// Rolls back a transaction from a pending savepoint state.
  165. /// </summary>
  166. public void Rollback(String savePointName)
  167. {
  168. CheckDisposed();
  169. if (_conn == null)
  170. {
  171. throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
  172. }
  173. if (!_conn.Connector.SupportsSavepoint)
  174. {
  175. throw new InvalidOperationException(resman.GetString("Exception_SavePointNotSupported"));
  176. }
  177. if (savePointName.Contains(";"))
  178. {
  179. throw new InvalidOperationException(resman.GetString("Exception_SavePointWithSemicolon"));
  180. }
  181. NpgsqlCommand command = new NpgsqlCommand("ROLLBACK TO SAVEPOINT " + savePointName, _conn.Connector);
  182. command.ExecuteBlind();
  183. }
  184. /// <summary>
  185. /// Creates a transaction save point.
  186. /// </summary>
  187. public void Save(String savePointName)
  188. {
  189. CheckDisposed();
  190. if (_conn == null)
  191. {
  192. throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
  193. }
  194. if (!_conn.Connector.SupportsSavepoint)
  195. {
  196. throw new InvalidOperationException(resman.GetString("Exception_SavePointNotSupported"));
  197. }
  198. if (savePointName.Contains(";"))
  199. {
  200. throw new InvalidOperationException(resman.GetString("Exception_SavePointWithSemicolon"));
  201. }
  202. NpgsqlCommand command = new NpgsqlCommand("SAVEPOINT " + savePointName, _conn.Connector);
  203. command.ExecuteBlind();
  204. }
  205. /// <summary>
  206. /// Cancel the transaction without telling the backend about it. This is
  207. /// used to make the transaction go away when closing a connection.
  208. /// </summary>
  209. internal void Cancel()
  210. {
  211. CheckDisposed();
  212. if (_conn != null)
  213. {
  214. _conn.Connector.Transaction = null;
  215. _conn = null;
  216. }
  217. }
  218. internal bool Disposed
  219. {
  220. get { return _disposed; }
  221. }
  222. internal void CheckDisposed()
  223. {
  224. if (_disposed)
  225. {
  226. throw new ObjectDisposedException(CLASSNAME);
  227. }
  228. }
  229. }
  230. }