Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members

pqxxobject/transaction.cc

Go to the documentation of this file.
00001 // database transaction convenience wrapper                      -*- C++ -*-
00002 // $Id: transaction.cc,v 1.10 2004/04/02 22:44:55 roger Exp $
00003 //
00004 // Copyright (C) 2003  Roger Leigh <rleigh@debian.org>
00005 //
00006 //
00007 //  All rights reserved.
00008 //
00009 //  Redistribution and use in source and binary forms, with or without
00010 //  modification, are permitted provided that the following conditions
00011 //  are met:
00012 //
00013 //  * Redistributions of source code must retain the above copyright
00014 //    notice, this list of conditions and the following disclaimer.
00015 //  * Redistributions in binary form must reproduce the above
00016 //    copyright notice, this list of conditions and the following
00017 //    disclaimer in the documentation and/or other materials provided
00018 //    with the distribution.
00019 //  * Neither the name of the author, nor the names of other
00020 //    contributors may be used to endorse or promote products derived
00021 //    from this software without specific prior written permission.
00022 //
00023 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
00024 //  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
00025 //  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00026 //  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00027 //  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
00028 //  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00029 //  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00030 //  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00031 //  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00032 //  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
00033 //  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00034 //  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00035 //  SUCH DAMAGE.
00036 //
00038 
00039 #include <cassert>
00040 #include <iostream>
00041 #include <sstream>
00042 
00043 #include <sigc++/object.h>
00044 
00045 #include <pqxxobject/transaction.h>
00046 
00047 using namespace pqxxobject;
00048 
00049 namespace
00050 {
00051   std::string describe_status(transaction::state current_state)
00052   {
00053     std::string status;
00054     switch (current_state)
00055       {
00056       case transaction::STATE_EXECUTING:
00057         status = "the current transaction is executing normally";
00058         break;
00059       case transaction::STATE_ABORTING:
00060         status = "the current transaction is in the process of aborting";
00061         break;
00062       case transaction::STATE_COMMITTING:
00063         status = "the current transaction is in the process of committing";
00064         break;
00065       case transaction::STATE_NONE:
00066         status = "there is no current transaction";
00067         break;
00068       case transaction::STATE_ABORTED:
00069         status = "the current transaction is aborted";
00070         break;
00071       case transaction::STATE_COMMITTED:
00072         status = "the current transaction is committed";
00073         break;
00074       default:
00075         status = "the current transaction status is unknown";
00076       }
00077     return status;
00078   }
00079 }
00080 
00081 transaction::transaction(pqxx::connection& conn,
00082                          pqxx::transaction<>*& tran):
00083   m_connection(conn),
00084   m_transaction(tran),
00085   m_state(STATE_NONE),
00086   m_recursion_level(0),
00087   m_checkpoint(false)
00088 {
00089 }
00090 
00091 transaction::~transaction()
00092 {
00093 }
00094 
00095 void
00096 transaction::set_checkpoint(bool checkpoint_exists)
00097 {
00098   m_checkpoint = checkpoint_exists;
00099 }
00100 
00101 bool
00102 transaction::get_checkpoint() const
00103 {
00104   return m_checkpoint;
00105 }
00106 
00107 SigC::Signal1<void, pqxxobject::transaction&>&
00108 transaction::signal_commit()
00109 {
00110   return m_signal_commit;
00111 }
00112 
00113 SigC::Signal1<void, pqxxobject::transaction&>&
00114 transaction::signal_abort()
00115 {
00116   return m_signal_abort;
00117 }
00118 
00119 SigC::Signal1<void, pqxxobject::transaction&>&
00120 transaction::signal_refresh()
00121 {
00122   return m_signal_refresh;
00123 }
00124 
00125 void
00126 transaction::begin(const std::string& name)
00127 {
00128 #ifdef PQXXOBJECT_DEBUG
00129   std::cerr << "transaction::begin";
00130 #endif
00131   try
00132     {
00133       ++m_recursion_level;
00134 #ifdef PQXXOBJECT_DEBUG
00135        std::cerr << "(" << m_recursion_level << ")" << std::endl;
00136 #endif
00137       if (m_recursion_level == 1)
00138         _begin(name);
00139     }
00140   catch (const std::exception& e)
00141     {
00142       _end();
00143       throw DatabaseError(e.what());
00144     }
00145 }
00146 
00147 void
00148 transaction::end()
00149 {
00150 #ifdef PQXXOBJECT_DEBUG
00151    std::cerr << "transaction::end";
00152    std::cerr << "(" << m_recursion_level << ")" << std::endl;
00153 #endif
00154   try
00155     {
00156       if (m_recursion_level > 0)
00157         {
00158           --m_recursion_level;
00159           if (m_recursion_level == 0)
00160             {
00161               _end();
00162             }
00163         }
00164       else
00165         _end();
00166     }
00167   catch (const std::exception& e)
00168     {
00169       throw DatabaseError(e.what());
00170     }
00171 }
00172 
00173 pqxx::result
00174 transaction::exec(const std::string& query)
00175 {
00176 #ifdef PQXXOBJECT_DEBUG
00177 //   std::cerr << "transaction::exec" << std::endl;
00178 #endif
00179   try
00180     {
00181       if (m_state == STATE_EXECUTING)
00182         return m_transaction->exec(query);
00183 #ifdef PQXXOBJECT_DEBUG
00184 //       else if (m_state == STATE_ABORTING || m_state == STATE_COMMITTING)
00185 //      throw Abort
00186 #endif
00187       else
00188         throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00189     }
00190   catch (const std::exception& e)
00191     {
00192       throw DatabaseError(e.what());
00193     }
00194 }
00195 
00196 pqxx::result
00197 transaction::exec(const std::ostringstream& query)
00198 {
00199   return exec(query.str());
00200 }
00201 
00202 pqxx::result
00203 transaction::exec(const query& query)
00204 {
00205   return exec(query.str());
00206 }
00207 
00208 pqxx::result::size_type
00209 transaction::exec_noresult(const std::string& query)
00210 {
00211 #ifdef PQXXOBJECT_DEBUG
00212 //   std::cerr << "transaction::exec_noresult" << std::endl;
00213 #endif
00214   try
00215     {
00216       if (m_state == STATE_EXECUTING)
00217         {
00218           pqxx::result R = exec(query);
00219           return R.affected_rows();
00220         }
00221 #ifdef PQXXOBJECT_DEBUG
00222 //       else if (m_state == STATE_ABORTING || m_state == STATE_COMMITTING)
00223 //      throw Abort
00224 #endif
00225       else
00226         throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00227     }
00228   catch (const std::exception& e)
00229     {
00230       throw DatabaseError(e.what());
00231     }
00232 }
00233 
00234 pqxx::result::size_type
00235 transaction::exec_noresult(const std::ostringstream& query)
00236 {
00237   return exec_noresult(query.str());
00238 }
00239 
00240 pqxx::result::size_type
00241 transaction::exec_noresult(const query& query)
00242 {
00243   return exec_noresult(query.str());
00244 }
00245 
00246 pqxx::result::size_type
00247 transaction::perform(const std::string& query,
00248                      pqxx::result::size_type min_rows,
00249                      pqxx::result::size_type max_rows)
00250 {
00251 #ifdef PQXXOBJECT_DEBUG
00252 //   std::cerr << "transaction::perform" << std::endl;
00253 #endif
00254   try
00255     {
00256       if (m_state == STATE_EXECUTING)
00257         {
00258           begin("transaction::perform()");
00259 
00260           pqxx::result::size_type rows;
00261           rows = exec_noresult(query);
00262 
00263           // Commit if the number of altered rows is greater or equal to
00264           // min_rows, or if min_rows is 0.
00265           if ((rows >= min_rows || min_rows == 0) &&
00266               (rows <= max_rows || max_rows == 0))
00267             commit();
00268           else
00269             abort();
00270 
00271           return rows;
00272         }
00273       else
00274 //       else if (m_state == STATE_ABORTING || m_state == STATE_COMMITTING)
00275 //      throw Abort
00276         throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00277     }
00278   catch (const std::exception& e)
00279     {
00280       throw DatabaseError(e.what());
00281     }
00282 
00283   // This should never be reached.
00284   return 0;
00285 }
00286 
00287 pqxx::result::size_type
00288 transaction::perform(const std::ostringstream& query,
00289                      pqxx::result::size_type min_rows,
00290                      pqxx::result::size_type max_rows)
00291 {
00292   return perform(query.str(), min_rows, max_rows);
00293 }
00294 
00295 pqxx::result::size_type
00296 transaction::perform(const query& query,
00297                      pqxx::result::size_type min_rows,
00298                      pqxx::result::size_type max_rows)
00299 {
00300   return perform(query.str(), min_rows, max_rows);
00301 }
00302 
00303 void
00304 transaction::commit()
00305 {
00306   commit(true);
00307 }
00308 
00309 void
00310 transaction::abort()
00311 {
00312   abort(true);
00313 }
00314 
00315 void
00316 transaction::_begin(const std::string& name)
00317 {
00318 #ifdef PQXXOBJECT_DEBUG
00319 //   std::cerr << "transaction::_begin" << std::endl;
00320 #endif
00321   assert (m_transaction == NULL);
00322 
00323   m_transaction = new pqxx::transaction<> (m_connection, name);
00324   m_state = STATE_EXECUTING;
00325 }
00326 
00327 void
00328 transaction::_end()
00329 {
00330 #ifdef PQXXOBJECT_DEBUG
00331 //   std::cerr << "transaction::_end" << std::endl;
00332 #endif
00333   if (m_transaction)
00334     {
00335       delete m_transaction;
00336       m_transaction = 0;
00337       m_state = STATE_NONE;
00338       m_recursion_level = 0;
00339       m_checkpoint = false;
00340     }
00341 }
00342 
00343 void transaction::commit(bool refresh)
00344 {
00345 #ifdef PQXXOBJECT_DEBUG
00346   std::cerr << "transaction::commit" << std::endl;
00347 #endif
00348   try
00349     {
00350       if (m_recursion_level == 1)
00351         {
00352           _commit(refresh);
00353         }
00354       else // end() is implict above
00355         end();
00356     }
00357   catch (const std::exception& e)
00358     {
00359       _end();
00360       throw DatabaseError(e.what());
00361     }
00362 }
00363 
00364 void
00365 transaction::_commit(bool refresh)
00366 {
00367   m_state = STATE_COMMITTING;
00368   m_transaction->commit();
00369   m_state = STATE_COMMITTED;
00370   end();
00371 
00372   // Start a new transaction for object refresh phase.
00373   if (refresh == true)
00374     {
00375       // Cbort actions.
00376       begin("pqxxobject::commit[object refresh]");
00377       m_signal_commit.emit(*this);
00378       m_signal_commit.clear();
00379       m_signal_abort.clear();
00380       m_signal_refresh.emit(*this);
00381       m_signal_refresh.clear();
00382       // Refresh cleanup.
00383       m_signal_commit.emit(*this);
00384       m_signal_commit.clear();
00385       m_signal_abort.clear();
00386       commit(false);
00387     }
00388 }
00389 
00390 void
00391 transaction::abort(bool rollback)
00392 {
00393 #ifdef PQXXOBJECT_DEBUG
00394   std::cerr << "transaction::abort" << std::endl;
00395 #endif
00396   try
00397     {
00398       m_state = STATE_ABORTING;
00399       if (m_recursion_level == 1)
00400         {
00401           _abort(rollback);
00402         }
00403       else // end() is implict above
00404         end();
00405     }
00406   catch (const std::exception& e)
00407     {
00408       _end();
00409       throw DatabaseError(e.what());
00410     }
00411 }
00412 void
00413 transaction::_abort(bool rollback)
00414 {
00415   m_state = STATE_ABORTING;
00416   m_transaction->abort();
00417   m_state = STATE_ABORTED;
00418   end();
00419 
00420   // Start a new transaction for object refresh phase.
00421   if (rollback == true)
00422     {
00423       // Abort actions.
00424       begin("pqxxobject::abort[object rollback]");
00425       m_signal_abort.emit(*this);
00426       m_signal_commit.clear();
00427       m_signal_abort.clear();
00428       m_signal_refresh.emit(*this);
00429       m_signal_refresh.clear();
00430       // Refresh cleanup.
00431       m_transaction->commit();
00432       m_signal_commit.emit(*this);
00433       m_signal_commit.clear();
00434       m_signal_abort.clear();
00435       commit(false);
00436     }
00437 }
00438 

Generated on Sat May 22 18:33:58 2004 for pqxxobject API Reference by doxygen 1.3.6-20040222