Fix bug in json to xml convert library
[anna.git] / source / dbms.oracle / OutputBind.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 #include <orl.h>
11
12 #include <anna/config/defines.hpp>
13 #include <anna/core/tracing/Logger.hpp>
14 #include <anna/core/DataBlock.hpp>
15
16 #include <anna/dbms/Float.hpp>
17 #include <anna/dbms/ShortBlock.hpp>
18 #include <anna/dbms/LongBlock.hpp>
19 #include <anna/dbms/String.hpp>
20 #include <anna/dbms/Date.hpp>
21 #include <anna/dbms/TimeStamp.hpp>
22
23 #include <anna/dbms.oracle/oracle.hpp>
24
25 using namespace anna;
26 using namespace std;
27
28 OutputBind::OutputBind(const char* name, dbms::Data& data) :
29   dbms::OutputBind(name, data),
30   BaseBind(data),
31   a_ociDefine(NULL) {
32 }
33
34 OutputBind::~OutputBind() {
35 }
36
37 void OutputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos)
38 throw(dbms::DatabaseException) {
39   if(a_ociDefine != NULL)
40     return;
41
42   Database& database(static_cast <Database&>(dbmsStatement->getDatabase()));
43   OCIError* error = database.getErrorHandler();
44   Statement* statement(static_cast <Statement*>(dbmsStatement));
45   dbms::Data& data = anna::dbms::Bind::getData();
46   oci_param ociparam = getOCIParam(database, static_cast <oracle::Connection*>(connection), data);
47
48   if(data.isNulleable() == false) {
49     anna_dbms_oracle_check(
50       OCIDefineByPos(
51         *statement, &a_ociDefine, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
52         0, ociparam.length, 0, OCI_DEFAULT
53       ),
54       error
55     );
56   } else {
57     anna_dbms_oracle_check(
58       OCIDefineByPos(
59         *statement, &a_ociDefine, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
60         &a_nullIndicator, ociparam.length, 0, OCI_DEFAULT
61       ),
62       error
63     );
64   }
65
66   LOGDEBUG(
67     std::string msg("anna::dbms::oracle::OutputBind::prepare | ");
68     msg += asString();
69     msg += " | Sentence: ";
70     msg += statement->getName();
71     msg += " | Position: ";
72     msg += functions::asString(pos);
73     Logger::debug(msg, ANNA_FILE_LOCATION)
74   );
75 }
76
77 //-------------------------------------------------------------------------------
78 // Transfiere la informacin obtenida desde Oracle. Todos los comprobados en �te
79 // m�odo son parametros que modificados al ejecuta la sentencia Oracle debido cmo
80 // hemos invocamo al m�odo OCIDefineByPos
81 //
82 // Todo esto se podr� haber hecho en la anna::dbms::DataBlock pero exigir�
83 // definir una clase distinta para cada RDBMS. Creo que los Binds particulares de
84 // cada base de datos se ocupen toda la complejidad de convertir los datos.
85 //
86 // (1) Truco para fijar el contenido y la longitud actual.
87 //-------------------------------------------------------------------------------
88 void OutputBind::decode() const
89 throw(RuntimeException) {
90   OutputBind* _this = const_cast <OutputBind*>(this);
91   char* str;
92   Data& data = _this->getData();
93   data.setNull(a_nullIndicator < 0);
94
95   switch(data.getType()) {
96   case Data::Type::String:
97     str = (char*) data.getBuffer();
98
99     if(data.isNull() == true)
100       *str = 0;
101     else
102       dbms::String::strip(str);
103
104     break;
105   case Data::Type::Integer:
106     throw RuntimeException("anna::dbms::oracle::OutputBind::decode not implemented for Data::Type::Integer", ANNA_FILE_LOCATION);
107     break;
108   case Data::Type::Float:
109     decodeFloat(data);
110     break;
111   case Data::Type::ShortBlock:
112     decodeShortBlock(data);
113     break;
114   case Data::Type::LongBlock:
115     decodeLongBlock(data);
116     break;
117   case Data::Type::Date:
118   case Data::Type::TimeStamp:
119
120     try {
121       decodeDate(data);
122     } catch(DatabaseException& edb) {
123       throw RuntimeException(edb);
124     }
125
126     break;
127   }
128 }
129
130 void OutputBind::decodeFloat(dbms::Data& data) const
131 throw(RuntimeException) {
132   dbms::Float& _float = static_cast <dbms::Float&>(data);
133
134   if(data.isNull() == true) {
135     _float = 0.0;
136     return;
137   }
138
139   char* _data = (char*) a_ofb->getData();
140   const char decimalPoint = oracle::Database::getDecimalPoint();
141
142   if(decimalPoint != 0) {
143     char* point = anna_strchr(_data, decimalPoint);
144
145     if(point != NULL)
146       *point = '.';
147   }
148
149   sscanf(_data, _float.getFormat(), (float*) _float.getBuffer());
150 }
151
152 void OutputBind::decodeShortBlock(dbms::Data& data) const
153 throw(RuntimeException) {
154   const anna::DataBlock& constdbms(static_cast <dbms::ShortBlock&>(data).getValue());
155   anna::DataBlock& dataBlock(const_cast <anna::DataBlock&>(constdbms));
156
157   if(data.isNull() == true) {
158     dataBlock.clear();
159     return;
160   }
161
162   const char* src = a_ofb->getData();
163
164   char* dest = (char*) dataBlock.getData();
165
166   unsigned char hex;
167
168   int j = 0;
169
170   for(int i = 1; i < a_length; i += 2, j ++) {
171     hex = asByte(src [i - 1]) << 4;
172     hex |= asByte(src [i]);
173     dest [j] = hex;
174   }
175
176   dataBlock.clear();
177   dataBlock.allocate(j);     // (1)
178 }
179
180 //--------------------------------------------------------------------------------------------
181 // (1) Offset = 1 => primer caracter.
182 //--------------------------------------------------------------------------------------------
183 void OutputBind::decodeLongBlock(dbms::Data& data) const
184 throw(RuntimeException) {
185   const anna::DataBlock& constdbms(static_cast <dbms::LongBlock&>(data).getValue());
186   anna::DataBlock& dataBlock(const_cast <anna::DataBlock&>(constdbms));
187   dataBlock.clear();
188
189   if(data.isNull() == true)
190     return;
191
192   ub1* buffer;
193   ub4 maxLength;
194   ub4 length;
195   sword ret;
196   bool stop = false;
197   ub4 offset = 1;                                       // (1)
198
199   try {
200     buffer = (ub1*) a_ofb->getData();
201     maxLength = a_ofb->getSize();
202     length = 0;
203
204     do {
205       ret = OCILobRead(a_blob.context, a_blob.error, a_blob.handle, &length, offset, buffer, maxLength, 0, 0, 0, SQLCS_IMPLICIT);
206
207       switch(ret) {
208       case OCI_SUCCESS:
209         dataBlock += anna::DataBlock((const char*) buffer, length, false);
210         stop = true;
211         break;
212       case OCI_NEED_DATA:
213         dataBlock += anna::DataBlock((const char*) buffer, length, false);
214         offset += length;
215         break;
216       default:
217         throw dbms::DatabaseException(oracle::ResultCode(ret, a_blob.error),  ANNA_FILE_LOCATION);
218       }
219     } while(stop == false);
220   } catch(dbms::DatabaseException& edbms) {
221     throw RuntimeException(edbms);
222   }
223 }
224
225 void OutputBind::do_write(const dbms::LongBlock& data) const
226 throw(RuntimeException, dbms::DatabaseException) {
227   const anna::DataBlock& dataBlock = data.getValue();
228
229   if(a_blob.handle == NULL) {
230     string msg("anna::dbms::oracle::OutputBind::do_write | ");
231     msg += data.asString();
232     msg += " | BLOB must be loaded before modification";
233     throw RuntimeException(msg, ANNA_FILE_LOCATION);
234   }
235
236   ub1* buffer = (ub1*) dataBlock.getData();
237   ub4 length = dataBlock.getSize();
238   anna_dbms_oracle_check(
239     OCILobWrite(a_blob.context, a_blob.error, a_blob.handle, &length, (ub4) 1, buffer, length, OCI_ONE_PIECE, 0, 0, 0, SQLCS_IMPLICIT),
240     a_blob.error
241   );
242 }
243
244 void OutputBind::decodeDate(dbms::Data& data) const
245 throw(RuntimeException, dbms::DatabaseException) {
246   if(data.isNull() == true)
247     return;
248
249   Date& date = static_cast <Date&>(data);
250   sb2 year;
251   ub1 month, day;
252   anna_dbms_oracle_check(
253     OCIDateTimeGetDate(a_datetime.env, a_datetime.error, a_datetime.handle, &year, &month, &day),
254     a_datetime.error
255   );
256   date.setYear(year);
257   date.setMonth(month);
258   date.setDay(day);
259   ub1 hour, min, sec;
260   ub4 fsec;
261   sword status = OCIDateTimeGetTime(a_datetime.env, a_datetime.error, a_datetime.handle, &hour, &min, &sec, &fsec);
262
263   if(status == OCI_SUCCESS) {
264     date.setHour(hour);
265     date.setMinute(min);
266     date.setSecond(sec);
267
268     if(data.getType() == Data::Type::TimeStamp)
269       static_cast <dbms::TimeStamp&>(data).setFractionalSecond(fsec / 1000);
270   } else {
271     date.setHour(0);
272     date.setMinute(0);
273     date.setSecond(0);
274
275     if(data.getType() == Data::Type::TimeStamp)
276       static_cast <dbms::TimeStamp&>(data).setFractionalSecond(0);
277   }
278 }