--- /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 <oci.h>
+#include <orl.h>
+
+#include <anna/config/defines.hpp>
+#include <anna/core/tracing/Logger.hpp>
+#include <anna/core/DataBlock.hpp>
+
+#include <anna/dbms/Float.hpp>
+#include <anna/dbms/ShortBlock.hpp>
+#include <anna/dbms/LongBlock.hpp>
+#include <anna/dbms/String.hpp>
+#include <anna/dbms/Date.hpp>
+#include <anna/dbms/TimeStamp.hpp>
+
+#include <anna/dbms.oracle/oracle.hpp>
+
+using namespace anna;
+using namespace std;
+
+OutputBind::OutputBind(const char* name, dbms::Data& data) :
+ dbms::OutputBind(name, data),
+ BaseBind(data),
+ a_ociDefine(NULL) {
+}
+
+OutputBind::~OutputBind() {
+}
+
+void OutputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos)
+throw(dbms::DatabaseException) {
+ if(a_ociDefine != NULL)
+ return;
+
+ Database& database(static_cast <Database&>(dbmsStatement->getDatabase()));
+ OCIError* error = database.getErrorHandler();
+ Statement* statement(static_cast <Statement*>(dbmsStatement));
+ dbms::Data& data = anna::dbms::Bind::getData();
+ oci_param ociparam = getOCIParam(database, static_cast <oracle::Connection*>(connection), data);
+
+ if(data.isNulleable() == false) {
+ anna_dbms_oracle_check(
+ OCIDefineByPos(
+ *statement, &a_ociDefine, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
+ 0, ociparam.length, 0, OCI_DEFAULT
+ ),
+ error
+ );
+ } else {
+ anna_dbms_oracle_check(
+ OCIDefineByPos(
+ *statement, &a_ociDefine, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
+ &a_nullIndicator, ociparam.length, 0, OCI_DEFAULT
+ ),
+ error
+ );
+ }
+
+ LOGDEBUG(
+ std::string msg("anna::dbms::oracle::OutputBind::prepare | ");
+ msg += asString();
+ msg += " | Sentence: ";
+ msg += statement->getName();
+ msg += " | Position: ";
+ msg += functions::asString(pos);
+ Logger::debug(msg, ANNA_FILE_LOCATION)
+ );
+}
+
+//-------------------------------------------------------------------------------
+// Transfiere la informacin obtenida desde Oracle. Todos los comprobados en �te
+// m�odo son parametros que modificados al ejecuta la sentencia Oracle debido cmo
+// hemos invocamo al m�odo OCIDefineByPos
+//
+// 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.
+//
+// (1) Truco para fijar el contenido y la longitud actual.
+//-------------------------------------------------------------------------------
+void OutputBind::decode() const
+throw(RuntimeException) {
+ OutputBind* _this = const_cast <OutputBind*>(this);
+ char* str;
+ Data& data = _this->getData();
+ data.setNull(a_nullIndicator < 0);
+
+ switch(data.getType()) {
+ case Data::Type::String:
+ str = (char*) data.getBuffer();
+
+ if(data.isNull() == true)
+ *str = 0;
+ else
+ dbms::String::strip(str);
+
+ break;
+ case Data::Type::Integer:
+ throw RuntimeException("anna::dbms::oracle::OutputBind::decode not implemented for Data::Type::Integer", ANNA_FILE_LOCATION);
+ break;
+ case Data::Type::Float:
+ decodeFloat(data);
+ break;
+ case Data::Type::ShortBlock:
+ decodeShortBlock(data);
+ break;
+ case Data::Type::LongBlock:
+ decodeLongBlock(data);
+ break;
+ case Data::Type::Date:
+ case Data::Type::TimeStamp:
+
+ try {
+ decodeDate(data);
+ } catch(DatabaseException& edb) {
+ throw RuntimeException(edb);
+ }
+
+ break;
+ }
+}
+
+void OutputBind::decodeFloat(dbms::Data& data) const
+throw(RuntimeException) {
+ dbms::Float& _float = static_cast <dbms::Float&>(data);
+
+ if(data.isNull() == true) {
+ _float = 0.0;
+ return;
+ }
+
+ char* _data = (char*) a_ofb->getData();
+ const char decimalPoint = oracle::Database::getDecimalPoint();
+
+ if(decimalPoint != 0) {
+ char* point = anna_strchr(_data, decimalPoint);
+
+ if(point != NULL)
+ *point = '.';
+ }
+
+ sscanf(_data, _float.getFormat(), (float*) _float.getBuffer());
+}
+
+void OutputBind::decodeShortBlock(dbms::Data& data) const
+throw(RuntimeException) {
+ const anna::DataBlock& constdbms(static_cast <dbms::ShortBlock&>(data).getValue());
+ anna::DataBlock& dataBlock(const_cast <anna::DataBlock&>(constdbms));
+
+ if(data.isNull() == true) {
+ dataBlock.clear();
+ return;
+ }
+
+ const char* src = a_ofb->getData();
+
+ char* dest = (char*) dataBlock.getData();
+
+ unsigned char hex;
+
+ int j = 0;
+
+ for(int i = 1; i < a_length; i += 2, j ++) {
+ hex = asByte(src [i - 1]) << 4;
+ hex |= asByte(src [i]);
+ dest [j] = hex;
+ }
+
+ dataBlock.clear();
+ dataBlock.allocate(j); // (1)
+}
+
+//--------------------------------------------------------------------------------------------
+// (1) Offset = 1 => primer caracter.
+//--------------------------------------------------------------------------------------------
+void OutputBind::decodeLongBlock(dbms::Data& data) const
+throw(RuntimeException) {
+ const anna::DataBlock& constdbms(static_cast <dbms::LongBlock&>(data).getValue());
+ anna::DataBlock& dataBlock(const_cast <anna::DataBlock&>(constdbms));
+ dataBlock.clear();
+
+ if(data.isNull() == true)
+ return;
+
+ ub1* buffer;
+ ub4 maxLength;
+ ub4 length;
+ sword ret;
+ bool stop = false;
+ ub4 offset = 1; // (1)
+
+ try {
+ buffer = (ub1*) a_ofb->getData();
+ maxLength = a_ofb->getSize();
+ length = 0;
+
+ do {
+ ret = OCILobRead(a_blob.context, a_blob.error, a_blob.handle, &length, offset, buffer, maxLength, 0, 0, 0, SQLCS_IMPLICIT);
+
+ switch(ret) {
+ case OCI_SUCCESS:
+ dataBlock += anna::DataBlock((const char*) buffer, length, false);
+ stop = true;
+ break;
+ case OCI_NEED_DATA:
+ dataBlock += anna::DataBlock((const char*) buffer, length, false);
+ offset += length;
+ break;
+ default:
+ throw dbms::DatabaseException(oracle::ResultCode(ret, a_blob.error), ANNA_FILE_LOCATION);
+ }
+ } while(stop == false);
+ } catch(dbms::DatabaseException& edbms) {
+ throw RuntimeException(edbms);
+ }
+}
+
+void OutputBind::do_write(const dbms::LongBlock& data) const
+throw(RuntimeException, dbms::DatabaseException) {
+ const anna::DataBlock& dataBlock = data.getValue();
+
+ if(a_blob.handle == NULL) {
+ string msg("anna::dbms::oracle::OutputBind::do_write | ");
+ msg += data.asString();
+ msg += " | BLOB must be loaded before modification";
+ throw RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ ub1* buffer = (ub1*) dataBlock.getData();
+ ub4 length = dataBlock.getSize();
+ anna_dbms_oracle_check(
+ OCILobWrite(a_blob.context, a_blob.error, a_blob.handle, &length, (ub4) 1, buffer, length, OCI_ONE_PIECE, 0, 0, 0, SQLCS_IMPLICIT),
+ a_blob.error
+ );
+}
+
+void OutputBind::decodeDate(dbms::Data& data) const
+throw(RuntimeException, dbms::DatabaseException) {
+ if(data.isNull() == true)
+ return;
+
+ Date& date = static_cast <Date&>(data);
+ sb2 year;
+ ub1 month, day;
+ anna_dbms_oracle_check(
+ OCIDateTimeGetDate(a_datetime.env, a_datetime.error, a_datetime.handle, &year, &month, &day),
+ a_datetime.error
+ );
+ date.setYear(year);
+ date.setMonth(month);
+ date.setDay(day);
+ ub1 hour, min, sec;
+ ub4 fsec;
+ sword status = OCIDateTimeGetTime(a_datetime.env, a_datetime.error, a_datetime.handle, &hour, &min, &sec, &fsec);
+
+ if(status == OCI_SUCCESS) {
+ date.setHour(hour);
+ date.setMinute(min);
+ date.setSecond(sec);
+
+ if(data.getType() == Data::Type::TimeStamp)
+ static_cast <dbms::TimeStamp&>(data).setFractionalSecond(fsec / 1000);
+ } else {
+ date.setHour(0);
+ date.setMinute(0);
+ date.setSecond(0);
+
+ if(data.getType() == Data::Type::TimeStamp)
+ static_cast <dbms::TimeStamp&>(data).setFractionalSecond(0);
+ }
+}