MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SqlClient.cpp
1 /*
2  Copyright (C) 2008 MySQL AB
3  All rights reserved. Use is subject to license terms.
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; version 2 of the License.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 #include <SqlClient.hpp>
20 #include <NDBT_Output.hpp>
21 #include <NdbSleep.h>
22 
23 SqlClient::SqlClient(const char* _user,
24  const char* _password,
25  const char* _group_suffix):
26  connected(false),
27  mysql(NULL),
28  free_mysql(false)
29 {
30 
31  const char* env= getenv("MYSQL_HOME");
32  if (env && strlen(env))
33  {
34  default_file.assfmt("%s/my.cnf", env);
35  }
36 
37  if (_group_suffix != NULL){
38  default_group.assfmt("client%s", _group_suffix);
39  }
40  else {
41  default_group.assign("client.1.atrt");
42  }
43 
44  g_info << "default_file: " << default_file.c_str() << endl;
45  g_info << "default_group: " << default_group.c_str() << endl;
46 
47  user.assign(_user);
48  password.assign(_password);
49 }
50 
51 
52 SqlClient::SqlClient(MYSQL* mysql):
53  connected(true),
54  mysql(mysql),
55  free_mysql(false)
56 {
57 }
58 
59 
60 SqlClient::~SqlClient(){
61  disconnect();
62 }
63 
64 
65 bool
66 SqlClient::isConnected(){
67  if (connected == true)
68  {
69  assert(mysql);
70  return true;
71  }
72  return connect() == 0;
73 }
74 
75 
76 int
77 SqlClient::connect(){
78  disconnect();
79 
80 // mysql_debug("d:t:O,/tmp/client.trace");
81 
82  if ((mysql= mysql_init(NULL)) == NULL){
83  g_err << "mysql_init failed" << endl;
84  return -1;
85  }
86 
87  /* Load connection parameters file and group */
88  if (mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, default_file.c_str()) ||
89  mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, default_group.c_str()))
90  {
91  g_err << "mysql_options failed" << endl;
92  disconnect();
93  return 1;
94  }
95 
96  /*
97  Connect, read settings from my.cnf
98  NOTE! user and password can be stored there as well
99  */
100  if (mysql_real_connect(mysql, NULL, user.c_str(),
101  password.c_str(), "atrt", 0, NULL, 0) == NULL)
102  {
103  g_err << "Connection to atrt server failed: "<< mysql_error(mysql) << endl;
104  disconnect();
105  return -1;
106  }
107 
108  g_err << "Connected to MySQL " << mysql_get_server_info(mysql)<< endl;
109 
110  connected = true;
111  return 0;
112 }
113 
114 
115 bool
116 SqlClient::waitConnected(int timeout) {
117  timeout*= 10;
118  while(!isConnected()){
119  if (timeout-- == 0)
120  return false;
121  NdbSleep_MilliSleep(100);
122  }
123  return true;
124 }
125 
126 
127 void
128 SqlClient::disconnect(){
129  if (mysql != NULL){
130  if (free_mysql)
131  mysql_close(mysql);
132  mysql= NULL;
133  }
134  connected = false;
135 }
136 
137 
138 static bool is_int_type(enum_field_types type){
139  switch(type){
140  case MYSQL_TYPE_TINY:
141  case MYSQL_TYPE_SHORT:
142  case MYSQL_TYPE_LONGLONG:
143  case MYSQL_TYPE_INT24:
144  case MYSQL_TYPE_LONG:
145  case MYSQL_TYPE_ENUM:
146  return true;
147  default:
148  return false;
149  }
150  return false;
151 }
152 
153 
154 bool
155 SqlClient::runQuery(const char* sql,
156  const Properties& args,
157  SqlResultSet& rows){
158 
159  rows.clear();
160  if (!isConnected())
161  return false;
162 
163  g_debug << "runQuery: " << endl
164  << " sql: '" << sql << "'" << endl;
165 
166 
167  MYSQL_STMT *stmt= mysql_stmt_init(mysql);
168  if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
169  {
170  g_err << "Failed to prepare: " << mysql_error(mysql) << endl;
171  return false;
172  }
173 
174  uint params= mysql_stmt_param_count(stmt);
175  MYSQL_BIND bind_param[params];
176  bzero(bind_param, sizeof(bind_param));
177 
178  for(uint i= 0; i < mysql_stmt_param_count(stmt); i++)
179  {
181  name.assfmt("%d", i);
182  // Parameters are named 0, 1, 2...
183  if (!args.contains(name.c_str()))
184  {
185  g_err << "param " << i << " missing" << endl;
186  assert(false);
187  }
188  PropertiesType t;
189  Uint32 val_i;
190  const char* val_s;
191  args.getTypeOf(name.c_str(), &t);
192  switch(t) {
193  case PropertiesType_Uint32:
194  args.get(name.c_str(), &val_i);
195  bind_param[i].buffer_type= MYSQL_TYPE_LONG;
196  bind_param[i].buffer= (char*)&val_i;
197  g_debug << " param" << name.c_str() << ": " << val_i << endl;
198  break;
199  case PropertiesType_char:
200  args.get(name.c_str(), &val_s);
201  bind_param[i].buffer_type= MYSQL_TYPE_STRING;
202  bind_param[i].buffer= (char*)val_s;
203  bind_param[i].buffer_length= strlen(val_s);
204  g_debug << " param" << name.c_str() << ": " << val_s << endl;
205  break;
206  default:
207  assert(false);
208  break;
209  }
210  }
211  if (mysql_stmt_bind_param(stmt, bind_param))
212  {
213  g_err << "Failed to bind param: " << mysql_error(mysql) << endl;
214  mysql_stmt_close(stmt);
215  return false;
216  }
217 
218  if (mysql_stmt_execute(stmt))
219  {
220  g_err << "Failed to execute: " << mysql_error(mysql) << endl;
221  mysql_stmt_close(stmt);
222  return false;
223  }
224 
225  /*
226  Update max_length, making it possible to know how big
227  buffers to allocate
228  */
229  my_bool one= 1;
230  mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one);
231 
232  if (mysql_stmt_store_result(stmt))
233  {
234  g_err << "Failed to store result: " << mysql_error(mysql) << endl;
235  mysql_stmt_close(stmt);
236  return false;
237  }
238 
239  uint row= 0;
240  MYSQL_RES* res= mysql_stmt_result_metadata(stmt);
241  if (res != NULL)
242  {
243  MYSQL_FIELD *fields= mysql_fetch_fields(res);
244  uint num_fields= mysql_num_fields(res);
245  MYSQL_BIND bind_result[num_fields];
246  bzero(bind_result, sizeof(bind_result));
247 
248  for (uint i= 0; i < num_fields; i++)
249  {
250  if (is_int_type(fields[i].type)){
251  bind_result[i].buffer_type= MYSQL_TYPE_LONG;
252  bind_result[i].buffer= malloc(sizeof(int));
253  }
254  else
255  {
256  uint max_length= fields[i].max_length + 1;
257  bind_result[i].buffer_type= MYSQL_TYPE_STRING;
258  bind_result[i].buffer= malloc(max_length);
259  bind_result[i].buffer_length= max_length;
260  }
261  }
262 
263  if (mysql_stmt_bind_result(stmt, bind_result)){
264  g_err << "Failed to bind result: " << mysql_error(mysql) << endl;
265  mysql_stmt_close(stmt);
266  return false;
267  }
268 
269  while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
270  {
271  Properties curr(true);
272  for (uint i= 0; i < num_fields; i++){
273  if (is_int_type(fields[i].type))
274  curr.put(fields[i].name, *(int*)bind_result[i].buffer);
275  else
276  curr.put(fields[i].name, (char*)bind_result[i].buffer);
277  }
278  rows.put("row", row++, &curr);
279  }
280 
281  mysql_free_result(res);
282 
283  for (uint i= 0; i < num_fields; i++)
284  free(bind_result[i].buffer);
285 
286  }
287 
288  // Save stats in result set
289  rows.put("rows", row);
290  rows.put("affected_rows", mysql_affected_rows(mysql));
291  rows.put("mysql_errno", mysql_errno(mysql));
292  rows.put("mysql_error", mysql_error(mysql));
293  rows.put("mysql_sqlstate", mysql_sqlstate(mysql));
294  rows.put("insert_id", mysql_insert_id(mysql));
295 
296  mysql_stmt_close(stmt);
297  return true;
298 }
299 
300 
301 bool
302 SqlClient::doQuery(const char* query){
303  const Properties args;
304  SqlResultSet result;
305  return doQuery(query, args, result);
306 }
307 
308 
309 bool
310 SqlClient::doQuery(const char* query, SqlResultSet& result){
311  Properties args;
312  return doQuery(query, args, result);
313 }
314 
315 
316 bool
317 SqlClient::doQuery(const char* query, const Properties& args,
318  SqlResultSet& result){
319  if (!runQuery(query, args, result))
320  return false;
321  result.get_row(0); // Load first row
322  return true;
323 }
324 
325 
326 bool
327 SqlClient::doQuery(BaseString& str){
328  return doQuery(str.c_str());
329 }
330 
331 
332 bool
333 SqlClient::doQuery(BaseString& str, SqlResultSet& result){
334  return doQuery(str.c_str(), result);
335 }
336 
337 
338 bool
339 SqlClient::doQuery(BaseString& str, const Properties& args,
340  SqlResultSet& result){
341  return doQuery(str.c_str(), args, result);
342 }
343 
344 
345 
346 
347 bool
348 SqlResultSet::get_row(int row_num){
349  if(!get("row", row_num, &m_curr_row)){
350  return false;
351  }
352  return true;
353 }
354 
355 bool
356 SqlResultSet::next(void){
357  return get_row(++m_curr_row_num);
358 }
359 
360 // Reset iterator
361 void SqlResultSet::reset(void){
362  m_curr_row_num= -1;
363  m_curr_row= 0;
364 }
365 
366 // Remove row from resultset
367 void SqlResultSet::remove(){
368  BaseString row_name;
369  row_name.assfmt("row_%d", m_curr_row_num);
370  Properties::remove(row_name.c_str());
371 }
372 
373 
374 SqlResultSet::SqlResultSet(): m_curr_row(0), m_curr_row_num(-1){
375 }
376 
377 SqlResultSet::~SqlResultSet(){
378 }
379 
380 const char* SqlResultSet::column(const char* col_name){
381  const char* value;
382  if (!m_curr_row){
383  g_err << "ERROR: SqlResultSet::column("<< col_name << ")" << endl
384  << "There is no row loaded, call next() before "
385  << "acessing the column values" << endl;
386  assert(m_curr_row);
387  }
388  if (!m_curr_row->get(col_name, &value))
389  return NULL;
390  return value;
391 }
392 
393 uint SqlResultSet::columnAsInt(const char* col_name){
394  uint value;
395  if (!m_curr_row){
396  g_err << "ERROR: SqlResultSet::columnAsInt("<< col_name << ")" << endl
397  << "There is no row loaded, call next() before "
398  << "acessing the column values" << endl;
399  assert(m_curr_row);
400  }
401  if (!m_curr_row->get(col_name, &value))
402  return (uint)-1;
403  return value;
404 }
405 
406 uint SqlResultSet::insertId(){
407  return get_int("insert_id");
408 }
409 
410 uint SqlResultSet::affectedRows(){
411  return get_int("affected_rows");
412 }
413 
414 uint SqlResultSet::numRows(void){
415  return get_int("rows");
416 }
417 
418 uint SqlResultSet::mysqlErrno(void){
419  return get_int("mysql_errno");
420 }
421 
422 
423 const char* SqlResultSet::mysqlError(void){
424  return get_string("mysql_error");
425 }
426 
427 const char* SqlResultSet::mysqlSqlstate(void){
428  return get_string("mysql_sqlstate");
429 }
430 
431 uint SqlResultSet::get_int(const char* name){
432  uint value;
433  assert(get(name, &value));
434  return value;
435 }
436 
437 const char* SqlResultSet::get_string(const char* name){
438  const char* value;
439  assert(get(name, &value));
440  return value;
441 }