First commit
[anna.git] / source / dbms.mysql / Statement.cpp
1 // ANNA - Anna is Not 'N' 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/TraceMethod.hpp>
41
42 #include <anna/dbms.mysql/mysql.hpp>
43
44 using namespace std;
45 using namespace anna;
46
47 dbms::mysql::Statement::~Statement() {
48   if(a_mysqlStmt) {
49     try {
50       anna_dbms_mysql_check(mysql_stmt_close(a_mysqlStmt), a_mysqlStmt);
51     } catch(DatabaseException& edb) {
52       edb.trace();
53     }
54   }
55
56   delete [] a_params;
57   delete [] a_results;
58 }
59
60 /**
61  * Según el ejemplo de: http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html
62  *
63  */
64 void dbms::mysql::Statement::prepare(dbms::Connection* dbmsConnection)
65 throw(RuntimeException, dbms::DatabaseException) {
66   LOGMETHOD(TraceMethod tm("anna::dbms::mysql::Statement", "prepare", ANNA_FILE_LOCATION));
67   Connection* connection(static_cast <Connection*>(dbmsConnection));
68   Database& dbms(static_cast <Database&>(connection->getDatabase()));
69   LOGDEBUG(
70     string msg("anna::dbms::mysql::Statement::prepare | ");
71     msg += asString();
72     Logger::debug(msg, ANNA_FILE_LOCATION);
73   );
74
75   /*
76    * Libera la información establecida anteriormente. Las sentencias se pueden reusar.
77    */
78   if(a_mysqlStmt != NULL) {
79     anna_dbms_mysql_check(mysql_stmt_reset(a_mysqlStmt), a_mysqlStmt);
80     delete [] a_params;
81     delete [] a_results;
82     a_params = a_results = NULL;
83   } else if((a_mysqlStmt = mysql_stmt_init(*connection)) == NULL) {
84     string msg(asString());
85     msg += " | Insufficient memory";
86     throw RuntimeException(msg, ANNA_FILE_LOCATION);
87   }
88
89   const char* expression = dbms::Statement::getExpression().c_str();
90
91   anna_dbms_mysql_check(mysql_stmt_prepare(a_mysqlStmt, expression, anna_strlen(expression)), a_mysqlStmt);
92
93   const int paramCount = mysql_stmt_param_count(a_mysqlStmt);
94
95   const int inputSize = dbms::Statement::input_size();
96
97   const int outputSize = dbms::Statement::output_size();
98
99   /*
100    * Verifica que el número de parámetros de entrada de la sentencia coincida con el número de parámetros
101    * indicados por el programador.
102    */
103   if(paramCount != inputSize) {
104     string msg(asString());
105     msg += " | Wrong input parameters";
106     throw RuntimeException(msg, ANNA_FILE_LOCATION);
107   }
108
109   /*
110    * Verifica que el número de parámetros de salida de la sentencia coincida con el número de parámetros
111    * indicados por el programador. Si no tiene parámetros de salida (INSERT) => no debe tener parámetros
112    * indicados por el programador.
113    */
114   MYSQL_RES* metaResult = mysql_stmt_result_metadata(a_mysqlStmt);
115
116   try {
117     if(metaResult == NULL) {
118       if(outputSize != 0) {
119         string msg(asString());
120         msg += " | Wrong output parameters";
121         throw RuntimeException(msg, ANNA_FILE_LOCATION);
122       }
123     } else if(mysql_num_fields(metaResult) != outputSize) {
124       string msg(asString());
125       msg += " | Wrong output parameters";
126       throw RuntimeException(msg, ANNA_FILE_LOCATION);
127     }
128   } catch(RuntimeException&) {
129     mysql_free_result(metaResult);
130     throw;
131   }
132
133   /*
134    * Define las estructuras requeridas para asociar las columasn MySQL con las áreas de memoria C++
135    */
136   int pos;
137
138   if((a_params = create(inputSize, "input")) != NULL) {
139     pos = 0;
140
141     for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++)
142       inputBind(ii)->prepare(this, dbmsConnection, pos ++);
143
144     anna_dbms_mysql_check(mysql_stmt_bind_param(a_mysqlStmt, a_params), a_mysqlStmt);
145   }
146
147   if((a_results = create(outputSize, "output")) != NULL) {
148     pos = 0;
149
150     for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++)
151       outputBind(oo)->prepare(this, dbmsConnection, pos ++);
152
153     anna_dbms_mysql_check(mysql_stmt_bind_result(a_mysqlStmt, a_results), a_mysqlStmt);
154   }
155 }
156
157 dbms::ResultCode dbms::mysql::Statement::execute(dbms::Connection* dbmsConnection)
158 throw(RuntimeException, dbms::DatabaseException) {
159   Connection* connection(static_cast <Connection*>(dbmsConnection));
160
161   for(input_iterator ii = input_begin(), maxii = input_end(); ii != maxii; ii ++) {
162     inputBind(ii)->code();
163     LOGDEBUG(
164       string msg("anna::dbms::mysql::Statement::InputBind: ");
165       msg += inputBind(ii)->asString();
166       Logger::debug(msg, ANNA_FILE_LOCATION);
167     );
168   }
169
170   anna_dbms_mysql_check(mysql_stmt_execute(a_mysqlStmt), a_mysqlStmt)
171   ResultCode result(a_mysqlStmt);
172   return result;
173 }
174
175 /*
176  * Según la información de http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-fetch.html
177  */
178 bool dbms::mysql::Statement::fetch()
179 throw(RuntimeException, dbms::DatabaseException) {
180   bool result = false;
181
182   switch(mysql_stmt_fetch(a_mysqlStmt)) {
183   case 0:
184     result = true;
185
186     for(output_iterator oo = output_begin(), maxoo = output_end(); oo != maxoo; oo ++) {
187       outputBind(oo)->decode();
188       LOGDEBUG(Logger::write(Logger::Debug, outputBind(oo)->asString(), ANNA_FILE_LOCATION));
189     }
190
191     break;
192   case 1:
193     throw DatabaseException(ResultCode(a_mysqlStmt), ANNA_FILE_LOCATION);
194   default:
195     result = false;
196     break;
197   }
198
199   LOGDEBUG(
200     string msg("anna::dbms::mysql::Statement::fetch | Result: ");
201     msg += functions::asString(result);
202     Logger::debug(msg, ANNA_FILE_LOCATION);
203   );
204   return result;
205 }
206
207 st_mysql_bind* dbms::mysql::Statement::create(const int size, const char* whatis)
208 throw(RuntimeException) {
209   st_mysql_bind* result = NULL;
210
211   if(size > 0) {
212     if((result = new st_mysql_bind [size]) == NULL) {
213       string msg(asString());
214       msg += anna::functions::asString("Insufficient memory to create %d parameters of %s", size, whatis);
215       throw RuntimeException(msg, ANNA_FILE_LOCATION);
216     }
217   }
218
219   return result;
220 }