diff --git a/include/trrep/client_context.hpp b/include/trrep/client_context.hpp index 6328349..24b22fe 100644 --- a/include/trrep/client_context.hpp +++ b/include/trrep/client_context.hpp @@ -120,6 +120,10 @@ namespace trrep * system. */ s_exec, + /*! + * Client handler is sending result to client. + */ + s_result, /*! * The client session is terminating. */ @@ -136,32 +140,40 @@ namespace trrep } /*! - * Virtual method which should be called before the client + * Method which should be called before the client * starts processing the command received from the application. * This method will wait until the possible synchronous * rollback for associated transaction has finished. * The method has a side effect of changing the client * context state to executing. * - * If overridden, the implementation should call base - * class method before any implementation specific operations. - * * \return Zero in case of success, non-zero in case of the * associated transaction was BF aborted. */ - virtual int before_command(); + int before_command(); /*! - * Virtual method which should be called before returning + * Method which should be called before returning * the control back to application which uses the DBMS system. * This method will check if the transaction associated to - * the connection has been aborted. This method has a side effect - * of changing the client state to idle. - * - * If overridden, the implementation should call base - * class metods after any implementation specifict operations. + * the connection has been aborted. Rollback is performed + * if needed. */ - virtual void after_command(); + void after_command_before_result(); + + /*! + * Method which should be called after returning the + * control back to application which uses the DBMS system. + * The method will do the check if the transaction associated + * to the connection has been aborted. If so, rollback is + * performed and the transaction is left to aborted state + * so that the client will get appropriate error on next + * command. + * + * This method has a side effect of changing state to + * idle. + */ + void after_command_after_result(); /*! * Before statement execution operations. @@ -176,7 +188,7 @@ namespace trrep * is not allowed to be executed due to read or write * isolation requirements. */ - virtual int before_statement(); + int before_statement(); /*! * After statement execution operations. @@ -187,7 +199,7 @@ namespace trrep * If overridden by the implementation, base class method * should be called after any implementation specific operations. */ - virtual void after_statement(); + void after_statement(); int start_transaction() { diff --git a/src/client_context.cpp b/src/client_context.cpp index af8ccc2..25d5529 100644 --- a/src/client_context.cpp +++ b/src/client_context.cpp @@ -40,9 +40,10 @@ int trrep::client_context::before_command() return 0; } -void trrep::client_context::after_command() +void trrep::client_context::after_command_before_result() { trrep::unique_lock lock(mutex_); + assert(state() == s_exec); if (transaction_.active() && transaction_.state() == trrep::transaction_context::s_must_abort) { @@ -54,6 +55,26 @@ void trrep::client_context::after_command() assert(transaction_.state() == trrep::transaction_context::s_aborted); assert(current_error() != trrep::e_success); } + state(lock, s_result); +} + +void trrep::client_context::after_command_after_result() +{ + trrep::unique_lock lock(mutex_); + assert(state() == s_result); + if (transaction_.active() && + transaction_.state() == trrep::transaction_context::s_must_abort) + { + // Note: Error is not overridden here as the result has already + // been sent to client. The error should be set in before_command() + // when the client issues next command. + lock.unlock(); + rollback(); + transaction_.after_statement(); + lock.lock(); + assert(transaction_.state() == trrep::transaction_context::s_aborted); + assert(current_error() != trrep::e_success); + } state(lock, s_idle); } @@ -103,10 +124,11 @@ void trrep::client_context::state( assert(lock.owns_lock()); static const char allowed[state_max_][state_max_] = { - /* idle exec quit */ - { 0, 1, 1}, /* idle */ - { 1, 0, 1}, /* exec */ - { 0, 0, 0} + /* idle exec result quit */ + { 0, 1, 0, 1}, /* idle */ + { 0, 0, 1, 0}, /* exec */ + { 1, 0, 0, 1}, /* result */ + { 0, 0, 0, 0} /* quit */ }; if (allowed[state_][state]) { diff --git a/src/client_context_test.cpp b/src/client_context_test.cpp index d4ed849..61b1748 100644 --- a/src/client_context_test.cpp +++ b/src/client_context_test.cpp @@ -25,5 +25,6 @@ BOOST_AUTO_TEST_CASE(client_context_test_error_codes) trrep_mock::bf_abort_unordered(cc); cc.after_statement(); - cc.after_command(); + cc.after_command_before_result(); + cc.after_command_after_result(); } diff --git a/src/dbms_simulator.cpp b/src/dbms_simulator.cpp index 46639b0..67875eb 100644 --- a/src/dbms_simulator.cpp +++ b/src/dbms_simulator.cpp @@ -392,7 +392,8 @@ private: } after_statement(); } - after_command(); + after_command_before_result(); + after_command_after_result(); return err; } diff --git a/src/server_context.cpp b/src/server_context.cpp index 0701d86..dd2b16a 100644 --- a/src/server_context.cpp +++ b/src/server_context.cpp @@ -323,7 +323,8 @@ int trrep::server_context::on_apply( if (not_replaying) { client_context.after_statement(); - client_context.after_command(); + client_context.after_command_before_result(); + client_context.after_command_after_result(); } assert(ret || txc.state() == trrep::transaction_context::s_committed);