Revert "Remove mysql and oracle resources for anna-ericsson project"
[anna.git] / source / dbms / Connection.cpp
diff --git a/source/dbms/Connection.cpp b/source/dbms/Connection.cpp
new file mode 100644 (file)
index 0000000..9dcdcb5
--- /dev/null
@@ -0,0 +1,230 @@
+// 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;
+}
+