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