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