00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #include "TMySQLServer.h"
00045 #include "TMySQLResult.h"
00046 #include "TMySQLStatement.h"
00047 #include "TSQLColumnInfo.h"
00048 #include "TSQLTableInfo.h"
00049 #include "TSQLRow.h"
00050 #include "TUrl.h"
00051 #include "TList.h"
00052 #include "TObjString.h"
00053 #include "TObjArray.h"
00054
00055 #include <my_global.h>
00056
00057
00058 ClassImp(TMySQLServer)
00059
00060
00061 TMySQLServer::TMySQLServer(const char *db, const char *uid, const char *pw)
00062 {
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088 fMySQL = 0;
00089 fInfo = "MySQL";
00090
00091 TUrl url(db);
00092
00093 if (!url.IsValid()) {
00094 TString errmsg("malformed db argument ");
00095 errmsg += db;
00096 SetError(-1, errmsg.Data(), "TMySQLServer");
00097 MakeZombie();
00098 return;
00099 }
00100
00101 if (strncmp(url.GetProtocol(), "mysql", 5)) {
00102 SetError(-1, "protocol in db argument should be mysql://", "TMySQLServer");
00103 MakeZombie();
00104 return;
00105 }
00106
00107 const char* dbase = url.GetFile();
00108 if (dbase!=0)
00109 if (*dbase=='/') dbase++;
00110
00111 fMySQL = new MYSQL;
00112 mysql_init(fMySQL);
00113
00114 ULong_t client_flag = 0;
00115 TString socket;
00116
00117 TString optstr = url.GetOptions();
00118 TObjArray* optarr = optstr.Tokenize("&");
00119 if (optarr!=0) {
00120 TIter next(optarr);
00121 TObject *obj = 0;
00122 while ((obj = next()) != 0) {
00123 TString opt = obj->GetName();
00124 opt.ToLower();
00125 opt.ReplaceAll(" ","");
00126 if (opt.Contains("timeout=")) {
00127 opt.Remove(0, 8);
00128 Int_t timeout = opt.Atoi();
00129 if (timeout > 0) {
00130 UInt_t mysqltimeout = (UInt_t) timeout;
00131 mysql_options(fMySQL, MYSQL_OPT_CONNECT_TIMEOUT, (const char*) &mysqltimeout);
00132 if (gDebug) Info("TMySQLServer","Set timeout %d",timeout);
00133 }
00134 } else
00135 if (opt.Contains("read_timeout=")) {
00136 #if MYSQL_VERSION_ID >= 40101
00137 opt.Remove(0, 13);
00138 Int_t timeout = opt.Atoi();
00139 if (timeout > 0) {
00140 UInt_t mysqltimeout = (UInt_t) timeout;
00141 mysql_options(fMySQL, MYSQL_OPT_READ_TIMEOUT, (const char*) &mysqltimeout);
00142 if (gDebug) Info("TMySQLServer","Set read timeout %d", timeout);
00143 }
00144 #else
00145 Warning("TMySQLServer","MYSQL_OPT_READ_TIMEOUT option not supported by this version of MySql");
00146 #endif
00147
00148 } else
00149 if (opt.Contains("write_timeout=")) {
00150 #if MYSQL_VERSION_ID >= 40101
00151 opt.Remove(0, 14);
00152 Int_t timeout = opt.Atoi();
00153 if (timeout > 0) {
00154 UInt_t mysqltimeout = (UInt_t) timeout;
00155 mysql_options(fMySQL, MYSQL_OPT_WRITE_TIMEOUT, (const char*) &mysqltimeout);
00156 if (gDebug) Info("TMySQLServer","Set write timeout %d", timeout);
00157 }
00158 #else
00159 Warning("TMySQLServer","MYSQL_OPT_WRITE_TIMEOUT option not supported by this version of MySql");
00160 #endif
00161 } else
00162 if (opt.Contains("reconnect=")) {
00163 #if MYSQL_VERSION_ID >= 50013
00164 opt.Remove(0, 10);
00165 my_bool reconnect_on = (opt=="1") || (opt=="true");
00166 mysql_options(fMySQL, MYSQL_OPT_RECONNECT, (const char*) &reconnect_on);
00167 if (gDebug) Info("TMySQLServer","Set reconnect options %s", (reconnect_on ? "ON" : "OFF"));
00168 #else
00169 Warning("TMySQLServer","MYSQL_OPT_RECONNECT option not supported by this version of MySql");
00170 #endif
00171 } else
00172 if (opt.Contains("socket=")) {
00173 socket = (obj->GetName()+7);
00174 if (gDebug) Info("TMySQLServer","Use socket %s", socket.Data());
00175 } else
00176 if (opt.Contains("multi_statements")) {
00177 #if MYSQL_VERSION_ID >= 40100
00178 client_flag = client_flag | CLIENT_MULTI_STATEMENTS;
00179 if (gDebug) Info("TMySQLServer","Use CLIENT_MULTI_STATEMENTS");
00180 #else
00181 Warning("TMySQLServer","CLIENT_MULTI_STATEMENTS not supported by this version of MySql");
00182 #endif
00183 } else
00184 if (opt.Contains("multi_results")) {
00185 #if MYSQL_VERSION_ID >= 40100
00186 client_flag = client_flag | CLIENT_MULTI_RESULTS;
00187 if (gDebug) Info("TMySQLServer","Use CLIENT_MULTI_RESULTS");
00188 #else
00189 Warning("TMySQLServer","CLIENT_MULTI_RESULTS not supported by this version of MySql");
00190 #endif
00191 } else
00192 if (opt.Contains("compress")) {
00193 mysql_options(fMySQL, MYSQL_OPT_COMPRESS, 0);
00194 if (gDebug) Info("TMySQLServer","Use compressed client/server protocol");
00195 } else
00196 if (opt.Contains("cnf_file=")) {
00197 const char* filename = (obj->GetName()+9);
00198 mysql_options(fMySQL, MYSQL_READ_DEFAULT_FILE, filename);
00199 if (gDebug) Info("TMySQLServer","Read mysql options from %s file", filename);
00200 } else
00201 if (opt.Contains("cnf_group=")) {
00202 const char* groupname = (obj->GetName()+10);
00203 mysql_options(fMySQL, MYSQL_READ_DEFAULT_GROUP, groupname);
00204 if (gDebug) Info("TMySQLServer","Read mysql options from %s group of my.cnf file", groupname);
00205 }
00206 }
00207 optarr->Delete();
00208 delete optarr;
00209 }
00210
00211 Int_t port = 3306;
00212 if (url.GetPort()>0) port = url.GetPort();
00213
00214 if (mysql_real_connect(fMySQL, url.GetHost(), uid, pw, dbase, port,
00215 (socket.Length()>0) ? socket.Data() : 0 , client_flag)) {
00216 fType = "MySQL";
00217 fHost = url.GetHost();
00218 fDB = dbase;
00219 fPort = port;
00220 } else {
00221 SetError(mysql_errno(fMySQL), mysql_error(fMySQL), "TMySQLServer");
00222 MakeZombie();
00223 }
00224 }
00225
00226
00227 TMySQLServer::~TMySQLServer()
00228 {
00229
00230
00231 if (IsConnected())
00232 Close();
00233 delete fMySQL;
00234 }
00235
00236
00237 #define CheckConnect(method, res) \
00238 { \
00239 ClearError(); \
00240 if (!IsConnected()) { \
00241 SetError(-1,"MySQL server is not connected",method); \
00242 return res; \
00243 } \
00244 }
00245
00246
00247
00248 #define CheckErrNo(method, force, res) \
00249 { \
00250 unsigned int sqlerrno = mysql_errno(fMySQL); \
00251 if ((sqlerrno!=0) || force) { \
00252 const char* sqlerrmsg = mysql_error(fMySQL); \
00253 if (sqlerrno==0) { sqlerrno = 11111; sqlerrmsg = "MySQL error"; } \
00254 SetError(sqlerrno, sqlerrmsg, method); \
00255 return res; \
00256 } \
00257 }
00258
00259
00260
00261 void TMySQLServer::Close(Option_t *)
00262 {
00263
00264
00265 ClearError();
00266
00267 if (!fMySQL)
00268 return;
00269
00270 mysql_close(fMySQL);
00271 fPort = -1;
00272 }
00273
00274
00275 TSQLResult *TMySQLServer::Query(const char *sql)
00276 {
00277
00278
00279
00280
00281 CheckConnect("Query", 0);
00282
00283 if (mysql_query(fMySQL, sql))
00284 CheckErrNo("Query",kTRUE,0);
00285
00286 MYSQL_RES *res = mysql_store_result(fMySQL);
00287 CheckErrNo("Query", kFALSE, 0);
00288
00289 return new TMySQLResult(res);
00290 }
00291
00292
00293 Bool_t TMySQLServer::Exec(const char* sql)
00294 {
00295
00296
00297
00298 CheckConnect("Exec", kFALSE);
00299
00300 if (mysql_query(fMySQL, sql))
00301 CheckErrNo("Exec",kTRUE,kFALSE);
00302
00303 return !IsError();
00304 }
00305
00306
00307 Int_t TMySQLServer::SelectDataBase(const char *dbname)
00308 {
00309
00310
00311 CheckConnect("SelectDataBase", -1);
00312
00313 Int_t res = mysql_select_db(fMySQL, dbname);
00314 if (res==0) fDB = dbname;
00315 else CheckErrNo("SelectDataBase", kTRUE, res);
00316
00317 return res;
00318 }
00319
00320
00321 TSQLResult *TMySQLServer::GetDataBases(const char *wild)
00322 {
00323
00324
00325
00326
00327
00328 CheckConnect("GetDataBases", 0);
00329
00330 MYSQL_RES *res = mysql_list_dbs(fMySQL, wild);
00331
00332 CheckErrNo("GetDataBases", kFALSE, 0);
00333
00334 return new TMySQLResult(res);
00335 }
00336
00337
00338 TSQLResult *TMySQLServer::GetTables(const char *dbname, const char *wild)
00339 {
00340
00341
00342
00343
00344
00345 CheckConnect("GetTables", 0);
00346
00347 if (SelectDataBase(dbname) != 0) return 0;
00348
00349 MYSQL_RES *res = mysql_list_tables(fMySQL, wild);
00350
00351 CheckErrNo("GetTables", kFALSE, 0);
00352
00353 return new TMySQLResult(res);
00354 }
00355
00356
00357
00358 TList* TMySQLServer::GetTablesList(const char* wild)
00359 {
00360
00361
00362 CheckConnect("GetTablesList", 0);
00363
00364 MYSQL_RES *res = mysql_list_tables(fMySQL, wild);
00365
00366 CheckErrNo("GetTablesList", kFALSE, 0);
00367
00368 MYSQL_ROW row = mysql_fetch_row(res);
00369
00370 TList* lst = 0;
00371
00372 while (row!=0) {
00373 CheckErrNo("GetTablesList", kFALSE, lst);
00374
00375 const char* tablename = row[0];
00376
00377 if (tablename!=0) {
00378 if (lst==0) {
00379 lst = new TList();
00380 lst->SetOwner(kTRUE);
00381 }
00382 lst->Add(new TObjString(tablename));
00383 }
00384
00385 row = mysql_fetch_row(res);
00386 }
00387
00388 mysql_free_result(res);
00389
00390 return lst;
00391 }
00392
00393
00394 TSQLTableInfo *TMySQLServer::GetTableInfo(const char* tablename)
00395 {
00396
00397
00398
00399 CheckConnect("GetTableInfo", 0);
00400
00401 if ((tablename==0) || (*tablename==0)) return 0;
00402
00403 TString sql;
00404 sql.Form("SELECT * FROM `%s` LIMIT 1", tablename);
00405
00406 if (mysql_query(fMySQL, sql.Data()) != 0)
00407 CheckErrNo("GetTableInfo", kTRUE, 0);
00408
00409 MYSQL_RES *res = mysql_store_result(fMySQL);
00410 CheckErrNo("GetTableInfo", kFALSE, 0);
00411
00412 unsigned int numfields = mysql_num_fields(res);
00413
00414 MYSQL_FIELD* fields = mysql_fetch_fields(res);
00415
00416 sql.Form("SHOW COLUMNS FROM `%s`", tablename);
00417 TSQLResult* showres = Query(sql.Data());
00418
00419 if (showres==0) {
00420 mysql_free_result(res);
00421 return 0;
00422 }
00423
00424 TList* lst = 0;
00425
00426 unsigned int nfield = 0;
00427
00428 TSQLRow* row = 0;
00429
00430 while ((row = showres->Next()) != 0) {
00431 const char* column_name = row->GetField(0);
00432 const char* type_name = row->GetField(1);
00433
00434 if ((nfield>=numfields) ||
00435 (strcmp(column_name, fields[nfield].name)!=0))
00436 {
00437 SetError(-1,"missmatch in column names","GetTableInfo");
00438 break;
00439 }
00440
00441 Int_t sqltype = kSQL_NONE;
00442
00443 Int_t data_size = -1;
00444 Int_t data_length = -1;
00445 Int_t data_scale = -1;
00446 Int_t data_sign = -1;
00447
00448 if (IS_NUM(fields[nfield].type)) {
00449 if (fields[nfield].flags & UNSIGNED_FLAG)
00450 data_sign = 0;
00451 else
00452 data_sign = 1;
00453 }
00454
00455 Bool_t nullable = (fields[nfield].flags & NOT_NULL_FLAG) == 0;
00456
00457 data_length = fields[nfield].length;
00458 if (data_length==0) data_length = -1;
00459
00460 #if MYSQL_VERSION_ID >= 40100
00461
00462 switch (fields[nfield].type) {
00463 case MYSQL_TYPE_TINY:
00464 case MYSQL_TYPE_SHORT:
00465 case MYSQL_TYPE_LONG:
00466 case MYSQL_TYPE_INT24:
00467 case MYSQL_TYPE_LONGLONG:
00468 sqltype = kSQL_INTEGER;
00469 break;
00470 case MYSQL_TYPE_DECIMAL:
00471 sqltype = kSQL_NUMERIC;
00472 data_scale = fields[nfield].decimals;
00473 break;
00474 case MYSQL_TYPE_FLOAT:
00475 sqltype = kSQL_FLOAT;
00476 break;
00477 case MYSQL_TYPE_DOUBLE:
00478 sqltype = kSQL_DOUBLE;
00479 break;
00480 case MYSQL_TYPE_TIMESTAMP:
00481 sqltype = kSQL_TIMESTAMP;
00482 break;
00483 case MYSQL_TYPE_DATE:
00484 case MYSQL_TYPE_TIME:
00485 case MYSQL_TYPE_DATETIME:
00486 case MYSQL_TYPE_YEAR:
00487 break;
00488 case MYSQL_TYPE_STRING:
00489 if (fields[nfield].charsetnr==63)
00490 sqltype = kSQL_BINARY;
00491 else
00492 sqltype = kSQL_CHAR;
00493 data_size = data_length;
00494 break;
00495 case MYSQL_TYPE_VAR_STRING:
00496 if (fields[nfield].charsetnr==63)
00497 sqltype = kSQL_BINARY;
00498 else
00499 sqltype = kSQL_VARCHAR;
00500 data_size = data_length;
00501 break;
00502 case MYSQL_TYPE_BLOB:
00503 if (fields[nfield].charsetnr==63)
00504 sqltype = kSQL_BINARY;
00505 else
00506 sqltype = kSQL_VARCHAR;
00507 data_size = data_length;
00508 break;
00509 case MYSQL_TYPE_SET:
00510 case MYSQL_TYPE_ENUM:
00511 case MYSQL_TYPE_GEOMETRY:
00512 case MYSQL_TYPE_NULL:
00513 break;
00514 default:
00515 if (IS_NUM(fields[nfield].type))
00516 sqltype = kSQL_NUMERIC;
00517 }
00518
00519 #endif
00520
00521 if (!lst)
00522 lst = new TList;
00523 lst->Add(new TSQLColumnInfo(column_name,
00524 type_name,
00525 nullable,
00526 sqltype,
00527 data_size,
00528 data_length,
00529 data_scale,
00530 data_sign));
00531
00532 nfield++;
00533 delete row;
00534 }
00535
00536 mysql_free_result(res);
00537 delete showres;
00538
00539 sql.Form("SHOW TABLE STATUS LIKE '%s'", tablename);
00540
00541 TSQLTableInfo* info = 0;
00542
00543 TSQLResult* stats = Query(sql.Data());
00544
00545 if (stats!=0) {
00546 row = 0;
00547
00548 while ((row = stats->Next()) != 0) {
00549 if (strcmp(row->GetField(0), tablename)!=0) {
00550 delete row;
00551 continue;
00552 }
00553 const char* comments = 0;
00554 const char* engine = 0;
00555 const char* create_time = 0;
00556 const char* update_time = 0;
00557
00558 for (int n=1;n<stats->GetFieldCount();n++) {
00559 TString fname = stats->GetFieldName(n);
00560 fname.ToLower();
00561 if (fname=="engine") engine = row->GetField(n); else
00562 if (fname=="comment") comments = row->GetField(n); else
00563 if (fname=="create_time") create_time = row->GetField(n); else
00564 if (fname=="update_time") update_time = row->GetField(n);
00565 }
00566
00567 info = new TSQLTableInfo(tablename,
00568 lst,
00569 comments,
00570 engine,
00571 create_time,
00572 update_time);
00573
00574 delete row;
00575 break;
00576 }
00577 delete stats;
00578 }
00579
00580 if (info==0)
00581 info = new TSQLTableInfo(tablename, lst);
00582
00583 return info;
00584 }
00585
00586
00587 TSQLResult *TMySQLServer::GetColumns(const char *dbname, const char *table,
00588 const char *wild)
00589 {
00590
00591
00592
00593
00594
00595 CheckConnect("GetColumns", 0);
00596
00597 if (SelectDataBase(dbname) != 0) return 0;
00598
00599 TString sql;
00600 if (wild)
00601 sql.Form("SHOW COLUMNS FROM %s LIKE '%s'", table, wild);
00602 else
00603 sql.Form("SHOW COLUMNS FROM %s", table);
00604
00605 return Query(sql.Data());
00606 }
00607
00608
00609 Int_t TMySQLServer::CreateDataBase(const char *dbname)
00610 {
00611
00612
00613 CheckConnect("CreateDataBase", -1);
00614
00615 Int_t res = mysql_query(fMySQL, Form("CREATE DATABASE %s",dbname));
00616
00617 CheckErrNo("CreateDataBase", kFALSE, res);
00618
00619 return res;
00620 }
00621
00622
00623 Int_t TMySQLServer::DropDataBase(const char *dbname)
00624 {
00625
00626
00627
00628 CheckConnect("DropDataBase", -1);
00629
00630 Int_t res = mysql_query(fMySQL, Form("DROP DATABASE %s",dbname));
00631
00632 CheckErrNo("DropDataBase", kFALSE, res);
00633
00634 return res;
00635 }
00636
00637
00638 Int_t TMySQLServer::Reload()
00639 {
00640
00641
00642
00643 CheckConnect("Reload", -1);
00644
00645 Int_t res = mysql_reload(fMySQL);
00646
00647 CheckErrNo("Reload", kFALSE, res);
00648
00649 return res;
00650 }
00651
00652
00653 Int_t TMySQLServer::Shutdown()
00654 {
00655
00656
00657
00658 CheckConnect("Shutdown", -1);
00659
00660 Int_t res;
00661
00662 #if MYSQL_VERSION_ID >= 50001 || \
00663 (MYSQL_VERSION_ID < 50000 && MYSQL_VERSION_ID >= 40103)
00664 res = mysql_shutdown(fMySQL, SHUTDOWN_DEFAULT);
00665 #else
00666 res = mysql_shutdown(fMySQL);
00667 #endif
00668
00669 CheckErrNo("Shutdown", kFALSE, res);
00670
00671 return res;
00672 }
00673
00674
00675 const char *TMySQLServer::ServerInfo()
00676 {
00677
00678
00679 CheckConnect("ServerInfo", 0);
00680
00681 const char* res = mysql_get_server_info(fMySQL);
00682
00683 CheckErrNo("ServerInfo", kFALSE, res);
00684
00685 fInfo = "MySQL ";
00686 fInfo += res;
00687
00688 return fInfo.Data();
00689 }
00690
00691
00692 Bool_t TMySQLServer::HasStatement() const
00693 {
00694
00695
00696
00697 #if MYSQL_VERSION_ID < 40100
00698 return kFALSE;
00699 #else
00700 return kTRUE;
00701 #endif
00702 }
00703
00704
00705
00706 TSQLStatement *TMySQLServer::Statement(const char *sql, Int_t)
00707 {
00708
00709
00710 #if MYSQL_VERSION_ID < 40100
00711 ClearError();
00712 SetError(-1, "Statement class does not supported by MySQL version < 4.1", "Statement");
00713 return 0;
00714 #else
00715
00716 CheckConnect("Statement", 0);
00717
00718 if (!sql || !*sql) {
00719 SetError(-1, "no query string specified","Statement");
00720 return 0;
00721 }
00722
00723 MYSQL_STMT *stmt = mysql_stmt_init(fMySQL);
00724 if (!stmt)
00725 CheckErrNo("Statement", kTRUE, 0);
00726
00727 if (mysql_stmt_prepare(stmt, sql, strlen(sql))) {
00728 SetError(mysql_errno(fMySQL), mysql_error(fMySQL), "Statement");
00729 mysql_stmt_close(stmt);
00730 return 0;
00731 }
00732
00733 return new TMySQLStatement(stmt, fErrorOut);
00734
00735 #endif
00736 }
00737
00738
00739 Bool_t TMySQLServer::StartTransaction()
00740 {
00741
00742
00743 CheckConnect("StartTransaction", kFALSE);
00744
00745 return TSQLServer::StartTransaction();
00746 }
00747
00748
00749 Bool_t TMySQLServer::Commit()
00750 {
00751
00752
00753 CheckConnect("Commit", kFALSE);
00754
00755 #if MYSQL_VERSION_ID >= 40100
00756
00757 if (mysql_commit(fMySQL))
00758 CheckErrNo("Commit", kTRUE, kFALSE);
00759
00760 return kTRUE;
00761
00762 #else
00763
00764 return TSQLServer::Commit();
00765
00766 #endif
00767
00768 }
00769
00770
00771 Bool_t TMySQLServer::Rollback()
00772 {
00773
00774
00775 CheckConnect("Rollback", kFALSE);
00776
00777 #if MYSQL_VERSION_ID >= 40100
00778
00779 if (mysql_rollback(fMySQL))
00780 CheckErrNo("Rollback", kTRUE, kFALSE);
00781
00782 return kTRUE;
00783
00784 #else
00785
00786 return TSQLServer::Rollback();
00787
00788 #endif
00789
00790 }
00791
00792
00793 Bool_t TMySQLServer::PingVerify()
00794 {
00795
00796
00797
00798
00799
00800 CheckConnect("Ping", kFALSE);
00801
00802 if (mysql_ping(fMySQL)) {
00803 if (mysql_ping(fMySQL)) {
00804 Error("PingVerify", "not able to automatically reconnect a second time");
00805 CheckErrNo("Ping", kTRUE, kFALSE);
00806 } else
00807 Info("PingVerify", "connection was lost, but could automatically reconnect");
00808 }
00809
00810 return !IsError();
00811 }
00812
00813
00814 Int_t TMySQLServer::Ping()
00815 {
00816
00817
00818
00819 CheckConnect("PingInt", kFALSE);
00820
00821 return mysql_ping(fMySQL);
00822 }