Updated license
[anna.git] / source / dbms.mysql / OutputBind.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
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 Google Inc. 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 <mysql/mysql.h>
38
39 #include <anna/config/defines.hpp>
40 #include <anna/core/tracing/Logger.hpp>
41 #include <anna/core/DataBlock.hpp>
42
43 #include <anna/dbms/Float.hpp>
44 #include <anna/dbms/ShortBlock.hpp>
45 #include <anna/dbms/LongBlock.hpp>
46 #include <anna/dbms/String.hpp>
47 #include <anna/dbms/Date.hpp>
48 #include <anna/dbms/TimeStamp.hpp>
49
50 #include <anna/dbms.mysql/mysql.hpp>
51
52 using namespace anna;
53 using namespace std;
54
55 OutputBind::OutputBind(const char* name, dbms::Data& data) :
56   dbms::OutputBind(name, data),
57   BaseBind(data) {
58   a_blob = (data.getType() == Data::Type::LongBlock) ? new Blob : NULL;
59 }
60
61 OutputBind::~OutputBind() {
62   delete a_blob;
63 }
64
65 /*
66  * El dato de LONG BLOB se recoge según cuenta:
67  * http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html.
68  */
69 void OutputBind::prepare(anna::dbms::Statement* dbmsStmt, anna::dbms::Connection*, const int pos)
70 throw(RuntimeException) {
71   st_mysql_bind* bind = static_cast <dbms::mysql::Statement*>(dbmsStmt)->getBindResults() + pos;
72   Data& data = anna::dbms::Bind::getData();
73   BaseBind::setupBind(*bind, data);
74
75   if(data.getType() == Data::Type::LongBlock) {
76     dbms::mysql::Statement* myStmt = static_cast <dbms::mysql::Statement*>(dbmsStmt);
77     a_blob->stmt = *myStmt;
78     a_blob->binds = myStmt->getBindResults();
79     a_blob->pos = pos;
80     bind->buffer_type = MYSQL_TYPE_BLOB;
81     bind->buffer = NULL;
82     bind->buffer_length = 0;
83     bind->length = &a_length;
84   }
85 }
86
87 /*
88  * Transfiere la información del los MYSQL_BIND del API C de MySQL a las
89  * estructuras dbms::Data de nuestro programa C++.
90  */
91 void OutputBind::decode() const
92 throw(RuntimeException) {
93   OutputBind* _this = const_cast <OutputBind*>(this);
94   char* str;
95   Data& data = _this->getData();
96   data.setNull(a_nullIndicator == true);
97
98   switch(data.getType()) {
99   case Data::Type::String:
100     str = (char*) data.getBuffer();
101
102     if(data.isNull() == true)
103       *str = 0;
104     else
105       dbms::String::strip(str);
106
107     break;
108   case Data::Type::Float:
109
110     if(data.isNull() == true)
111       static_cast <dbms::Float&>(data) = 0.0;
112
113     break;
114   case Data::Type::Date:
115   case Data::Type::TimeStamp:
116     _this->decodeDate(data);
117     break;
118   case Data::Type::LongBlock:
119
120     try {
121       _this->decodeLongBlob(data);
122     } catch(dbms::DatabaseException& edb) {
123       throw RuntimeException(edb);
124     }
125
126     break;
127   }
128 }
129
130 void OutputBind::do_write(const dbms::LongBlock&) const
131 throw(RuntimeException, dbms::DatabaseException) {
132 }
133
134 /*
135  * El m�todo BaseBind::setupBind asocia� el contenido de la variable
136  * a_time al buffer de salida de la sentencia SQL, as� que el contenido
137  * de la columna est� contenido ah�. S�lo tendremos que copiar dichos
138  * contenidos en la variable C++ de nuestro entorno.
139  */
140 void OutputBind::decodeDate(dbms::Data& data)
141 throw() {
142   if(data.isNull() == true)
143     return;
144
145   Date& date = static_cast <Date&>(data);
146   date.setYear(a_time->year);
147   date.setMonth(a_time->month);
148   date.setDay(a_time->day);
149   date.setHour(a_time->hour);
150   date.setMinute(a_time->minute);
151   date.setSecond(a_time->second);
152
153   if(data.getType() == Data::Type::TimeStamp)
154     static_cast <dbms::TimeStamp&>(data).setFractionalSecond(a_time->second_part);
155 }
156
157 /*
158  * Según http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html
159  *
160  * Recoge el contenido total de BLOB en partes que va componiendo sobre el
161  * DataBlock final asociado al anna::dbms::LongBlock sobre el que trabaja el
162  * programador final.
163  */
164 void OutputBind::decodeLongBlob(dbms::Data& data) const
165 throw(RuntimeException, dbms::DatabaseException) {
166   const int bufferSize = a_blob->buffer.getMaxSize();
167   const int pos = a_blob->pos;
168   st_mysql_bind& bind = a_blob->binds [a_blob->pos];
169   DataBlock& target = static_cast <dbms::LongBlock&>(data).getValue();
170   const int maxloop = *bind.length / bufferSize;
171   const int remainder = *bind.length % bufferSize;
172   int offset = 0;
173   target.clear();
174   bind.buffer = (void*) a_blob->buffer.getData();
175
176   for(int iloop = 0; iloop < maxloop; iloop ++) {
177     bind.buffer_length = bufferSize;
178     anna_dbms_mysql_check(
179       mysql_stmt_fetch_column(a_blob->stmt, a_blob->binds, a_blob->pos, offset), a_blob->stmt
180     );
181     target += DataBlock((const char*) bind.buffer, bind.buffer_length, false);
182     offset += bufferSize;
183   }
184
185   if(remainder) {
186     bind.buffer_length = remainder;
187     anna_dbms_mysql_check(
188       mysql_stmt_fetch_column(a_blob->stmt, a_blob->binds, a_blob->pos, offset), a_blob->stmt
189     );
190     target += DataBlock((const char*) bind.buffer, remainder, false);
191   }
192 }
193
194 OutputBind::Blob::Blob() :
195   buffer(true) {
196   buffer.allocate(64 * 1024);
197 }