--- /dev/null
+// ANNA - Anna is Not Nothingness Anymore //
+// //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
+// //
+// See project site at http://redmine.teslayout.com/projects/anna-suite //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
+
+
+#include <anna/config/defines.hpp>
+#include <anna/core/tracing/TraceMethod.hpp>
+
+#include <anna/xml/Node.hpp>
+#include <anna/xml/Attribute.hpp>
+
+#include <anna/dbms/Connection.hpp>
+#include <anna/dbms/Database.hpp>
+#include <anna/dbms/Statement.hpp>
+#include <anna/dbms/Statement.hpp>
+
+using namespace std;
+using namespace anna;
+
+//-----------------------------------------------------------------------------------------------------------
+// (1) Si no tiene variables de salida => consideramos que es un update, insert o delete.
+//-----------------------------------------------------------------------------------------------------------
+dbms::ResultCode dbms::Connection::execute(Statement* statement)
+throw(RuntimeException, dbms::DatabaseException) {
+ if(statement == NULL) {
+ string msg(asString());
+ msg += " | Cannot execute a NULL sentence";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ LOGMETHOD(TraceMethod ttmm("dbms::Connection", "execute", ANNA_FILE_LOCATION));
+ LOGDEBUG(
+ string msg("Using Connection | ");
+ msg += asString();
+ Logger::debug(msg, ANNA_FILE_LOCATION)
+ );
+ Guard guard(statement, "Statement from dbms::Connection::execute");
+ const Microsecond init = functions::hardwareClock();
+
+ if(statement->a_prepared == false) {
+ statement->prepare(this);
+ statement->a_prepared = true;
+ }
+
+ LOGDEBUG(
+ string msg("dbms::Connection::execute | ");
+ msg += statement->asString();
+ Logger::debug(msg, ANNA_FILE_LOCATION);
+ );
+
+ if(statement->requiresCommit() == true && a_rollbackPending == true) { // (1)
+ string msg("dbms::Connection::execute | ");
+ msg += asString();
+ msg += " | Connection has pending ROLLBACKS for execution";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ ResultCode result;
+ int tryCounter = 0;
+
+ while(true) {
+ result = statement->execute(this);
+
+ if(result.lostConnection() == false)
+ break;
+
+ string msg = asString();
+ msg += " | ";
+ msg += statement->asString();
+ msg += " | ";
+ msg += result.asString();
+ Logger::alert(msg, ANNA_FILE_LOCATION);
+ a_database.recover(*this, ++ tryCounter);
+ }
+
+ statement->measureTiming(init, functions::hardwareClock());
+
+ if(result.successful() == false && result.notFound() == false) {
+ string msg(asString());
+ msg += " | Sentence: ";
+ msg += statement->getName();
+ Logger::write(Logger::Error, msg, result.asString(), ANNA_FILE_LOCATION);
+ }
+
+ if(statement->requiresCommit() == true) { // (1)
+ if(result.successful() == false) {
+ if(statement->isCritical() == true) {
+ a_rollbackPending = true;
+ throw DatabaseException(statement->getName(), result, ANNA_FILE_LOCATION);
+ }
+ } else {
+ a_commitPending ++;
+
+ if(a_maxCommitPending > 0 && a_commitPending > a_maxCommitPending) {
+ commit();
+ a_commitPending = 0;
+ a_rollbackPending = false;
+ }
+ }
+ }
+
+ return result;
+}
+
+//------------------------------------------------------------------------------------------------
+// (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas
+// y los volcados de contexto.
+//------------------------------------------------------------------------------------------------
+void dbms::Connection::commit()
+throw(RuntimeException, dbms::DatabaseException) {
+ LOGINFORMATION(
+ string msg("dbms::Connection::commit | ");
+ msg += asString();
+ Logger::information(msg, ANNA_FILE_LOCATION);
+ );
+
+ if(isAvailable() == false) {
+ string msg(asString());
+ msg += " | Unavailable connection";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ do_commit();
+ a_commitPending = 0; // (1)
+ a_rollbackPending = false;
+}
+
+//------------------------------------------------------------------------------------------------
+// (1) Esto no es estrictamente necesario, pero lo hacemos para que no nos despisten las trazas
+// y los volcados de contexto.
+//------------------------------------------------------------------------------------------------
+void dbms::Connection::rollback()
+throw() {
+ LOGWARNING(
+ string msg("dbms::Connection::rollback | ");
+ msg += asString();
+ Logger::warning(msg, ANNA_FILE_LOCATION);
+ );
+
+ if(isAvailable() == false) {
+ string msg(asString());
+ msg += " | Unavailable connection";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ do_rollback();
+ a_commitPending = 0;
+ a_rollbackPending = false; // (1)
+}
+
+void dbms::Connection::lock()
+throw(RuntimeException) {
+ if(isAvailable() == false) {
+ string msg(asString());
+ msg += " | Unavailable connection";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ comm::Resource::lock();
+
+ if(a_lockingCounter ++ == 0) {
+ a_commitPending = 0;
+ a_rollbackPending = false;
+
+ try {
+ if(do_beginTransaction() == true)
+ a_commitPending = 1;
+ } catch(dbms::DatabaseException& edb) {
+ throw RuntimeException(edb);
+ }
+ }
+
+ LOGDEBUG(
+ string msg("dbms::Connection::lock | ");
+ msg += asString();
+ Logger::debug(msg, ANNA_FILE_LOCATION)
+ );
+}
+
+void dbms::Connection::unlock()
+throw() {
+ LOGDEBUG(
+ string msg("dbms::Connection::unlock | ");
+ msg += asString();
+ Logger::debug(msg, ANNA_FILE_LOCATION)
+ );
+
+ if(-- a_lockingCounter <= 0) {
+ a_lockingCounter = 0;
+
+ try {
+ if(a_rollbackPending == true)
+ rollback();
+ else if(a_commitPending > 0)
+ commit();
+ } catch(Exception& ex) {
+ Logger::emergency(ex.getText(), ex.getFromFile(), ex.getFromLine());
+ }
+ }
+
+ comm::Resource::unlock();
+}
+
+string dbms::Connection::asString() const
+throw() {
+ string result("dbms::Connection { ");
+ result += comm::Resource::asString();
+ result += " | ";
+ result += a_database.asString();
+ result += " | user: ";
+ result += a_user;
+ result += functions::asText(" | LockingCounter: ", a_lockingCounter);
+ result += functions::asText(" | password: ******* | CommitPending: ", a_commitPending);
+ result += functions::asText(" | RollbackPending: ", a_rollbackPending);
+ return result += " }";
+}
+
+xml::Node* dbms::Connection::asXML(xml::Node* parent) const
+throw() {
+ xml::Node* result = comm::Resource::asXML(parent);
+ result->createAttribute("User", a_user);
+ result->createAttribute("LockingCounter", a_lockingCounter);
+ result->createAttribute("CommitPending", a_commitPending);
+ result->createAttribute("RollbackPending", functions::asString(a_rollbackPending));
+ return result;
+}
+