PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/semistable/server/src/dal/mysqldataprovider.cpp

https://gitlab.com/ablu/invertika-backup-invertika
C++ | 454 lines | 305 code | 84 blank | 65 comment | 44 complexity | 2d46b316ceb88ac8f2b76e9877e812f9 MD5 | raw file
  1. /*
  2. * The Mana Server
  3. * Copyright (C) 2004-2010 The Mana World Development Team
  4. *
  5. * This file is part of The Mana Server.
  6. *
  7. * The Mana Server is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * any later version.
  11. *
  12. * The Mana Server is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include "mysqldataprovider.h"
  21. #include "dalexcept.h"
  22. namespace dal
  23. {
  24. const std::string MySqlDataProvider::CFGPARAM_MYSQL_HOST ="mysql_hostname";
  25. const std::string MySqlDataProvider::CFGPARAM_MYSQL_PORT ="mysql_port";
  26. const std::string MySqlDataProvider::CFGPARAM_MYSQL_DB ="mysql_database";
  27. const std::string MySqlDataProvider::CFGPARAM_MYSQL_USER ="mysql_username";
  28. const std::string MySqlDataProvider::CFGPARAM_MYSQL_PWD ="mysql_password";
  29. const std::string MySqlDataProvider::CFGPARAM_MYSQL_HOST_DEF = "localhost";
  30. const unsigned int MySqlDataProvider::CFGPARAM_MYSQL_PORT_DEF = 3306;
  31. const std::string MySqlDataProvider::CFGPARAM_MYSQL_DB_DEF = "mana";
  32. const std::string MySqlDataProvider::CFGPARAM_MYSQL_USER_DEF = "mana";
  33. const std::string MySqlDataProvider::CFGPARAM_MYSQL_PWD_DEF = "mana";
  34. /**
  35. * Constructor.
  36. */
  37. MySqlDataProvider::MySqlDataProvider()
  38. throw()
  39. : mDb(0)
  40. {
  41. }
  42. /**
  43. * Destructor.
  44. */
  45. MySqlDataProvider::~MySqlDataProvider()
  46. throw()
  47. {
  48. // we are using the MySQL C API, there are no exceptions to catch.
  49. // make sure that the database is closed.
  50. // disconnect() calls mysql_close() which takes care of freeing
  51. // the memory allocated for the handle.
  52. if (mIsConnected) {
  53. disconnect();
  54. }
  55. }
  56. /**
  57. * Get the database backend name.
  58. */
  59. DbBackends MySqlDataProvider::getDbBackend() const
  60. throw()
  61. {
  62. return DB_BKEND_MYSQL;
  63. }
  64. /**
  65. * Create a connection to the database.
  66. */
  67. void MySqlDataProvider::connect()
  68. {
  69. if (mIsConnected) {
  70. return;
  71. }
  72. // retrieve configuration from config file
  73. const std::string hostname
  74. = Configuration::getValue(CFGPARAM_MYSQL_HOST, CFGPARAM_MYSQL_HOST_DEF);
  75. const std::string dbName
  76. = Configuration::getValue(CFGPARAM_MYSQL_DB, CFGPARAM_MYSQL_DB_DEF);
  77. const std::string username
  78. = Configuration::getValue(CFGPARAM_MYSQL_USER, CFGPARAM_MYSQL_USER_DEF);
  79. const std::string password
  80. = Configuration::getValue(CFGPARAM_MYSQL_PWD, CFGPARAM_MYSQL_PWD_DEF);
  81. const unsigned int tcpPort
  82. = Configuration::getValue(CFGPARAM_MYSQL_PORT, CFGPARAM_MYSQL_PORT_DEF);
  83. // allocate and initialize a new MySQL object suitable
  84. // for mysql_real_connect().
  85. mDb = mysql_init(NULL);
  86. if (!mDb) {
  87. throw DbConnectionFailure(
  88. "unable to initialize the MySQL library: no memory");
  89. }
  90. LOG_INFO("Trying to connect with mySQL database server '"
  91. << hostname << ":" << tcpPort << "' using '" << username
  92. << "' as user, and '" << dbName << "' as database.");
  93. // actually establish the connection.
  94. if (!mysql_real_connect(mDb, // handle to the connection
  95. hostname.c_str(), // hostname
  96. username.c_str(), // username
  97. password.c_str(), // password
  98. dbName.c_str(), // database name
  99. tcpPort, // tcp port
  100. NULL, // socket, currently not used
  101. CLIENT_FOUND_ROWS)) // client flags
  102. {
  103. std::string msg(mysql_error(mDb));
  104. mysql_close(mDb);
  105. throw DbConnectionFailure(msg);
  106. }
  107. // Save the Db Name.
  108. mDbName = dbName;
  109. mStmt = mysql_stmt_init(mDb);
  110. mIsConnected = true;
  111. LOG_INFO("Connection to mySQL was sucessfull.");
  112. }
  113. /**
  114. * Execute a SQL query.
  115. */
  116. const RecordSet&
  117. MySqlDataProvider::execSql(const std::string& sql,
  118. const bool refresh)
  119. {
  120. if (!mIsConnected) {
  121. throw std::runtime_error("not connected to database");
  122. }
  123. LOG_DEBUG("MySqlDataProvider::execSql Performing SQL query: "<<sql);
  124. // do something only if the query is different from the previous
  125. // or if the cache must be refreshed
  126. // otherwise just return the recordset from cache.
  127. if (refresh || (sql != mSql)) {
  128. mRecordSet.clear();
  129. // actually execute the query.
  130. if (mysql_query(mDb, sql.c_str()) != 0) {
  131. throw DbSqlQueryExecFailure(mysql_error(mDb));
  132. }
  133. if (mysql_field_count(mDb) > 0) {
  134. MYSQL_RES* res;
  135. // get the result of the query.
  136. if (!(res = mysql_store_result(mDb))) {
  137. throw DbSqlQueryExecFailure(mysql_error(mDb));
  138. }
  139. // set the field names.
  140. unsigned int nFields = mysql_num_fields(res);
  141. MYSQL_FIELD* fields = mysql_fetch_fields(res);
  142. Row fieldNames;
  143. for (unsigned int i = 0; i < nFields; ++i) {
  144. fieldNames.push_back(fields[i].name);
  145. }
  146. mRecordSet.setColumnHeaders(fieldNames);
  147. // populate the RecordSet.
  148. MYSQL_ROW row;
  149. while ((row = mysql_fetch_row(res))) {
  150. Row r;
  151. for (unsigned int i = 0; i < nFields; ++i) {
  152. r.push_back(static_cast<char *>(row[i]));
  153. }
  154. mRecordSet.add(r);
  155. }
  156. // free memory
  157. mysql_free_result(res);
  158. }
  159. }
  160. return mRecordSet;
  161. }
  162. /**
  163. * Close the connection to the database.
  164. */
  165. void MySqlDataProvider::disconnect()
  166. {
  167. if (!mIsConnected) {
  168. return;
  169. }
  170. // mysql_close() closes the connection and deallocates the connection
  171. // handle allocated by mysql_init().
  172. mysql_close(mDb);
  173. mysql_stmt_close(mStmt);
  174. // deinitialize the MySQL client library.
  175. mysql_library_end();
  176. mDb = 0;
  177. mIsConnected = false;
  178. }
  179. void MySqlDataProvider::beginTransaction()
  180. throw (std::runtime_error)
  181. {
  182. if (!mIsConnected)
  183. {
  184. const std::string error = "Trying to begin a transaction while not "
  185. "connected to the database!";
  186. LOG_ERROR(error);
  187. throw std::runtime_error(error);
  188. }
  189. mysql_autocommit(mDb, AUTOCOMMIT_OFF);
  190. execSql("BEGIN");
  191. LOG_DEBUG("SQL: started transaction");
  192. }
  193. void MySqlDataProvider::commitTransaction()
  194. throw (std::runtime_error)
  195. {
  196. if (!mIsConnected)
  197. {
  198. const std::string error = "Trying to commit a transaction while not "
  199. "connected to the database!";
  200. LOG_ERROR(error);
  201. throw std::runtime_error(error);
  202. }
  203. if (mysql_commit(mDb) != 0)
  204. {
  205. LOG_ERROR("MySqlDataProvider::commitTransaction: " << mysql_error(mDb));
  206. throw DbSqlQueryExecFailure(mysql_error(mDb));
  207. }
  208. mysql_autocommit(mDb, AUTOCOMMIT_ON);
  209. LOG_DEBUG("SQL: commited transaction");
  210. }
  211. void MySqlDataProvider::rollbackTransaction()
  212. throw (std::runtime_error)
  213. {
  214. if (!mIsConnected)
  215. {
  216. const std::string error = "Trying to rollback a transaction while not "
  217. "connected to the database!";
  218. LOG_ERROR(error);
  219. throw std::runtime_error(error);
  220. }
  221. if (mysql_rollback(mDb) != 0)
  222. {
  223. LOG_ERROR("MySqlDataProvider::rollbackTransaction: " << mysql_error(mDb));
  224. throw DbSqlQueryExecFailure(mysql_error(mDb));
  225. }
  226. mysql_autocommit(mDb, AUTOCOMMIT_ON);
  227. LOG_DEBUG("SQL: transaction rolled back");
  228. }
  229. unsigned MySqlDataProvider::getModifiedRows() const
  230. {
  231. if (!mIsConnected)
  232. {
  233. const std::string error = "Trying to getModifiedRows while not "
  234. "connected to the database!";
  235. LOG_ERROR(error);
  236. throw std::runtime_error(error);
  237. }
  238. // FIXME: not sure if this is correct to bring 64bit int into int?
  239. const my_ulonglong affected = mysql_affected_rows(mDb);
  240. if (affected > INT_MAX)
  241. throw std::runtime_error("MySqlDataProvider::getLastId exceeded INT_MAX");
  242. if (affected == (my_ulonglong)-1)
  243. {
  244. LOG_ERROR("MySqlDataProvider::getModifiedRows: " << mysql_error(mDb));
  245. throw DbSqlQueryExecFailure(mysql_error(mDb));
  246. }
  247. return (unsigned) affected;
  248. }
  249. unsigned MySqlDataProvider::getLastId() const
  250. {
  251. if (!mIsConnected)
  252. {
  253. const std::string error = "not connected to the database!";
  254. LOG_ERROR(error);
  255. throw std::runtime_error(error);
  256. }
  257. // FIXME: not sure if this is correct to bring 64bit int into int?
  258. const my_ulonglong lastId = mysql_insert_id(mDb);
  259. if (lastId > UINT_MAX)
  260. throw std::runtime_error("MySqlDataProvider::getLastId exceeded INT_MAX");
  261. return (unsigned) lastId;
  262. }
  263. bool MySqlDataProvider::prepareSql(const std::string &sql)
  264. {
  265. if (!mIsConnected)
  266. return false;
  267. LOG_DEBUG("MySqlDataProvider::prepareSql Preparing SQL statement: "<<sql);
  268. mBind.clear();
  269. if (mysql_stmt_prepare(mStmt, sql.c_str(), sql.size()) != 0)
  270. {
  271. return false;
  272. }
  273. return true;
  274. }
  275. const RecordSet &MySqlDataProvider::processSql()
  276. {
  277. MYSQL_BIND* paramsBind;
  278. unsigned int i;
  279. if (!mIsConnected) {
  280. throw std::runtime_error("not connected to database");
  281. }
  282. paramsBind = new MYSQL_BIND[mBind.size()];
  283. for (i = 0; i < mBind.size(); ++i) {
  284. paramsBind[i].buffer_type = mBind[i]->buffer_type;
  285. paramsBind[i].buffer = mBind[i]->buffer;
  286. paramsBind[i].buffer_length = mBind[i]->buffer_length;
  287. paramsBind[i].is_null = 0;
  288. paramsBind[i].length = mBind[i]->length;
  289. }
  290. if (mysql_stmt_bind_param(mStmt, paramsBind))
  291. {
  292. LOG_ERROR("MySqlDataProvider::processSql Bind params failed: " << mysql_stmt_error(mStmt));
  293. }
  294. if (mysql_stmt_field_count(mStmt) > 0) {
  295. mRecordSet.clear();
  296. MYSQL_BIND* resultBind;
  297. MYSQL_RES* res;
  298. if (mysql_stmt_execute(mStmt))
  299. {
  300. LOG_ERROR("MySqlDataProvider::processSql Execute failed: " << mysql_stmt_error(mStmt));
  301. }
  302. res = mysql_stmt_result_metadata(mStmt);
  303. // set the field names.
  304. unsigned int nFields = mysql_num_fields(res);
  305. MYSQL_FIELD* fields = mysql_fetch_fields(res);
  306. Row fieldNames;
  307. resultBind = new MYSQL_BIND[mysql_num_fields(res)];
  308. for (i = 0; i < mysql_num_fields(res); ++i) {
  309. resultBind[i].buffer_type = MYSQL_TYPE_STRING;
  310. resultBind[i].buffer = (void*) new char[255];
  311. resultBind[i].buffer_length = 255;
  312. resultBind[i].is_null = new my_bool;
  313. resultBind[i].length = new unsigned long;
  314. resultBind[i].error = new my_bool;
  315. }
  316. if (mysql_stmt_bind_result(mStmt, resultBind))
  317. {
  318. LOG_ERROR("MySqlDataProvider::processSql Bind result failed: " << mysql_stmt_error(mStmt));
  319. }
  320. for (i = 0; i < nFields; ++i) {
  321. fieldNames.push_back(fields[i].name);
  322. }
  323. mRecordSet.setColumnHeaders(fieldNames);
  324. // store the result of the query.
  325. if (mysql_stmt_store_result(mStmt)) {
  326. throw DbSqlQueryExecFailure(mysql_stmt_error(mStmt));
  327. }
  328. // populate the RecordSet.
  329. while (!mysql_stmt_fetch(mStmt)) {
  330. Row r;
  331. for (unsigned int i = 0; i < nFields; ++i) {
  332. r.push_back(static_cast<char *>(resultBind[i].buffer));
  333. }
  334. mRecordSet.add(r);
  335. }
  336. delete[] resultBind;
  337. }
  338. else
  339. {
  340. if (mysql_stmt_execute(mStmt))
  341. {
  342. LOG_ERROR("MySqlDataProvider::processSql Execute failed: " << mysql_stmt_error(mStmt));
  343. }
  344. }
  345. // free memory
  346. delete[] paramsBind;
  347. mysql_stmt_free_result(mStmt);
  348. return mRecordSet;
  349. }
  350. void MySqlDataProvider::bindValue(int place, const std::string &value)
  351. {
  352. unsigned long* size = new unsigned long;
  353. *size = value.size();
  354. MYSQL_BIND* bind = new MYSQL_BIND;
  355. bind->buffer_type= MYSQL_TYPE_STRING;
  356. bind->buffer= (void*) value.c_str();
  357. bind->buffer_length= value.size();
  358. bind->length = size;
  359. //FIXME : Isn't taking care of the place param
  360. mBind.push_back(bind);
  361. }
  362. void MySqlDataProvider::bindValue(int place, int value)
  363. {
  364. MYSQL_BIND* bind = new MYSQL_BIND;
  365. bind->buffer_type= MYSQL_TYPE_LONG;
  366. bind->buffer= &value;
  367. //FIXME : Isn't taking care of the place param
  368. mBind.push_back(bind);
  369. }
  370. } // namespace dal