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

pqxx-object/transaction.cc

Go to the documentation of this file.
00001 // database transaction convenience wrapper                      -*- C++ -*-
00002 // $Id: transaction.cc,v 1.9 2004/02/20 22:49:15 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 
00042 #include <sigc++/object.h>
00043 
00044 #include <pqxx-object/transaction.h>
00045 
00046 using namespace pqxxobject;
00047 
00048 namespace
00049 {
00050   std::string describe_status(transaction::state current_state)
00051   {
00052     std::string status;
00053     switch (current_state)
00054       {
00055       case transaction::STATE_EXECUTING:
00056         status = "the current transaction is executing normally";
00057         break;
00058       case transaction::STATE_ABORTING:
00059         status = "the current transaction is in the process of aborting";
00060         break;
00061       case transaction::STATE_COMMITTING:
00062         status = "the current transaction is in the process of committing";
00063         break;
00064       case transaction::STATE_NONE:
00065         status = "there is no current transaction";
00066         break;
00067       case transaction::STATE_ABORTED:
00068         status = "the current transaction is aborted";
00069         break;
00070       case transaction::STATE_COMMITTED:
00071         status = "the current transaction is committed";
00072         break;
00073       default:
00074         status = "the current transaction status is unknown";
00075       }
00076     return status;
00077   }
00078 }
00079 
00080 transaction::transaction(pqxx::connection& conn,
00081                          pqxx::transaction<>*& tran):
00082   m_connection(conn),
00083   m_transaction(tran),
00084   m_state(STATE_NONE),
00085   m_recursion_level(0),
00086   m_checkpoint(false)
00087 {
00088 }
00089 
00090 transaction::~transaction()
00091 {
00092 }
00093 
00094 void
00095 transaction::set_checkpoint(bool checkpoint_exists)
00096 {
00097   m_checkpoint = checkpoint_exists;
00098 }
00099 
00100 bool
00101 transaction::get_checkpoint() const
00102 {
00103   return m_checkpoint;
00104 }
00105 
00106 SigC::Signal1<void, pqxxobject::transaction&>&
00107 transaction::signal_commit()
00108 {
00109   return m_signal_commit;
00110 }
00111 
00112 SigC::Signal1<void, pqxxobject::transaction&>&
00113 transaction::signal_abort()
00114 {
00115   return m_signal_abort;
00116 }
00117 
00118 SigC::Signal1<void, pqxxobject::transaction&>&
00119 transaction::signal_refresh()
00120 {
00121   return m_signal_refresh;
00122 }
00123 
00124 void
00125 transaction::begin(const std::string& name)
00126 {
00127 #ifdef PQXX_OBJECT_DEBUG
00128   std::cerr << "transaction::begin";
00129 #endif
00130   try
00131     {
00132       ++m_recursion_level;
00133 #ifdef PQXX_OBJECT_DEBUG
00134        std::cerr << "(" << m_recursion_level << ")" << std::endl;
00135 #endif
00136       if (m_recursion_level == 1)
00137         _begin(name);
00138     }
00139   catch (const std::exception& e)
00140     {
00141       _end();
00142       throw DatabaseError(e.what());
00143     }
00144 }
00145 
00146 void
00147 transaction::end()
00148 {
00149 #ifdef PQXX_OBJECT_DEBUG
00150    std::cerr << "transaction::end";
00151    std::cerr << "(" << m_recursion_level << ")" << std::endl;
00152 #endif
00153   try
00154     {
00155       if (m_recursion_level > 0)
00156         {
00157           --m_recursion_level;
00158           if (m_recursion_level == 0)
00159             {
00160               _end();
00161             }
00162         }
00163       else
00164         _end();
00165     }
00166   catch (const std::exception& e)
00167     {
00168       throw DatabaseError(e.what());
00169     }
00170 }
00171 
00172 pqxx::result
00173 transaction::exec(const std::string& query)
00174 {
00175 #ifdef PQXX_OBJECT_DEBUG
00176 //   std::cerr << "transaction::exec" << std::endl;
00177 #endif
00178   try
00179     {
00180       if (m_state == STATE_EXECUTING)
00181         return m_transaction->exec(query);
00182 #ifdef PQXX_OBJECT_DEBUG
00183 //       else if (m_state == STATE_ABORTING || m_state == STATE_COMMITTING)
00184 //      throw Abort
00185 #endif
00186       else
00187         throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00188     }
00189   catch (const std::exception& e)
00190     {
00191       throw DatabaseError(e.what());
00192     }
00193 }
00194 
00195 pqxx::result::size_type
00196 transaction::exec_noresult(const std::string& query)
00197 {
00198 #ifdef PQXX_OBJECT_DEBUG
00199 //   std::cerr << "transaction::exec_noresult" << std::endl;
00200 #endif
00201   try
00202     {
00203       if (m_state == STATE_EXECUTING)
00204         {
00205           pqxx::result R = exec(query);
00206           return R.affected_rows();
00207         }
00208 #ifdef PQXX_OBJECT_DEBUG
00209 //       else if (m_state == STATE_ABORTING || m_state == STATE_COMMITTING)
00210 //      throw Abort
00211 #endif
00212       else
00213         throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00214     }
00215   catch (const std::exception& e)
00216     {
00217       throw DatabaseError(e.what());
00218     }
00219 }
00220 
00221 pqxx::result::size_type
00222 transaction::perform(const std::string& query,
00223                      pqxx::result::size_type min_rows,
00224                      pqxx::result::size_type max_rows)
00225 {
00226 #ifdef PQXX_OBJECT_DEBUG
00227 //   std::cerr << "transaction::perform" << std::endl;
00228 #endif
00229   try
00230     {
00231       if (m_state == STATE_EXECUTING)
00232         {
00233           begin("transaction::perform()");
00234 
00235           pqxx::result::size_type rows;
00236           rows = exec_noresult(query);
00237 
00238           // Commit if the number of altered rows is greater or equal to
00239           // min_rows, or if min_rows is 0.
00240           if ((rows >= min_rows || min_rows == 0) &&
00241               (rows <= max_rows || max_rows == 0))
00242             commit();
00243           else
00244             abort();
00245 
00246           return rows;
00247         }
00248       else
00249 //       else if (m_state == STATE_ABORTING || m_state == STATE_COMMITTING)
00250 //      throw Abort
00251         throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00252     }
00253   catch (const std::exception& e)
00254     {
00255       throw DatabaseError(e.what());
00256     }
00257 
00258   // This should never be reached.
00259   return 0;
00260 }
00261 
00262 void
00263 transaction::commit()
00264 {
00265   commit(true);
00266 }
00267 
00268 void
00269 transaction::abort()
00270 {
00271   abort(true);
00272 }
00273 
00274 void
00275 transaction::_begin(const std::string& name)
00276 {
00277 #ifdef PQXX_OBJECT_DEBUG
00278 //   std::cerr << "transaction::_begin" << std::endl;
00279 #endif
00280   assert (m_transaction == NULL);
00281 
00282   m_transaction = new pqxx::transaction<> (m_connection, name);
00283   m_state = STATE_EXECUTING;
00284 }
00285 
00286 void
00287 transaction::_end()
00288 {
00289 #ifdef PQXX_OBJECT_DEBUG
00290 //   std::cerr << "transaction::_end" << std::endl;
00291 #endif
00292   if (m_transaction)
00293     {
00294       delete m_transaction;
00295       m_transaction = 0;
00296       m_state = STATE_NONE;
00297       m_recursion_level = 0;
00298       m_checkpoint = false;
00299     }
00300 }
00301 
00302 void transaction::commit(bool refresh)
00303 {
00304 #ifdef PQXX_OBJECT_DEBUG
00305   std::cerr << "transaction::commit" << std::endl;
00306 #endif
00307   try
00308     {
00309       if (m_recursion_level == 1)
00310         {
00311           _commit(refresh);
00312         }
00313       else // end() is implict above
00314         end();
00315     }
00316   catch (const std::exception& e)
00317     {
00318       _end();
00319       throw DatabaseError(e.what());
00320     }
00321 }
00322 
00323 void
00324 transaction::_commit(bool refresh)
00325 {
00326   m_state = STATE_COMMITTING;
00327   m_transaction->commit();
00328   m_state = STATE_COMMITTED;
00329   end();
00330 
00331   // Start a new transaction for object refresh phase.
00332   if (refresh == true)
00333     {
00334       // Cbort actions.
00335       begin("pqxxobject::commit[object refresh]");
00336       m_signal_commit.emit(*this);
00337       m_signal_commit.clear();
00338       m_signal_abort.clear();
00339       m_signal_refresh.emit(*this);
00340       m_signal_refresh.clear();
00341       // Refresh cleanup.
00342       m_signal_commit.emit(*this);
00343       m_signal_commit.clear();
00344       m_signal_abort.clear();
00345       commit(false);
00346     }
00347 }
00348 
00349 void
00350 transaction::abort(bool rollback)
00351 {
00352 #ifdef PQXX_OBJECT_DEBUG
00353   std::cerr << "transaction::abort" << std::endl;
00354 #endif
00355   try
00356     {
00357       m_state = STATE_ABORTING;
00358       if (m_recursion_level == 1)
00359         {
00360           _abort(rollback);
00361         }
00362       else // end() is implict above
00363         end();
00364     }
00365   catch (const std::exception& e)
00366     {
00367       _end();
00368       throw DatabaseError(e.what());
00369     }
00370 }
00371 void
00372 transaction::_abort(bool rollback)
00373 {
00374   m_state = STATE_ABORTING;
00375   m_transaction->abort();
00376   m_state = STATE_ABORTED;
00377   end();
00378 
00379   // Start a new transaction for object refresh phase.
00380   if (rollback == true)
00381     {
00382       // Abort actions.
00383       begin("pqxxobject::abort[object rollback]");
00384       m_signal_abort.emit(*this);
00385       m_signal_commit.clear();
00386       m_signal_abort.clear();
00387       m_signal_refresh.emit(*this);
00388       m_signal_refresh.clear();
00389       // Refresh cleanup.
00390       m_transaction->commit();
00391       m_signal_commit.emit(*this);
00392       m_signal_commit.clear();
00393       m_signal_abort.clear();
00394       commit(false);
00395     }
00396 }
00397 

Generated on Thu Apr 1 12:53:10 2004 for pqxx-object API Reference by doxygen 1.3.5