00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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 {
00087 }
00088
00089 transaction::~transaction()
00090 {
00091 }
00092
00093 SigC::Signal1<void, pqxxobject::transaction&>&
00094 transaction::signal_commit()
00095 {
00096 return m_signal_commit;
00097 }
00098
00099 SigC::Signal1<void, pqxxobject::transaction&>&
00100 transaction::signal_abort()
00101 {
00102 return m_signal_abort;
00103 }
00104
00105 void
00106 transaction::begin(const std::string& name)
00107 {
00108
00109 try
00110 {
00111 ++m_recursion_level;
00112
00113 if (m_recursion_level == 1)
00114 _begin(name);
00115 }
00116 catch (const std::exception& e)
00117 {
00118 _end();
00119 throw DatabaseError(e.what());
00120 }
00121 }
00122
00123 void
00124 transaction::end()
00125 {
00126
00127
00128 try
00129 {
00130 if (m_recursion_level > 0)
00131 {
00132 --m_recursion_level;
00133 if (m_recursion_level == 0)
00134 {
00135 _end();
00136 }
00137 }
00138 else
00139 _end();
00140 }
00141 catch (const std::exception& e)
00142 {
00143 throw DatabaseError(e.what());
00144 }
00145 }
00146
00147 pqxx::result
00148 transaction::exec(const std::string& query)
00149 {
00150
00151 try
00152 {
00153 if (m_state == STATE_EXECUTING)
00154 return m_transaction->exec(query);
00155
00156
00157 else
00158 throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00159 }
00160 catch (const std::exception& e)
00161 {
00162 throw DatabaseError(e.what());
00163 }
00164 }
00165
00166 pqxx::result::size_type
00167 transaction::exec_noresult(const std::string& query)
00168 {
00169
00170 try
00171 {
00172 if (m_state == STATE_EXECUTING)
00173 {
00174 pqxx::result R = exec(query);
00175 return R.affected_rows();
00176 }
00177
00178
00179 else
00180 throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00181 }
00182 catch (const std::exception& e)
00183 {
00184 throw DatabaseError(e.what());
00185 }
00186 }
00187
00188 pqxx::result::size_type
00189 transaction::perform(const std::string& query,
00190 pqxx::result::size_type min_rows,
00191 pqxx::result::size_type max_rows)
00192 {
00193
00194 try
00195 {
00196 if (m_state == STATE_EXECUTING)
00197 {
00198 begin("transaction::perform()");
00199
00200 pqxx::result::size_type rows;
00201 rows = exec_noresult(query);
00202
00203
00204
00205 if ((rows >= min_rows || min_rows == 0) &&
00206 (rows <= max_rows || max_rows == 0))
00207 commit();
00208 else
00209 abort();
00210
00211 return rows;
00212 }
00213 else
00214
00215
00216 throw DatabaseError("The query could not be executed: " + describe_status(m_state));
00217 }
00218 catch (const std::exception& e)
00219 {
00220 _end();
00221 throw DatabaseError(e.what());
00222 }
00223
00224
00225 return 0;
00226 }
00227
00228 void
00229 transaction::commit()
00230 {
00231 commit(true);
00232 }
00233
00234 void
00235 transaction::abort()
00236 {
00237 abort(true);
00238 }
00239
00240 void
00241 transaction::_begin(const std::string& name)
00242 {
00243
00244 assert (m_transaction == NULL);
00245
00246 m_transaction = new pqxx::transaction<> (m_connection, name);
00247 m_state = STATE_EXECUTING;
00248 }
00249
00250 void
00251 transaction::_end()
00252 {
00253
00254 if (m_transaction)
00255 {
00256 delete m_transaction;
00257 m_transaction = 0;
00258 m_state = STATE_NONE;
00259 m_recursion_level = 0;
00260 }
00261 }
00262
00263 void transaction::commit(bool refresh)
00264 {
00265
00266 try
00267 {
00268 if (m_recursion_level == 1)
00269 {
00270 _commit(refresh);
00271 }
00272 else
00273 end();
00274 }
00275 catch (const std::exception& e)
00276 {
00277 _end();
00278 throw DatabaseError(e.what());
00279 }
00280 }
00281
00282 void
00283 transaction::_commit(bool refresh)
00284 {
00285 m_state = STATE_COMMITTING;
00286 m_transaction->commit();
00287 m_state = STATE_COMMITTED;
00288 end();
00289
00290
00291 if (refresh == true)
00292 {
00293 begin("pqxxobject::commit[object refresh]");
00294 m_signal_commit.emit(*this);
00295 m_signal_commit.clear();
00296 m_signal_abort.clear();
00297 commit(false);
00298 }
00299 }
00300
00301 void
00302 transaction::abort(bool refresh)
00303 {
00304
00305 try
00306 {
00307 m_state = STATE_ABORTING;
00308 if (m_recursion_level == 1)
00309 {
00310 _abort(refresh);
00311 }
00312 else
00313 end();
00314 }
00315 catch (const std::exception& e)
00316 {
00317 _end();
00318 throw DatabaseError(e.what());
00319 }
00320 }
00321 void
00322 transaction::_abort(bool refresh)
00323 {
00324 m_state = STATE_ABORTING;
00325 m_transaction->abort();
00326 m_state = STATE_ABORTED;
00327 end();
00328
00329
00330 if (refresh == true)
00331 {
00332 begin("pqxxobject::abort[object refresh]");
00333 m_signal_commit.emit(*this);
00334 m_signal_commit.clear();
00335 m_signal_abort.clear();
00336 m_transaction->commit();
00337 commit(false);
00338 }
00339 }
00340