Otclient  14/8/2020
mysql.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #include "mysql.h"
24 
25 #include <mysql/errmsg.h>
26 
27 #include <framework/core/logger.h>
28 
30 {
31  m_handle = new MYSQL();
32  if(!mysql_init(m_handle)) {
33  g_logger.fatal("Failed to initialize MySQL connection handle.");
34  }
35 
36  my_bool reconnect = true;
37  mysql_options(m_handle, MYSQL_OPT_RECONNECT, &reconnect);
38 }
39 
41 {
42  mysql_close(m_handle);
43  delete m_handle;
44 }
45 
46 void DatabaseMySQL::connect(const std::string& host, const std::string& user, const std::string& pass,
47  const std::string& db, uint16 port, const std::string& unix_socket)
48 {
49  if(!mysql_real_connect(m_handle,
50  host.c_str(),
51  user.c_str(),
52  pass.c_str(),
53  db.c_str(),
54  port,
55  unix_socket.empty() ? NULL : unix_socket.c_str(), 0)) {
56  g_logger.error(stdext::format("Failed to connect to database. MYSQL ERROR: %s", mysql_error(m_handle)));
57  }
58  else {
59  setConnected(true);
60  }
61 }
62 
64 {
65  unsigned int error = mysql_errno(m_handle);
66  g_logger.error(stdext::format("MYSQL error code = %d, message: %s", error, mysql_error(m_handle)));
67 
68  if(error == CR_SOCKET_CREATE_ERROR ||
69  error == CR_CONNECTION_ERROR ||
70  error == CR_CONN_HOST_ERROR ||
71  error == CR_IPSOCK_ERROR ||
72  error == CR_UNKNOWN_HOST ||
73  error == CR_SERVER_GONE_ERROR ||
74  error == CR_SERVER_LOST ||
75  error == CR_SERVER_HANDSHAKE_ERR) {
76  g_logger.error("MYSQL connection lost, trying to reconnect...");
77  setConnected(false);
78 
79  ticks_t startTime = stdext::millis();
80  while(true) {
81  bool connected = (mysql_ping(m_handle) == 0);
82  ticks_t diffTime = (stdext::millis() - startTime);
83  if(connected) {
84  g_logger.info(stdext::format("MySQL reconneted in %d ms", diffTime));
85  setConnected(true);
86  return true;
87  }
88  stdext::millisleep(100);
89  }
90  }
91 
92  return false;
93 }
94 
96 {
97  return executeQuery("BEGIN");
98 }
99 
101 {
102  if(mysql_rollback(m_handle)) {
103  g_logger.error(stdext::format("[DatabaseMySQL::rollback] ERROR: %s (%s)", mysql_error(m_handle), mysql_errno(m_handle)));
104  return false;
105  }
106 
107  return true;
108 }
109 
111 {
112  if(mysql_commit(m_handle)) {
113  g_logger.error(stdext::format("[DatabaseMySQL::commit] ERROR: %s (%s)", mysql_error(m_handle), mysql_errno(m_handle)));
114  return false;
115  }
116 
117  return true;
118 }
119 
120 bool DatabaseMySQL::internalExecuteQuery(const std::string &query)
121 {
122  while(mysql_real_query(m_handle, query.c_str(), query.length()) != 0) {
123  if(!handleError()) {
124  return false;
125  }
126  }
127 
128  return true;
129 }
130 
131 bool DatabaseMySQL::executeQuery(const std::string &query)
132 {
133  if(internalExecuteQuery(query)) {
134  MYSQL_RES* res = mysql_store_result(m_handle);
135 
136  if(res) {
137  mysql_free_result(res);
138  } else if(mysql_errno(m_handle) != 0) {
139  handleError();
140  }
141 
142  return true;
143  }
144 
145  return false;
146 }
147 
148 DBResultPtr DatabaseMySQL::storeQuery(const std::string& query)
149 {
150  while(internalExecuteQuery(query)) {
151  MYSQL_RES* res = mysql_store_result(m_handle);
152 
153  if(res) {
155  if(!verifyResult(result))
156  break;
157 
158  return result;
159  }
160  else if(mysql_errno(m_handle) != 0) {
161  if(!handleError())
162  break;
163  }
164 
165  stdext::millisleep(10);
166  }
167 
168  return nullptr;
169 }
170 
172 {
173  return (uint64)mysql_insert_id(m_handle);
174 }
175 
176 std::string DatabaseMySQL::escapeString(const std::string &s)
177 {
178  return escapeBlob( s.c_str(), s.length() );
179 }
180 
181 std::string DatabaseMySQL::escapeBlob(const char* s, uint32 length)
182 {
183  if(!s) {
184  return std::string();
185  }
186 
187  char* output = new char[length * 2 + 1];
188  mysql_real_escape_string(m_handle, output, s, length);
189 
190  std::string res = "'";
191  res += output;
192  res += "'";
193 
194  delete[] output;
195  return res;
196 }
197 
198 int32 MySQLResult::getDataInt(const std::string& s)
199 {
200  RowNames_t::iterator it = m_names.find(s);
201  if(it != m_names.end())
202  return m_row[it->second] ? atoi(m_row[it->second]) : 0;
203 
204  g_logger.error(stdext::format("[MySQLResult::getDataInt] Error: %d", s));
205  return 0;
206 }
207 
208 int64 MySQLResult::getDataLong(const std::string& s)
209 {
210  RowNames_t::iterator it = m_names.find(s);
211  if(it != m_names.end())
212  return m_row[it->second] ? atoll(m_row[it->second]) : 0;
213 
214  g_logger.error(stdext::format("[MySQLResult::getDataLong] Error: %d", s));
215  return 0;
216 }
217 
218 std::string MySQLResult::getDataString(const std::string& s)
219 {
220  RowNames_t::iterator it = m_names.find(s);
221  if(it != m_names.end())
222  return m_row[it->second] ? std::string(m_row[it->second]) : std::string();
223 
224  g_logger.error(stdext::format("[MySQLResult::getDataString] Error: %d", s));
225  return std::string();
226 }
227 
228 const char* MySQLResult::getDataStream(const std::string& s, uint64& size)
229 {
230  size = 0;
231  RowNames_t::iterator it = m_names.find(s);
232  if(it == m_names.end()) {
233  g_logger.error(stdext::format("[MySQLResult::getDataStream] Error: %d", s));
234  return NULL;
235  }
236 
237  if(!m_row[it->second])
238  return NULL;
239 
240  size = mysql_fetch_lengths(m_result)[it->second];
241  return m_row[it->second];
242 }
243 
245 {
246  if(!m_result) {
247  g_logger.fatal("[MySQLResult::free] Error: trying to free already freed result");
248  return;
249  }
250 
251  mysql_free_result(m_result);
252  m_result = NULL;
253 }
254 
256 {
257  m_row = mysql_fetch_row(m_result);
258  return (m_row != NULL);
259 }
260 
262 {
263  if(m_result)
264  mysql_free_result(m_result);
265 }
266 
267 MySQLResult::MySQLResult(MYSQL_RES* result)
268 {
269  m_result = result;
270  m_names.clear();
271 
272  MYSQL_FIELD* field;
273  int32 i = 0;
274  while((field = mysql_fetch_field(m_result))) {
275  m_names[field->name] = i++;
276  }
277 }
stdext::millis
ticks_t millis()
Definition: time.cpp:37
MySQLResult::getDataString
virtual std::string getDataString(const std::string &s)
Definition: mysql.cpp:218
Database::setConnected
void setConnected(bool connected)
Definition: database.h:139
DatabaseMySQL::internalExecuteQuery
bool internalExecuteQuery(const std::string &query)
Definition: mysql.cpp:120
MySQLResult::~MySQLResult
virtual ~MySQLResult()
Definition: mysql.cpp:261
DatabaseMySQL::executeQuery
virtual bool executeQuery(const std::string &query)
Definition: mysql.cpp:131
DatabaseMySQL::DatabaseMySQL
DatabaseMySQL()
Definition: mysql.cpp:29
DatabaseMySQL::getLastInsertedRowID
virtual uint64 getLastInsertedRowID()
Definition: mysql.cpp:171
DatabaseMySQL::commit
virtual bool commit()
Definition: mysql.cpp:110
logger.h
MySQLResult::MySQLResult
MySQLResult(MYSQL_RES *result)
Definition: mysql.cpp:267
DatabaseMySQL::storeQuery
virtual DBResultPtr storeQuery(const std::string &query)
Definition: mysql.cpp:148
uint32
uint32_t uint32
Definition: types.h:35
MySQLResult::m_result
MYSQL_RES * m_result
Definition: mysql.h:87
mysql.h
DatabaseMySQL::rollback
virtual bool rollback()
Definition: mysql.cpp:100
MySQLResult
Definition: mysql.h:65
MySQLResult::free
virtual void free()
Definition: mysql.cpp:244
Logger::error
void error(const std::string &what)
Definition: logger.h:54
MySQLResult::getDataStream
virtual const char * getDataStream(const std::string &s, uint64 &size)
Definition: mysql.cpp:228
ticks_t
int64 ticks_t
Definition: types.h:43
MySQLResult::m_row
MYSQL_ROW m_row
Definition: mysql.h:88
DatabaseMySQL::escapeString
virtual std::string escapeString(const std::string &s)
Definition: mysql.cpp:176
int64
int64_t int64
Definition: types.h:38
stdext::format
std::string format()
Definition: format.h:82
stdext::millisleep
void millisleep(size_t ms)
Definition: time.cpp:45
uint16
uint16_t uint16
Definition: types.h:36
Logger::fatal
void fatal(const std::string &what)
Definition: logger.h:55
g_logger
Logger g_logger
Definition: logger.cpp:35
int32
int32_t int32
Definition: types.h:39
MySQLResult::next
virtual bool next()
Definition: mysql.cpp:255
DatabaseMySQL::~DatabaseMySQL
virtual ~DatabaseMySQL()
Definition: mysql.cpp:40
DatabaseMySQL::beginTransaction
virtual bool beginTransaction()
Definition: mysql.cpp:95
MySQLResult::getDataLong
virtual int64 getDataLong(const std::string &s)
Definition: mysql.cpp:208
Database::verifyResult
DBResultPtr verifyResult(DBResultPtr result)
Definition: database.cpp:27
DatabaseMySQL::connect
virtual void connect(const std::string &host, const std::string &user, const std::string &pass, const std::string &db, uint16 port, const std::string &unix_socket="")
Definition: mysql.cpp:46
uint64
uint64_t uint64
Definition: types.h:34
stdext::shared_object_ptr
Definition: shared_object.h:39
DatabaseMySQL::handleError
bool handleError()
Definition: mysql.cpp:63
MySQLResult::getDataInt
virtual int32 getDataInt(const std::string &s)
Definition: mysql.cpp:198
DatabaseMySQL::escapeBlob
virtual std::string escapeBlob(const char *s, uint32 length)
Definition: mysql.cpp:181
DBResultPtr
stdext::shared_object_ptr< DBResult > DBResultPtr
Definition: declarations.h:32
MySQLResultPtr
stdext::shared_object_ptr< MySQLResult > MySQLResultPtr
Definition: declarations.h:39
MySQLResult::m_names
RowNames_t m_names
Definition: mysql.h:85
DatabaseMySQL::m_handle
MYSQL * m_handle
Definition: mysql.h:62
Logger::info
void info(const std::string &what)
Definition: logger.h:52