Suuports clang compiler
[anna.git] / source / dbms.oracle / InputBind.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // http://redmine.teslayout.com/projects/anna-suite
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     *  Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <oci.h>
38
39 #include <time.h>
40
41 #include <anna/config/defines.hpp>
42 #include <anna/core/functions.hpp>
43 #include <anna/core/DataBlock.hpp>
44 #include <anna/core/tracing/Logger.hpp>
45
46 #include <anna/dbms/Float.hpp>
47 #include <anna/dbms/ShortBlock.hpp>
48 #include <anna/dbms/Date.hpp>
49 #include <anna/dbms/TimeStamp.hpp>
50 #include <anna/dbms/Database.hpp>
51
52 #include <anna/dbms.oracle/oracle.hpp>
53
54 using namespace std;
55 using namespace anna;
56
57 InputBind::InputBind(const char* name, dbms::Data&  data) :
58   dbms::InputBind(name, data),
59   BaseBind(data),
60   a_ociBind(NULL) {
61 }
62
63 InputBind::~InputBind() {
64 }
65
66 // Slo se invoca una vez. Establece las variables atrav� de las que nos vamos a poder
67 // comunicar con Oracle, para indicar la longitud de una variable, o su estado de nulo o
68 // no nulo.
69 void InputBind::prepare(dbms::Statement* dbmsStatement, dbms::Connection* connection, const int pos)
70 throw(RuntimeException, dbms::DatabaseException) {
71   if(a_ociBind != NULL)
72     return;
73
74   Data& data = anna::dbms::Bind::getData();
75
76   if(data.getType() == Data::Type::LongBlock) {
77     string msg("anna::dbms::oracle::InputBind::prepare | ");
78     msg += data.asString();
79     msg += " | This RDBMS doesn't support BLOB type as BindInput (see anna::dbms::OutputBind::write)";
80     throw RuntimeException(msg, ANNA_FILE_LOCATION);
81   }
82
83   Database& database = static_cast <Database&>(dbmsStatement->getDatabase());
84   OCIError* error = database.getErrorHandler();
85   Statement* statement(static_cast <Statement*>(dbmsStatement));
86   oci_param ociparam = getOCIParam(database, static_cast <oracle::Connection*>(connection), data);
87
88   if(data.isNulleable() == false) {
89     anna_dbms_oracle_check(
90       OCIBindByPos(
91         *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
92         0, ociparam.length, 0, 0, 0, OCI_DEFAULT
93       ),
94       error
95     );
96   } else {
97     anna_dbms_oracle_check(
98       OCIBindByPos(
99         *statement, &a_ociBind, error, pos, ociparam.buffer, ociparam.maxLength, ociparam.type,
100         &a_nullIndicator, ociparam.length, 0, 0, 0, OCI_DEFAULT
101       ),
102       error
103     );
104   }
105
106   LOGDEBUG(
107     std::string msg("anna::dbms::oracle::InputBind::prepare | ");
108     msg += asString();
109     msg += " | Sentence: ";
110     msg += statement->getName();
111     msg += " | Position: ";
112     msg += functions::asString(pos);
113     Logger::debug(msg, ANNA_FILE_LOCATION)
114   );
115 }
116
117 //-------------------------------------------------------------------------------
118 // Establece la informacin mediante la que conectamos con Oracle. Todos los
119 // par�etros que modificamos en �te m�odo tienen efecto en la llamada a Oracle
120 // debido cmo hemos invocamo al m�odo OCIBindByPos.
121 //
122 // Todo esto se podr� haber hecho en la anna::dbms::DataBlock pero exigir�
123 // definir una clase distinta para cada RDBMS. Creo que los Binds particulares de
124 // cada base de datos se ocupen toda la complejidad de convertir los datos.
125 //-------------------------------------------------------------------------------
126 void InputBind::code() const
127 throw(RuntimeException) {
128   InputBind* _this = const_cast <InputBind*>(this);
129   Data& data = _this->getData();
130
131   if((_this->a_nullIndicator = data.isNull() ? -1 : 0) == -1)
132     return;
133
134   switch(data.getType())  {
135   case Data::Type::Float:
136     codeFloat(data);
137     break;
138   case Data::Type::ShortBlock:
139     codeShortBlock(data);
140     break;
141   case Data::Type::Date:
142   case Data::Type::TimeStamp:
143
144     try {
145       codeDate(data);
146     } catch(DatabaseException& edb) {
147       throw RuntimeException(edb);
148     }
149
150     break;
151   }
152 }
153
154 /**
155  * Transfiere el valor numerico del float, al buffer reservado para
156  * ubiucarlo como una cadena. Ã\89ste buffer es el que está "conectado" con
157  * Oracle (tm).
158  */
159 void InputBind::codeFloat(dbms::Data& data) const
160 throw() {
161   dbms::Float& _float = static_cast <dbms::Float&>(data);
162   InputBind* _this = const_cast <InputBind*>(this);
163   char* buffer = (char*) _this->a_ofb->getData();
164   snprintf(buffer, FloatSize, _float.getFormat(), _float.getValue());
165   const char decimalPoint = oracle::Database::getDecimalPoint();
166
167   if(decimalPoint != 0) {
168     char* point = anna_strchr(buffer, '.');
169
170     if(point != NULL)
171       *point = decimalPoint;
172   }
173 }
174
175 void InputBind::codeShortBlock(dbms::Data& data) const
176 throw() {
177   const int length = static_cast <dbms::ShortBlock&>(data).getSize();
178   InputBind* _this = const_cast <InputBind*>(this);
179
180   if(length == 0) {
181     _this->a_ofb->clear();
182     _this->a_length = 0;
183     return;
184   }
185
186   const char* src = (const char*) data.getBuffer();
187
188   char* dest = const_cast <char*>(a_ofb->getData());
189
190   int j = 0;
191
192   for(int i = 0; i < length; i ++) {
193     dest [j ++] = asCharacter((src [i] & 0xf0) >> 4);
194     dest [j ++] = asCharacter(src [i] & 0x0f);
195   }
196
197   dest [j ++] = 0;
198   _this->a_length = j;
199 }
200
201 void InputBind::codeDate(dbms::Data& data) const
202 throw(RuntimeException, dbms::DatabaseException) {
203   dbms::Date& date = static_cast <dbms::Date&>(data);
204   ub4 fsec(0);
205
206   if(data.getType() == Data::Type::TimeStamp)
207     fsec = static_cast <dbms::TimeStamp&>(data).getFractionalSecond() * 1000;
208
209   anna_dbms_oracle_check(
210     OCIDateTimeConstruct(
211       a_datetime.env, a_datetime.error, a_datetime.handle,
212       date.getYear(), date.getMonth(), date.getDay(), date.getHour(), date.getMinute(), date.getSecond(), fsec, NULL, 0
213     ),
214     a_datetime.error
215   );
216   ub4 errorMask(0);
217   anna_dbms_oracle_check(
218     OCIDateTimeCheck(a_datetime.env, a_datetime.error, a_datetime.handle, &errorMask),
219     a_datetime.error
220   );
221
222   if(errorMask != 0) {
223     string msg(data.asString());
224     msg += anna::functions::asHexText(" | Invalid date | ErrorCode: ", (int) errorMask);
225     throw RuntimeException(msg, ANNA_FILE_LOCATION);
226   }
227 }