Revert "Remove mysql and oracle resources for anna-ericsson project"
[anna.git] / source / dbms.oracle / InputBind.cpp
diff --git a/source/dbms.oracle/InputBind.cpp b/source/dbms.oracle/InputBind.cpp
new file mode 100644 (file)
index 0000000..c554843
--- /dev/null
@@ -0,0 +1,208 @@
+// 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 <oci.h>
+
+#include <time.h>
+
+#include <anna/config/defines.hpp>
+#include <anna/core/functions.hpp>
+#include <anna/core/DataBlock.hpp>
+#include <anna/core/tracing/Logger.hpp>
+
+#include <anna/dbms/Float.hpp>
+#include <anna/dbms/ShortBlock.hpp>
+#include <anna/dbms/Date.hpp>
+#include <anna/dbms/TimeStamp.hpp>
+#include <anna/dbms/Database.hpp>
+
+#include <anna/dbms.oracle/oracle.hpp>
+
+using namespace std;
+using namespace anna;
+
+InputBind::InputBind(const char* name, dbms::Data&  data) :
+  dbms::InputBind(name, data),
+  BaseBind(data),
+  a_ociBind(NULL) {
+}
+
+InputBind::~InputBind() {
+}
+
+// Slo se invoca una vez. Establece las variables atrav� de las que nos vamos a poder
+// comunicar con Oracle, para indicar la longitud de una variable, o su estado de nulo o
+// no nulo.
+void InputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos)
+throw(RuntimeException, dbms::DatabaseException) {
+  if(a_ociBind != NULL)
+    return;
+
+  Data& data = anna::dbms::Bind::getData();
+
+  if(data.getType() == Data::Type::LongBlock) {
+    string msg("anna::dbms::oracle::InputBind::prepare | ");
+    msg += data.asString();
+    msg += " | This RDBMS doesn't support BLOB type as BindInput (see anna::dbms::OutputBind::write)";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  Database& database = static_cast <Database&>(dbmsStatement->getDatabase());
+  OCIError* error = database.getErrorHandler();
+  Statement* statement(static_cast <Statement*>(dbmsStatement));
+  oci_param ociparam = getOCIParam(database, static_cast <oracle::Connection*>(connection), data);
+
+  if(data.isNulleable() == false) {
+    anna_dbms_oracle_check(
+      OCIBindByPos(
+        *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
+        0, ociparam.length, 0, 0, 0, OCI_DEFAULT
+      ),
+      error
+    );
+  } else {
+    anna_dbms_oracle_check(
+      OCIBindByPos(
+        *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
+        &a_nullIndicator, ociparam.length, 0, 0, 0, OCI_DEFAULT
+      ),
+      error
+    );
+  }
+
+  LOGDEBUG(
+    std::string msg("anna::dbms::oracle::InputBind::prepare | ");
+    msg += asString();
+    msg += " | Sentence: ";
+    msg += statement->getName();
+    msg += " | Position: ";
+    msg += functions::asString(pos);
+    Logger::debug(msg, ANNA_FILE_LOCATION)
+  );
+}
+
+//-------------------------------------------------------------------------------
+// Establece la informacin mediante la que conectamos con Oracle. Todos los
+// par�etros que modificamos en �te m�odo tienen efecto en la llamada a Oracle
+// debido cmo hemos invocamo al m�odo OCIBindByPos.
+//
+// Todo esto se podr� haber hecho en la anna::dbms::DataBlock pero exigir�
+// definir una clase distinta para cada RDBMS. Creo que los Binds particulares de
+// cada base de datos se ocupen toda la complejidad de convertir los datos.
+//-------------------------------------------------------------------------------
+void InputBind::code() const
+throw(RuntimeException) {
+  InputBind* _this = const_cast <InputBind*>(this);
+  Data& data = _this->getData();
+
+  if((_this->a_nullIndicator = data.isNull() ? -1 : 0) == -1)
+    return;
+
+  switch(data.getType())  {
+  case Data::Type::String:
+    throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::String", ANNA_FILE_LOCATION);
+    break;
+  case Data::Type::Integer:
+    throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::Integer", ANNA_FILE_LOCATION);
+    break;
+  case Data::Type::Float:
+    codeFloat(data);
+    break;
+  case Data::Type::ShortBlock:
+    codeShortBlock(data);
+    break;
+  case Data::Type::LongBlock:
+    throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::LongBlock", ANNA_FILE_LOCATION);
+    break;
+  case Data::Type::Date:
+  case Data::Type::TimeStamp:
+
+    try {
+      codeDate(data);
+    } catch(DatabaseException& edb) {
+      throw RuntimeException(edb);
+    }
+
+    break;
+  }
+}
+
+/**
+ * Transfiere el valor numerico del float, al buffer reservado para
+ * ubiucarlo como una cadena. Ã\89ste buffer es el que está "conectado" con
+ * Oracle (tm).
+ */
+void InputBind::codeFloat(dbms::Data& data) const
+throw() {
+  dbms::Float& _float = static_cast <dbms::Float&>(data);
+  InputBind* _this = const_cast <InputBind*>(this);
+  char* buffer = (char*) _this->a_ofb->getData();
+  snprintf(buffer, FloatSize, _float.getFormat(), _float.getValue());
+  const char decimalPoint = oracle::Database::getDecimalPoint();
+
+  if(decimalPoint != 0) {
+    char* point = anna_strchr(buffer, '.');
+
+    if(point != NULL)
+      *point = decimalPoint;
+  }
+}
+
+void InputBind::codeShortBlock(dbms::Data& data) const
+throw() {
+  const int length = static_cast <dbms::ShortBlock&>(data).getSize();
+  InputBind* _this = const_cast <InputBind*>(this);
+
+  if(length == 0) {
+    _this->a_ofb->clear();
+    _this->a_length = 0;
+    return;
+  }
+
+  const char* src = (const char*) data.getBuffer();
+
+  char* dest = const_cast <char*>(a_ofb->getData());
+
+  int j = 0;
+
+  for(int i = 0; i < length; i ++) {
+    dest [j ++] = asCharacter((src [i] & 0xf0) >> 4);
+    dest [j ++] = asCharacter(src [i] & 0x0f);
+  }
+
+  dest [j ++] = 0;
+  _this->a_length = j;
+}
+
+void InputBind::codeDate(dbms::Data& data) const
+throw(RuntimeException, dbms::DatabaseException) {
+  dbms::Date& date = static_cast <dbms::Date&>(data);
+  ub4 fsec(0);
+
+  if(data.getType() == Data::Type::TimeStamp)
+    fsec = static_cast <dbms::TimeStamp&>(data).getFractionalSecond() * 1000;
+
+  anna_dbms_oracle_check(
+    OCIDateTimeConstruct(
+      a_datetime.env, a_datetime.error, a_datetime.handle,
+      date.getYear(), date.getMonth(), date.getDay(), date.getHour(), date.getMinute(), date.getSecond(), fsec, NULL, 0
+    ),
+    a_datetime.error
+  );
+  ub4 errorMask(0);
+  anna_dbms_oracle_check(
+    OCIDateTimeCheck(a_datetime.env, a_datetime.error, a_datetime.handle, &errorMask),
+    a_datetime.error
+  );
+
+  if(errorMask != 0) {
+    string msg(data.asString());
+    msg += anna::functions::asHexText(" | Invalid date | ErrorCode: ", (int) errorMask);
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+}