MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DbUtil.cpp
1 /*
2  Copyright (C) 2007, 2008 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
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 /* DbUtil.cpp: implementation of the database utilities class.*/
20 
21 #include "DbUtil.hpp"
22 #include <NdbSleep.h>
23 #include <NdbAutoPtr.hpp>
24 
25 /* Constructors */
26 
27 DbUtil::DbUtil(const char* _dbname,
28  const char* _suffix):
29  m_mysql(NULL),
30  m_free_mysql(true),
31  m_connected(false),
32  m_user("root"),
33  m_pass(""),
34  m_dbname(_dbname),
35  m_silent(false)
36 {
37  const char* env= getenv("MYSQL_HOME");
38  if (env && strlen(env))
39  {
40  m_default_file.assfmt("%s/my.cnf", env);
41  }
42 
43  if (_suffix != NULL){
44  m_default_group.assfmt("client%s", _suffix);
45  }
46  else {
47  m_default_group.assign("client.1.master");
48  }
49 
50  ndbout << "default_file: " << m_default_file.c_str() << endl;
51  ndbout << "default_group: " << m_default_group.c_str() << endl;
52 }
53 
54 
55 
56 DbUtil::DbUtil(MYSQL* mysql):
57  m_mysql(mysql),
58  m_free_mysql(false),
59  m_connected(true)
60 {
61 }
62 
63 
64 bool
65 DbUtil::isConnected(){
66  if (m_connected == true)
67  {
68  assert(m_mysql);
69  return true;
70  }
71  return connect();
72 }
73 
74 
75 bool
76 DbUtil::waitConnected(int timeout) {
77  timeout*= 10;
78  while(!isConnected()){
79  if (timeout-- == 0)
80  return false;
81  NdbSleep_MilliSleep(100);
82  }
83  return true;
84 }
85 
86 
87 void
88 DbUtil::disconnect(){
89  if (m_mysql != NULL){
90  if (m_free_mysql)
91  mysql_close(m_mysql);
92  m_mysql= NULL;
93  }
94  m_connected = false;
95 }
96 
97 
98 /* Destructor */
99 
100 DbUtil::~DbUtil()
101 {
102  disconnect();
103 }
104 
105 /* Database Login */
106 
107 bool
108 DbUtil::databaseLogin(const char* system, const char* usr,
109  const char* password, unsigned int portIn,
110  const char* sockIn, bool transactional)
111 {
112  if (!(m_mysql = mysql_init(NULL)))
113  {
114  myerror("DB Login-> mysql_init() failed");
115  return false;
116  }
117  setUser(usr);
118  setHost(system);
119  setPassword(password);
120  setPort(portIn);
121  setSocket(sockIn);
122  m_dbname.assign("test");
123 
124  if (!(mysql_real_connect(m_mysql,
125  m_host.c_str(),
126  m_user.c_str(),
127  m_pass.c_str(),
128  m_dbname.c_str(),
129  m_port,
130  m_socket.c_str(), 0)))
131  {
132  myerror("connection failed");
133  disconnect();
134  return false;
135  }
136 
137  m_mysql->reconnect = TRUE;
138 
139  /* set AUTOCOMMIT */
140  if(!transactional)
141  mysql_autocommit(m_mysql, TRUE);
142  else
143  mysql_autocommit(m_mysql, FALSE);
144 
145  #ifdef DEBUG
146  printf("\n\tConnected to MySQL server version: %s (%lu)\n\n",
147  mysql_get_server_info(m_mysql),
148  (unsigned long) mysql_get_server_version(m_mysql));
149  #endif
150  selectDb();
151  m_connected= true;
152  return true;
153 }
154 
155 /* Database Connect */
156 
157 bool
158 DbUtil::connect()
159 {
160  if (!(m_mysql = mysql_init(NULL)))
161  {
162  myerror("DB connect-> mysql_init() failed");
163  return false;
164  }
165 
166  /* Load connection parameters file and group */
167  if (mysql_options(m_mysql, MYSQL_READ_DEFAULT_FILE, m_default_file.c_str()) ||
168  mysql_options(m_mysql, MYSQL_READ_DEFAULT_GROUP, m_default_group.c_str()))
169  {
170  myerror("DB Connect -> mysql_options failed");
171  disconnect();
172  return false;
173  }
174 
175  /*
176  Connect, read settings from my.cnf
177  NOTE! user and password can be stored there as well
178  */
179  if (mysql_real_connect(m_mysql, NULL,
180  m_user.c_str(),
181  m_pass.c_str(),
182  m_dbname.c_str(),
183  0, NULL, 0) == NULL)
184  {
185  myerror("connection failed");
186  disconnect();
187  return false;
188  }
189  selectDb();
190  m_connected= true;
191  assert(m_mysql);
192  return true;
193 }
194 
195 
196 /* Database Logout */
197 
198 void
199 DbUtil::databaseLogout()
200 {
201  if (m_mysql){
202  #ifdef DEBUG
203  printf("\n\tClosing the MySQL database connection ...\n\n");
204  #endif
205  mysql_close(m_mysql);
206  }
207 }
208 
209 /* Prepare MySQL Statements Cont */
210 
211 MYSQL_STMT *STDCALL
212 DbUtil::mysqlSimplePrepare(const char *query)
213 {
214  #ifdef DEBUG
215  printf("Inside DbUtil::mysqlSimplePrepare\n");
216  #endif
217  MYSQL_STMT *my_stmt= mysql_stmt_init(this->getMysql());
218  if (my_stmt && mysql_stmt_prepare(my_stmt, query, strlen(query))){
219  this->printStError(my_stmt,"Prepare Statement Failed");
220  mysql_stmt_close(my_stmt);
221  return NULL;
222  }
223  return my_stmt;
224 }
225 
226 /* Close MySQL Statements Handle */
227 
228 void
229 DbUtil::mysqlCloseStmHandle(MYSQL_STMT *my_stmt)
230 {
231  mysql_stmt_close(my_stmt);
232 }
233 
234 /* Error Printing */
235 
236 void
237 DbUtil::printError(const char *msg)
238 {
239  if (m_mysql && mysql_errno(m_mysql))
240  {
241  if (m_mysql->server_version)
242  printf("\n [MySQL-%s]", m_mysql->server_version);
243  else
244  printf("\n [MySQL]");
245  printf("[%d] %s\n", getErrorNumber(), getError());
246  }
247  else if (msg)
248  printf(" [MySQL] %s\n", msg);
249 }
250 
251 void
252 DbUtil::printStError(MYSQL_STMT *stmt, const char *msg)
253 {
254  if (stmt && mysql_stmt_errno(stmt))
255  {
256  if (m_mysql && m_mysql->server_version)
257  printf("\n [MySQL-%s]", m_mysql->server_version);
258  else
259  printf("\n [MySQL]");
260 
261  printf("[%d] %s\n", mysql_stmt_errno(stmt),
262  mysql_stmt_error(stmt));
263  }
264  else if (msg)
265  printf("[MySQL] %s\n", msg);
266 }
267 
268 /* Select which database to use */
269 
270 bool
271 DbUtil::selectDb()
272 {
273  if ((getDbName()) != NULL)
274  {
275  if(mysql_select_db(m_mysql, this->getDbName()))
276  {
277  //printError("mysql_select_db failed");
278  return false;
279  }
280  return true;
281  }
282  printError("getDbName() == NULL");
283  return false;
284 }
285 
286 bool
287 DbUtil::selectDb(const char * m_db)
288 {
289  {
290  if(mysql_select_db(m_mysql, m_db))
291  {
292  printError("mysql_select_db failed");
293  return false;
294  }
295  return true;
296  }
297 }
298 
299 bool
300 DbUtil::createDb(BaseString& m_db)
301 {
302  BaseString stm;
303  setDbName(m_db.c_str());
304 
305  {
306  if(selectDb())
307  {
308  stm.assfmt("DROP DATABASE %s", m_db.c_str());
309  if(!doQuery(m_db.c_str()))
310  return false;
311  }
312  stm.assfmt("CREATE DATABASE %s", m_db.c_str());
313  if(!doQuery(m_db.c_str()))
314  return false;
315  return true;
316  }
317 }
318 
319 
320 /* Count Table Rows */
321 
322 unsigned long long
323 DbUtil::selectCountTable(const char * table)
324 {
325  BaseString query;
326  SqlResultSet result;
327 
328  query.assfmt("select count(*) as count from %s", table);
329  if (!doQuery(query, result)) {
330  printError("select count(*) failed");
331  return -1;
332  }
333  return result.columnAsLong("count");
334 }
335 
336 
337 /* Run Simple Queries */
338 
339 
340 bool
341 DbUtil::runQuery(const char* sql,
342  const Properties& args,
343  SqlResultSet& rows){
344 
345  clear_error();
346  rows.clear();
347  if (!isConnected())
348  return false;
349  assert(m_mysql);
350 
351  g_debug << "runQuery: " << endl
352  << " sql: '" << sql << "'" << endl;
353 
354 
355  MYSQL_STMT *stmt= mysql_stmt_init(m_mysql);
356  if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
357  {
358  report_error("Failed to prepare: ", m_mysql);
359  return false;
360  }
361 
362  uint params= mysql_stmt_param_count(stmt);
363  MYSQL_BIND *bind_param = new MYSQL_BIND[params];
364  NdbAutoObjArrayPtr<MYSQL_BIND> _guard(bind_param);
365 
366  memset(bind_param, 0, params * sizeof(MYSQL_BIND));
367 
368  for(uint i= 0; i < mysql_stmt_param_count(stmt); i++)
369  {
371  name.assfmt("%d", i);
372  // Parameters are named 0, 1, 2...
373  if (!args.contains(name.c_str()))
374  {
375  g_err << "param " << i << " missing" << endl;
376  assert(false);
377  }
378  PropertiesType t;
379  Uint32 val_i;
380  const char* val_s;
381  args.getTypeOf(name.c_str(), &t);
382  switch(t) {
383  case PropertiesType_Uint32:
384  args.get(name.c_str(), &val_i);
385  bind_param[i].buffer_type= MYSQL_TYPE_LONG;
386  bind_param[i].buffer= (char*)&val_i;
387  g_debug << " param" << name.c_str() << ": " << val_i << endl;
388  break;
389  case PropertiesType_char:
390  args.get(name.c_str(), &val_s);
391  bind_param[i].buffer_type= MYSQL_TYPE_STRING;
392  bind_param[i].buffer= (char*)val_s;
393  bind_param[i].buffer_length= strlen(val_s);
394  g_debug << " param" << name.c_str() << ": " << val_s << endl;
395  break;
396  default:
397  assert(false);
398  break;
399  }
400  }
401  if (mysql_stmt_bind_param(stmt, bind_param))
402  {
403  report_error("Failed to bind param: ", m_mysql);
404  mysql_stmt_close(stmt);
405  return false;
406  }
407 
408  if (mysql_stmt_execute(stmt))
409  {
410  report_error("Failed to execute: ", m_mysql);
411  mysql_stmt_close(stmt);
412  return false;
413  }
414 
415  /*
416  Update max_length, making it possible to know how big
417  buffers to allocate
418  */
419  my_bool one= 1;
420  mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one);
421 
422  if (mysql_stmt_store_result(stmt))
423  {
424  report_error("Failed to store result: ", m_mysql);
425  mysql_stmt_close(stmt);
426  return false;
427  }
428 
429  uint row= 0;
430  MYSQL_RES* res= mysql_stmt_result_metadata(stmt);
431  if (res != NULL)
432  {
433  MYSQL_FIELD *fields= mysql_fetch_fields(res);
434  uint num_fields= mysql_num_fields(res);
435  MYSQL_BIND *bind_result = new MYSQL_BIND[num_fields];
436  NdbAutoObjArrayPtr<MYSQL_BIND> _guard1(bind_result);
437  memset(bind_result, 0, num_fields * sizeof(MYSQL_BIND));
438 
439  for (uint i= 0; i < num_fields; i++)
440  {
441  unsigned long buf_len= sizeof(int);
442 
443  switch(fields[i].type){
444  case MYSQL_TYPE_STRING:
445  buf_len = fields[i].length + 1;
446  break;
447  case MYSQL_TYPE_VARCHAR:
448  case MYSQL_TYPE_VAR_STRING:
449  buf_len= fields[i].max_length + 1;
450  break;
451  case MYSQL_TYPE_LONGLONG:
452  buf_len= sizeof(long long);
453  break;
454  case MYSQL_TYPE_LONG:
455  buf_len = sizeof(long);
456  break;
457  default:
458  break;
459  }
460 
461  bind_result[i].buffer_type= fields[i].type;
462  bind_result[i].buffer= malloc(buf_len);
463  bind_result[i].buffer_length= buf_len;
464  bind_result[i].is_null = (my_bool*)malloc(sizeof(my_bool));
465  * bind_result[i].is_null = 0;
466  }
467 
468  if (mysql_stmt_bind_result(stmt, bind_result)){
469  report_error("Failed to bind result: ", m_mysql);
470  mysql_stmt_close(stmt);
471  return false;
472  }
473 
474  while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
475  {
476  Properties curr(true);
477  for (uint i= 0; i < num_fields; i++){
478  if (* bind_result[i].is_null)
479  continue;
480  switch(fields[i].type){
481  case MYSQL_TYPE_STRING:
482  ((char*)bind_result[i].buffer)[fields[i].max_length] = 0;
483  case MYSQL_TYPE_VARCHAR:
484  case MYSQL_TYPE_VAR_STRING:
485  curr.put(fields[i].name, (char*)bind_result[i].buffer);
486  break;
487 
488  case MYSQL_TYPE_LONGLONG:
489  curr.put64(fields[i].name,
490  *(unsigned long long*)bind_result[i].buffer);
491  break;
492 
493  default:
494  curr.put(fields[i].name, *(int*)bind_result[i].buffer);
495  break;
496  }
497  }
498  rows.put("row", row++, &curr);
499  }
500 
501  mysql_free_result(res);
502 
503  for (uint i= 0; i < num_fields; i++)
504  {
505  free(bind_result[i].buffer);
506  free(bind_result[i].is_null);
507  }
508  }
509 
510  // Save stats in result set
511  rows.put("rows", row);
512  rows.put64("affected_rows", mysql_affected_rows(m_mysql));
513  rows.put("mysql_errno", mysql_errno(m_mysql));
514  rows.put("mysql_error", mysql_error(m_mysql));
515  rows.put("mysql_sqlstate", mysql_sqlstate(m_mysql));
516  rows.put64("insert_id", mysql_insert_id(m_mysql));
517 
518  mysql_stmt_close(stmt);
519  return true;
520 }
521 
522 
523 bool
524 DbUtil::doQuery(const char* query){
525  const Properties args;
526  SqlResultSet result;
527  return doQuery(query, args, result);
528 }
529 
530 
531 bool
532 DbUtil::doQuery(const char* query, SqlResultSet& result){
533  Properties args;
534  return doQuery(query, args, result);
535 }
536 
537 
538 bool
539 DbUtil::doQuery(const char* query, const Properties& args,
540  SqlResultSet& result){
541  if (!runQuery(query, args, result))
542  return false;
543  result.get_row(0); // Load first row
544  return true;
545 }
546 
547 bool
548 DbUtil::doQuery(const char* query, const Properties& args){
549  SqlResultSet result;
550  return doQuery(query, args, result);
551 }
552 
553 
554 bool
555 DbUtil::doQuery(BaseString& str){
556  return doQuery(str.c_str());
557 }
558 
559 
560 bool
561 DbUtil::doQuery(BaseString& str, SqlResultSet& result){
562  return doQuery(str.c_str(), result);
563 }
564 
565 
566 bool
567 DbUtil::doQuery(BaseString& str, const Properties& args,
568  SqlResultSet& result){
569  return doQuery(str.c_str(), args, result);
570 }
571 
572 
573 bool
574 DbUtil::doQuery(BaseString& str, const Properties& args){
575  return doQuery(str.c_str(), args);
576 }
577 
578 
579 /* Return MySQL Error String */
580 
581 const char *
582 DbUtil::getError()
583 {
584  return mysql_error(this->getMysql());
585 }
586 
587 /* Return MySQL Error Number */
588 
589 int
590 DbUtil::getErrorNumber()
591 {
592  return mysql_errno(this->getMysql());
593 }
594 
595 void
596 DbUtil::report_error(const char* message, MYSQL* mysql)
597 {
598  m_last_errno= mysql_errno(mysql);
599  m_last_error.assfmt("%d: %s", m_last_errno, mysql_error(mysql));
600 
601  if (!m_silent)
602  g_err << message << m_last_error << endl;
603 }
604 
605 
606 /* DIE */
607 
608 void
609 DbUtil::die(const char *file, int line, const char *expr)
610 {
611  printf("%s:%d: check failed: '%s'\n", file, line, expr);
612  abort();
613 }
614 
615 
616 /* SqlResultSet */
617 
618 bool
619 SqlResultSet::get_row(int row_num){
620  if(!get("row", row_num, &m_curr_row)){
621  return false;
622  }
623  return true;
624 }
625 
626 
627 bool
628 SqlResultSet::next(void){
629  return get_row(++m_curr_row_num);
630 }
631 
632 
633 // Reset iterator
634 void SqlResultSet::reset(void){
635  m_curr_row_num= -1;
636  m_curr_row= 0;
637 }
638 
639 
640 // Remove row from resultset
641 void SqlResultSet::remove(){
642  BaseString row_name;
643  row_name.assfmt("row_%d", m_curr_row_num);
644  Properties::remove(row_name.c_str());
645 }
646 
647 
648 // Clear all rows and reset iterator
649 void SqlResultSet::clear(){
650  reset();
651  Properties::clear();
652 }
653 
654 
655 SqlResultSet::SqlResultSet(): m_curr_row(0), m_curr_row_num(-1){
656 }
657 
658 
659 SqlResultSet::~SqlResultSet(){
660 }
661 
662 
663 const char* SqlResultSet::column(const char* col_name){
664  const char* value;
665  if (!m_curr_row){
666  g_err << "ERROR: SqlResultSet::column("<< col_name << ")" << endl
667  << "There is no row loaded, call next() before "
668  << "acessing the column values" << endl;
669  assert(m_curr_row);
670  }
671  if (!m_curr_row->get(col_name, &value))
672  return NULL;
673  return value;
674 }
675 
676 
677 uint SqlResultSet::columnAsInt(const char* col_name){
678  uint value;
679  if (!m_curr_row){
680  g_err << "ERROR: SqlResultSet::columnAsInt("<< col_name << ")" << endl
681  << "There is no row loaded, call next() before "
682  << "acessing the column values" << endl;
683  assert(m_curr_row);
684  }
685  if (!m_curr_row->get(col_name, &value))
686  return (uint)-1;
687  return value;
688 }
689 
690 unsigned long long SqlResultSet::columnAsLong(const char* col_name){
691  unsigned long long value;
692  if (!m_curr_row){
693  g_err << "ERROR: SqlResultSet::columnAsLong("<< col_name << ")" << endl
694  << "There is no row loaded, call next() before "
695  << "acessing the column values" << endl;
696  assert(m_curr_row);
697  }
698  if (!m_curr_row->get(col_name, &value))
699  return (uint)-1;
700  return value;
701 }
702 
703 
704 unsigned long long SqlResultSet::insertId(){
705  return get_long("insert_id");
706 }
707 
708 
709 unsigned long long SqlResultSet::affectedRows(){
710  return get_long("affected_rows");
711 }
712 
713 uint SqlResultSet::numRows(void){
714  return get_int("rows");
715 }
716 
717 
718 uint SqlResultSet::mysqlErrno(void){
719  return get_int("mysql_errno");
720 }
721 
722 
723 const char* SqlResultSet::mysqlError(void){
724  return get_string("mysql_error");
725 }
726 
727 
728 const char* SqlResultSet::mysqlSqlstate(void){
729  return get_string("mysql_sqlstate");
730 }
731 
732 
733 uint SqlResultSet::get_int(const char* name){
734  uint value;
735  get(name, &value);
736  return value;
737 }
738 
739 unsigned long long SqlResultSet::get_long(const char* name){
740  unsigned long long value;
741  get(name, &value);
742  return value;
743 }
744 
745 
746 const char* SqlResultSet::get_string(const char* name){
747  const char* value;
748  get(name, &value);
749  return value;
750 }
751 
752 /* EOF */
753