c554843791b528c98b2e2dcee7e6cd6a35559315
[anna.git] / source / dbms.oracle / InputBind.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <oci.h>
10
11 #include <time.h>
12
13 #include <anna/config/defines.hpp>
14 #include <anna/core/functions.hpp>
15 #include <anna/core/DataBlock.hpp>
16 #include <anna/core/tracing/Logger.hpp>
17
18 #include <anna/dbms/Float.hpp>
19 #include <anna/dbms/ShortBlock.hpp>
20 #include <anna/dbms/Date.hpp>
21 #include <anna/dbms/TimeStamp.hpp>
22 #include <anna/dbms/Database.hpp>
23
24 #include <anna/dbms.oracle/oracle.hpp>
25
26 using namespace std;
27 using namespace anna;
28
29 InputBind::InputBind(const char* name, dbms::Data&  data) :
30   dbms::InputBind(name, data),
31   BaseBind(data),
32   a_ociBind(NULL) {
33 }
34
35 InputBind::~InputBind() {
36 }
37
38 // Slo se invoca una vez. Establece las variables atrav� de las que nos vamos a poder
39 // comunicar con Oracle, para indicar la longitud de una variable, o su estado de nulo o
40 // no nulo.
41 void InputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos)
42 throw(RuntimeException, dbms::DatabaseException) {
43   if(a_ociBind != NULL)
44     return;
45
46   Data& data = anna::dbms::Bind::getData();
47
48   if(data.getType() == Data::Type::LongBlock) {
49     string msg("anna::dbms::oracle::InputBind::prepare | ");
50     msg += data.asString();
51     msg += " | This RDBMS doesn't support BLOB type as BindInput (see anna::dbms::OutputBind::write)";
52     throw RuntimeException(msg, ANNA_FILE_LOCATION);
53   }
54
55   Database& database = static_cast <Database&>(dbmsStatement->getDatabase());
56   OCIError* error = database.getErrorHandler();
57   Statement* statement(static_cast <Statement*>(dbmsStatement));
58   oci_param ociparam = getOCIParam(database, static_cast <oracle::Connection*>(connection), data);
59
60   if(data.isNulleable() == false) {
61     anna_dbms_oracle_check(
62       OCIBindByPos(
63         *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
64         0, ociparam.length, 0, 0, 0, OCI_DEFAULT
65       ),
66       error
67     );
68   } else {
69     anna_dbms_oracle_check(
70       OCIBindByPos(
71         *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
72         &a_nullIndicator, ociparam.length, 0, 0, 0, OCI_DEFAULT
73       ),
74       error
75     );
76   }
77
78   LOGDEBUG(
79     std::string msg("anna::dbms::oracle::InputBind::prepare | ");
80     msg += asString();
81     msg += " | Sentence: ";
82     msg += statement->getName();
83     msg += " | Position: ";
84     msg += functions::asString(pos);
85     Logger::debug(msg, ANNA_FILE_LOCATION)
86   );
87 }
88
89 //-------------------------------------------------------------------------------
90 // Establece la informacin mediante la que conectamos con Oracle. Todos los
91 // par�etros que modificamos en �te m�odo tienen efecto en la llamada a Oracle
92 // debido cmo hemos invocamo al m�odo OCIBindByPos.
93 //
94 // Todo esto se podr� haber hecho en la anna::dbms::DataBlock pero exigir�
95 // definir una clase distinta para cada RDBMS. Creo que los Binds particulares de
96 // cada base de datos se ocupen toda la complejidad de convertir los datos.
97 //-------------------------------------------------------------------------------
98 void InputBind::code() const
99 throw(RuntimeException) {
100   InputBind* _this = const_cast <InputBind*>(this);
101   Data& data = _this->getData();
102
103   if((_this->a_nullIndicator = data.isNull() ? -1 : 0) == -1)
104     return;
105
106   switch(data.getType())  {
107   case Data::Type::String:
108     throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::String", ANNA_FILE_LOCATION);
109     break;
110   case Data::Type::Integer:
111     throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::Integer", ANNA_FILE_LOCATION);
112     break;
113   case Data::Type::Float:
114     codeFloat(data);
115     break;
116   case Data::Type::ShortBlock:
117     codeShortBlock(data);
118     break;
119   case Data::Type::LongBlock:
120     throw RuntimeException("anna::dbms::oracle::InputBind::code not implemented for Data::Type::LongBlock", ANNA_FILE_LOCATION);
121     break;
122   case Data::Type::Date:
123   case Data::Type::TimeStamp:
124
125     try {
126       codeDate(data);
127     } catch(DatabaseException& edb) {
128       throw RuntimeException(edb);
129     }
130
131     break;
132   }
133 }
134
135 /**
136  * Transfiere el valor numerico del float, al buffer reservado para
137  * ubiucarlo como una cadena. Ã\89ste buffer es el que está "conectado" con
138  * Oracle (tm).
139  */
140 void InputBind::codeFloat(dbms::Data& data) const
141 throw() {
142   dbms::Float& _float = static_cast <dbms::Float&>(data);
143   InputBind* _this = const_cast <InputBind*>(this);
144   char* buffer = (char*) _this->a_ofb->getData();
145   snprintf(buffer, FloatSize, _float.getFormat(), _float.getValue());
146   const char decimalPoint = oracle::Database::getDecimalPoint();
147
148   if(decimalPoint != 0) {
149     char* point = anna_strchr(buffer, '.');
150
151     if(point != NULL)
152       *point = decimalPoint;
153   }
154 }
155
156 void InputBind::codeShortBlock(dbms::Data& data) const
157 throw() {
158   const int length = static_cast <dbms::ShortBlock&>(data).getSize();
159   InputBind* _this = const_cast <InputBind*>(this);
160
161   if(length == 0) {
162     _this->a_ofb->clear();
163     _this->a_length = 0;
164     return;
165   }
166
167   const char* src = (const char*) data.getBuffer();
168
169   char* dest = const_cast <char*>(a_ofb->getData());
170
171   int j = 0;
172
173   for(int i = 0; i < length; i ++) {
174     dest [j ++] = asCharacter((src [i] & 0xf0) >> 4);
175     dest [j ++] = asCharacter(src [i] & 0x0f);
176   }
177
178   dest [j ++] = 0;
179   _this->a_length = j;
180 }
181
182 void InputBind::codeDate(dbms::Data& data) const
183 throw(RuntimeException, dbms::DatabaseException) {
184   dbms::Date& date = static_cast <dbms::Date&>(data);
185   ub4 fsec(0);
186
187   if(data.getType() == Data::Type::TimeStamp)
188     fsec = static_cast <dbms::TimeStamp&>(data).getFractionalSecond() * 1000;
189
190   anna_dbms_oracle_check(
191     OCIDateTimeConstruct(
192       a_datetime.env, a_datetime.error, a_datetime.handle,
193       date.getYear(), date.getMonth(), date.getDay(), date.getHour(), date.getMinute(), date.getSecond(), fsec, NULL, 0
194     ),
195     a_datetime.error
196   );
197   ub4 errorMask(0);
198   anna_dbms_oracle_check(
199     OCIDateTimeCheck(a_datetime.env, a_datetime.error, a_datetime.handle, &errorMask),
200     a_datetime.error
201   );
202
203   if(errorMask != 0) {
204     string msg(data.asString());
205     msg += anna::functions::asHexText(" | Invalid date | ErrorCode: ", (int) errorMask);
206     throw RuntimeException(msg, ANNA_FILE_LOCATION);
207   }
208 }