EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
dbcore.cpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------
3  * LICENSE:
4  * ------------------------------------------------------------------------------------
5  * This file is part of EVEmu: EVE Online Server Emulator
6  * Copyright 2006 - 2021 The EVEmu Team
7  * For the latest information visit https://evemu.dev
8  * ------------------------------------------------------------------------------------
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Lesser General Public License as published by the Free Software
11  * Foundation; either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License along with
19  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
21  * http://www.gnu.org/copyleft/lesser.txt.
22  * ------------------------------------------------------------------------------------
23  * Author: Zhur
24  */
25 
26 #include "eve-core.h"
27 
28 #include "database/dbcore.h"
29 
30 #include "log/LogNew.h"
31 #include "log/logsys.h"
32 #include "utils/misc.h"
33 #include "utils/utils_time.h"
34 //#include "../eve-server/Profiler.h"
35 
36 #define COLUMN_BOUNDS_CHECKING
37 
38 // this is to enable profile tracking for db
39 #define sProfiler ( Profiler::get() )
40 
41 class Profiler
42 : public Singleton<Profiler>
43 {
44 public:
45  void AddTime(uint8 key, double value);
46 };
47 
48 
50 : mysql(nullptr),
51 pSocket(false),
52 pStatus(Closed),
53 pReconnect(false),
54 pProfile(false)
55 {
56  mysql_thread_init(); // this is for each thread used for db connections
57  mysql = mysql_init(nullptr);
58 }
59 
60 void DBcore::Connect(uint* errnum, char* errbuf)
61 {
62  sLog.Cyan(" DB User", " %s", pUser.c_str());
63  sLog.Cyan(" DataBase", " %s", pDatabase.c_str());
64 
65  // options should be called BEFORE mysql_real_connect()
66  if (pSocket) {
67  enum mysql_protocol_type prot_type = MYSQL_PROTOCOL_SOCKET;
68  if (mysql_options(mysql, MYSQL_OPT_PROTOCOL, (void*)&prot_type) == 0) {
69  sLog.Cyan(" DB Server", " Unix Socket Connection");
70  } else {
71  sLog.Error(" DB Server", " Unix Socket Connection Option Failed");
72  enum mysql_protocol_type prot_type = MYSQL_PROTOCOL_TCP;
73  if (mysql_options(mysql, MYSQL_OPT_PROTOCOL, (void*)&prot_type) == 0)
74  sLog.Cyan(" DB Server", " %s:%d", pHost.c_str(), pPort);
75  else
76  sLog.Error(" DB Server", " TCP Connection Option Failed");
77  }
78  } else {
79  enum mysql_protocol_type prot_type = MYSQL_PROTOCOL_TCP;
80  if (mysql_options(mysql, MYSQL_OPT_PROTOCOL, (void*)&prot_type) == 0)
81  sLog.Cyan(" DB Server", " %s:%d", pHost.c_str(), pPort);
82  else
83  sLog.Error(" DB Server", " TCP Connection Option Failed");
84  }
85 
86  int32 flags = CLIENT_FOUND_ROWS; //2
87  if (pCompress)
88  flags |= CLIENT_COMPRESS; //32
89  // sql-ssl needs more info/settings to properly use....however, not needed when using socket under linux
90  if (pSSL and !pSocket)
91  flags |= CLIENT_SSL;
92  sLog.Cyan(" Connect Flags", " %x", flags);
93  /*
94  * unsigned int conn_timeout = 2;
95  * // not sure if this one will really be used here
96  * if (mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void*)&conn_timeout) == 0) {
97  * if (conn_timeout > 60)
98  * sLog.Error(" DataBase Manager", "Connection Timeout set to %us", conn_timeout);
99  * else if (conn_timeout > 40)
100  * sLog.Yellow(" DataBase Manager", "Connection Timeout set to %us", conn_timeout);
101  * else if (conn_timeout > 30)
102  * sLog.Cyan(" DataBase Manager", "Connection Timeout set to %us", conn_timeout);
103  * else
104  * sLog.Green(" DataBase Manager", "Connection Timeout set to %us", conn_timeout);
105 } else
106  sLog.Error(" DataBase Manager", "Connection Timeout Option Failed");
107 */
108  if (pReconnect) {
109  my_bool reconnect = true;
110  if (mysql_options(mysql, MYSQL_OPT_RECONNECT, (void*)&reconnect) == 0) // this will enable auto-reconnect...and render my Reconnect() worthless
111  sLog.Green(" DataBase Manager", "DataBase AutoReconnect Enabled");
112  else
113  sLog.Error(" DataBase Manager", "DataBase AutoReconnect Option Failed");
114  } else
115  sLog.Yellow(" DataBase Manager", "DataBase AutoReconnect Disabled");
116 
117  if (mysql_real_connect(mysql, pHost.c_str(), pUser.c_str(), pPassword.c_str(), pDatabase.c_str(), pPort, 0, flags) == nullptr) {
118  pStatus = Error;
119  *errnum = mysql_errno(mysql);
120  if (errbuf != nullptr)
121  snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(mysql), mysql_error(mysql));
122  DBerror err;
123  err.SetError(*errnum, errbuf);
124  sLog.Error( " ServerInit", "Unable to connect to the database: %s", err.c_str() );
125  return;
126  } else {
127  pStatus = Connected;
128  //mysql_get_socket();
129  sLog.Blue(" DataBase Manager", "DataBase Connected");
130  }
131 
132  // Setup character set we wish to use
133  if (mysql_set_character_set(mysql, "utf8") == 0)
134  sLog.Cyan(" DataBase Manager", "DataBase Character set: %s", mysql_character_set_name(mysql));
135 }
136 
138 {
139  _log(DATABASE__MESSAGE, "DBCore attempting to recover...");
140  Close();
141  mysql = mysql_init(nullptr);
142  uint errnum = 0;
143  char errbuf[1024];
144  errbuf[0] = 0;
145  MutexLock lock(MDatabase);
146  Connect(&errnum, errbuf);
147 
148  if (pStatus == Connected)
149  _log(DATABASE__MESSAGE, "DBCore recovery successful. Continuing.");
150 
151  return pStatus;
152 }
153 
154 void DBcore::Initialize(std::string host, std::string user, std::string password, std::string database, bool compress/*false*/,
155  bool SSL/*false*/, int16 port/*3306*/, bool socket/*false*/, bool reconnect/*false*/, bool profile/*false*/)
156 {
157  if (mysql == nullptr)
158  mysql = mysql_init(nullptr); // try again
159  if (mysql == nullptr) {
160  sLog.Error( " ServerInit", "Unable to connect to the database: mysql_init returned null");
161  return;
162  }
163  if (pStatus == Connected)
164  return;
165 
166  pHost = host;
167  pUser = user;
168  pPassword = password;
169  pDatabase = database;
170  pPort = port;
171  pSSL = SSL;
172  pCompress = compress;
173  pSocket = socket;
174  pReconnect = reconnect;
175  pProfile = profile;
176 
177  if (pHost.empty() or pUser.empty() or pPassword.empty() or pDatabase.empty()) {
178  sLog.Error( " ServerInit", "Unable to connect to the database: required connect field(s) are empty.");
179  return;
180  }
181 
182  uint errnum = 0;
183  char errbuf[1024];
184  errbuf[0] = 0;
185 
186  MutexLock lock(MDatabase);
187 
188  Connect(&errnum, errbuf);
189  sLog.Blue(" DataBase Manager", "DataBase Manager Initialized");
190 }
191 
193  pStatus = Closed;
194  mysql_close(mysql);
195  mysql_server_end();
196  mysql_thread_end(); // this is for each thread used for db connections
197 }
198 
199 /*
200 void DBcore::CallShutdown()
201 {
202  _log(DATABASE__MESSAGE, "DBCore recovery failed. Server restarting.");
203  std::vector<Client*> list;
204  sEntityList.GetClients(list);
205  for (auto cur : list)
206  cur->SendInfoModalMsg("DBCore lost connection and recovery failed. Server restarting.");
207  Sleep(4000); // pause running thread to allow players (if any) to view msg
208  sConsole.HaltServer(true);
209 } */
210 
211 // Sends the MySQL server a ping
213 {
214  // well, if it's locked, someone's using it. If someone's using it, it doesn't need a ping
215  if ( MDatabase.TryLock() ) {
216  mysql_ping(mysql);
217  MDatabase.Unlock();
218  }
219 }
220 
221 //query which returns a result (error is stored in the result if it occurs)
222 bool DBcore::RunQuery(DBQueryResult &into, const char *query_fmt, ...) {
223  MutexLock lock(MDatabase);
224 
225  char query[4096];
226  va_list vlist;
227  va_start(vlist, query_fmt);
228  int querylen = std::vsnprintf(query, 4096, query_fmt, vlist);
229  va_end(vlist);
230 
231  if (!DoQuery_locked(into.error, query, querylen))
232  return false;
233 
234  uint col_count = mysql_field_count(mysql);
235  if (col_count == 0) {
236  into.error.SetError(0xFFFF, "DBcore::RunQuery: No Result");
237  codelog(DATABASE__ERROR, "DBCore::RunQuery: %s failed because it did not return a result", query);
238  EvE::traceStack();
239  return false;
240  }
241 
242  into.SetResult(mysql_store_result(mysql), col_count);
243 
244  return true;
245 }
246 
247 //query which returns only error status
248 bool DBcore::RunQuery(DBerror &err, const char *query_fmt, ...) {
249  MutexLock lock(MDatabase);
250 
251  va_list args;
252  va_start(args, query_fmt);
253  char* query(nullptr);
254  int querylen = vasprintf(&query, query_fmt, args);
255  va_end(args);
256 
257  if (!DoQuery_locked(err, query, querylen)) {
258  free(query);
259  return false;
260  }
261 
262  free(query);
263  return true;
264 }
265 
266 //query which returns affected rows: (not used)
267 bool DBcore::RunQuery(DBerror &err, uint32 &affected_rows, const char *query_fmt, ...) {
268  MutexLock lock(MDatabase);
269 
270  va_list args;
271  va_start(args, query_fmt);
272  char* query(nullptr);
273  int querylen = vasprintf(&query, query_fmt, args);
274  va_end(args);
275 
276  if (!DoQuery_locked(err, query, querylen)) {
277  free(query);
278  return false;
279  }
280  free(query);
281 
282  affected_rows = (uint32)mysql_affected_rows(mysql);
283 
284  return true;
285 }
286 
287 //query which returns last insert ID:
288 bool DBcore::RunQueryLID(DBerror &err, uint32 &last_insert_id, const char *query_fmt, ...) {
289  MutexLock lock(MDatabase);
290 
291  va_list args;
292  va_start(args, query_fmt);
293  char* query(nullptr);
294  int querylen = vasprintf(&query, query_fmt, args);
295  va_end(args);
296 
297  if (!DoQuery_locked(err, query, querylen)) {
298  free(query);
299  return false;
300  }
301  free(query);
302 
303  last_insert_id = (uint32)mysql_insert_id(mysql);
304 
305  return true;
306 }
307 
308 bool DBcore::DoQuery_locked(DBerror &err, const char *query, int querylen, bool retry/*true*/)
309 {
310  double profileStartTime = GetTimeUSeconds();
311 
312  if (mysql == nullptr) {
313  pStatus = Error;
314  codelog(DATABASE__ERROR, "DBCore - mysql = null");
315  if (!Reconnect())
316  return false;
317  }
318 
319  if (pStatus != Connected) {
320  codelog(DATABASE__ERROR, "DBCore - Status != Connected");
321  _log(DATABASE__MESSAGE, "DBCore error detected. Look for error msgs in logs prior to this point.");
322  if (!Reconnect())
323  return false;
324  }
325 
326  if (is_log_enabled(DATABASE__QUERIES))
327  _log(DATABASE__QUERIES, "DBcore Query - %s", query);
328 
329  if (mysql_real_query(mysql, query, querylen)) {
330  uint num = mysql_errno(mysql);
331  if (num > 0)
332  pStatus = Error;
333 
334  // there are many correctable errors to check for
335  if ((num == CR_SERVER_LOST) or (num == CR_SERVER_GONE_ERROR)) {
336  _log(DATABASE__ERROR, "DBCore error - server lost or gone.");
337  if (!Reconnect())
338  return false;
339  }
340 
341  if ((pStatus == Connected) and retry)
342  return DoQuery_locked(err, query, querylen, retry);
343 
344  err.SetError(num, mysql_error(mysql));
345  codelog(DATABASE__ERROR, "DBCore Query - #%u in '%s': %s", err.GetErrNo(), query, err.c_str());
346  return false;
347  }
348 
349  err.ClearError();
350 
351  if (pProfile)
352  sProfiler.AddTime(9, GetTimeUSeconds() - profileStartTime);
353 
354  return true;
355 }
356 
357 
358 int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen)
359 {
360  return mysql_real_escape_string(mysql, tobuf, frombuf, fromlen);
361 }
362 
363 void DBcore::DoEscapeString(std::string &to, const std::string &from)
364 {
365  assert(mysql);
366  uint32 len = (uint32)from.length();
367  to.resize(len * 2); // make enough room
368  uint32 esc_len = mysql_real_escape_string(mysql, &to[0], from.c_str(), len);
369  to.resize(esc_len + 1); // optional.
370 }
371 
372 //look for things in the string which might cause SQL problems
373 bool DBcore::IsSafeString(const char *str) {
374  for(; *str != '\0'; str++) {
375  switch(*str) {
376  case '\'':
377  case '\\':
378  return false;
379  }
380  }
381  return true;
382 }
383 
384 bool DBcore::IsSafeString(std::string &s) {
385  for (uint i = 0; i < s.length(); ++i) {
386  switch (s[i]) {
387  case '\'':
388  case '\\':
389  return false;
390  }
391  }
392  return true;
393 }
394 
395 
396 /* this doesnt work right....look into later...
397 void DBcore::ReplaceSlash(const char *str) {
398  for(; *str != '\0'; str++) {
399  switch(*str) {
400  case '\'':
401  case '\\':
402  *str = '0';
403  }
404  }
405 } */
406 
407 /************************************************************************/
408 /* DBerror */
409 /************************************************************************/
411 {
412  ClearError();
413 }
414 
416 {
417  mErrStr.clear();
418 }
419 
420 void DBerror::SetError( uint err, const char* str )
421 {
422  mErrStr = str;
423  mErrNo = err;
424 }
425 
427 {
428  std::string errStr = "No Error";
429  mErrStr = errStr; // this gives mem leak. not sure why yet... test making string and clearing after set.
430  mErrNo = 0;
431  errStr.clear();
432 }
433 
434 /************************************************************************/
435 /* DBQueryResult */
436 /************************************************************************/
437 /* mysql to DBTYPE convention table */
438 /* treating all strings as wide isn't probably the best solution but it's
439  * the easiest one which preserves wide strings. */
441 {
442  DBTYPE_ERROR, //[ 0]MYSQL_TYPE_DECIMAL /* DECIMAL or NUMERIC field */
443  DBTYPE_I1, //[ 1]MYSQL_TYPE_TINY /* TINYINT field */
444  DBTYPE_I2, //[ 2]MYSQL_TYPE_SHORT /* SMALLINT field */
445  DBTYPE_I4, //[ 3]MYSQL_TYPE_LONG /* INTEGER field */
446  DBTYPE_R4, //[ 4]MYSQL_TYPE_FLOAT /* FLOAT field */
447  DBTYPE_R8, //[ 5]MYSQL_TYPE_DOUBLE /* DOUBLE or REAL field */
448  DBTYPE_ERROR, //[ 6]MYSQL_TYPE_NULL /* NULL-type field */
449  DBTYPE_FILETIME,//[ 7]MYSQL_TYPE_TIMESTAMP /* TIMESTAMP field */
450  DBTYPE_I8, //[ 8]MYSQL_TYPE_LONGLONG /* BIGINT field */
451  DBTYPE_I4, //[ 9]MYSQL_TYPE_INT24 /* MEDIUMINT field */
452  DBTYPE_ERROR, //[10]MYSQL_TYPE_DATE /* DATE field */
453  DBTYPE_ERROR, //[11]MYSQL_TYPE_TIME /* TIME field */
454  DBTYPE_ERROR, //[12]MYSQL_TYPE_DATETIME /* DATETIME field */
455  DBTYPE_ERROR, //[13]MYSQL_TYPE_YEAR /* YEAR field */
456  DBTYPE_ERROR, //[14]MYSQL_TYPE_NEWDATE /* ??? */
457  DBTYPE_ERROR, //[15]MYSQL_TYPE_VARCHAR /* ??? */
458  DBTYPE_BOOL, //[16]MYSQL_TYPE_BIT /* BIT field (MySQL 5.0.3 and up) */
459  DBTYPE_R8, //[17]MYSQL_TYPE_NEWDECIMAL=246 /* Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up) */
460  DBTYPE_ERROR, //[18]MYSQL_TYPE_ENUM=247 /* ENUM field */
461  DBTYPE_ERROR, //[19]MYSQL_TYPE_SET=248 /* SET field */
462  DBTYPE_WSTR, //[20]MYSQL_TYPE_TINY_BLOB=249 /* TINYBLOB or TINYTEXT field */
463  DBTYPE_WSTR, //[21]MYSQL_TYPE_MEDIUM_BLOB=250 /* MEDIUMBLOB or MEDIUMTEXT field */
464  DBTYPE_WSTR, //[22]MYSQL_TYPE_LONG_BLOB=251 /* LONGBLOB or LONGTEXT field */
465  DBTYPE_WSTR, //[23]MYSQL_TYPE_BLOB=252 /* BLOB or TEXT field */
466  DBTYPE_WSTR, //[24]MYSQL_TYPE_VAR_STRING=253 /* VARCHAR or VARBINARY field */
467  DBTYPE_STR, //[25]MYSQL_TYPE_STRING=254 /* CHAR or BINARY field */
468  DBTYPE_ERROR, //[26]MYSQL_TYPE_GEOMETRY=255 /* Spatial field */
469 };
470 
472 {
473  DBTYPE_ERROR, //[ 0]MYSQL_TYPE_DECIMAL /* DECIMAL or NUMERIC field */
474  DBTYPE_UI1, //[ 1]MYSQL_TYPE_TINY /* TINYINT field */
475  DBTYPE_UI2, //[ 2]MYSQL_TYPE_SHORT /* SMALLINT field */
476  DBTYPE_UI4, //[ 3]MYSQL_TYPE_LONG /* INTEGER field */
477  DBTYPE_R4, //[ 4]MYSQL_TYPE_FLOAT /* FLOAT field */
478  DBTYPE_R8, //[ 5]MYSQL_TYPE_DOUBLE /* DOUBLE or REAL field */
479  DBTYPE_ERROR, //[ 6]MYSQL_TYPE_NULL /* NULL-type field */
480  DBTYPE_FILETIME,//[ 7]MYSQL_TYPE_TIMESTAMP /* TIMESTAMP field */
481  DBTYPE_UI8, //[ 8]MYSQL_TYPE_LONGLONG /* BIGINT field */
482  DBTYPE_UI4, //[ 9]MYSQL_TYPE_INT24 /* MEDIUMINT field */
483  DBTYPE_ERROR, //[10]MYSQL_TYPE_DATE /* DATE field */
484  DBTYPE_ERROR, //[11]MYSQL_TYPE_TIME /* TIME field */
485  DBTYPE_ERROR, //[12]MYSQL_TYPE_DATETIME /* DATETIME field */
486  DBTYPE_ERROR, //[13]MYSQL_TYPE_YEAR /* YEAR field */
487  DBTYPE_ERROR, //[14]MYSQL_TYPE_NEWDATE /* ??? */
488  DBTYPE_ERROR, //[15]MYSQL_TYPE_VARCHAR /* ??? */
489  DBTYPE_BOOL, //[16]MYSQL_TYPE_BIT /* BIT field (MySQL 5.0.3 and up) */
490  DBTYPE_R8, //[17]MYSQL_TYPE_NEWDECIMAL=246 /* Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up) */
491  DBTYPE_ERROR, //[18]MYSQL_TYPE_ENUM=247 /* ENUM field */
492  DBTYPE_ERROR, //[19]MYSQL_TYPE_SET=248 /* SET field */
493  DBTYPE_WSTR, //[20]MYSQL_TYPE_TINY_BLOB=249 /* TINYBLOB or TINYTEXT field */
494  DBTYPE_WSTR, //[21]MYSQL_TYPE_MEDIUM_BLOB=250 /* MEDIUMBLOB or MEDIUMTEXT field */
495  DBTYPE_WSTR, //[22]MYSQL_TYPE_LONG_BLOB=251 /* LONGBLOB or LONGTEXT field */
496  DBTYPE_WSTR, //[23]MYSQL_TYPE_BLOB=252 /* BLOB or TEXT field */
497  DBTYPE_WSTR, //[24]MYSQL_TYPE_VAR_STRING=253 /* VARCHAR or VARBINARY field */
498  DBTYPE_STR, //[25]MYSQL_TYPE_STRING=254 /* CHAR or BINARY field */
499  DBTYPE_ERROR, //[26]MYSQL_TYPE_GEOMETRY=255 /* Spatial field */
500 };
501 
503 : mColumnCount(0),
504 mResult(nullptr),
505 mFields(nullptr)
506 {
507 }
508 
510 {
512 
513  if (mResult != nullptr)
514  mysql_free_result( mResult );
515 }
516 
518 {
519  mColumnCount = 0;
521 
522  if (mResult != nullptr)
523  mysql_free_result(mResult);
524 }
525 
527 {
528  return ((mFields[index]->flags & UNSIGNED_FLAG) == UNSIGNED_FLAG);
529 }
530 
531 bool DBQueryResult::IsBinary( uint32 index ) const
532 {
533  // According to MySQL C API Documentation, binary string
534  // fields like BLOB or VAR_BINARY have charset "63".
535  return (mFields[index]->charsetnr == 63);
536 }
537 
538 void DBQueryResult::SetResult( MYSQL_RES* res, uint32 colCount )
539 {
540  Reset();
541 
542  mResult = res;
543  mColumnCount = colCount;
544 
545  if (mResult != nullptr) {
546  mFields = new MYSQL_FIELD*[ mColumnCount ];
547  for( uint16 i = 0; i < mColumnCount; ++i )
548  mFields[ i ] = mysql_fetch_field( mResult );
549  }
550 }
551 
553 {
554  if (mResult == nullptr)
555  return false;
556 
557  MYSQL_ROW row = mysql_fetch_row( mResult );
558  if (row == nullptr)
559  return false;
560 
561  const ulong* lengths = mysql_fetch_lengths( mResult );
562  if (lengths == nullptr)
563  return false;
564 
565  into.SetData( this, row, lengths );
566  return true;
567 }
568 
569 const char* DBQueryResult::ColumnName( uint32 index ) const
570 {
571  if (index >= mColumnCount) {
572  _log(DATABASE__ERROR, "DBCore ColumnName: Column index %d exceeds number of columns in row (%s)\n", index, mColumnCount );
573  EvE::traceStack();
574  return "(ERROR)";
575  }
576 
577  return mFields[ index ]->name;
578 }
579 
581 {
582  if (index >= mColumnCount) {
583  _log(DATABASE__ERROR, "DBCore ColumnType: Column index %d exceeds number of columns in row (%s)\n", index, mColumnCount );
584  EvE::traceStack();
585  return DBTYPE_STR;
586  }
587 
588  uint16 columnType = mFields[ index ]->type;
589 
590  if ( columnType > MYSQL_TYPE_BIT )
591  columnType -= 229; //( MYSQL_TYPE_NEWDECIMAL - MYSQL_TYPE_BIT - 1 )
592 
593  DBTYPE result = ( IsUnsigned( index ) ? MYSQL_DBTYPE_TABLE_UNSIGNED : MYSQL_DBTYPE_TABLE_SIGNED )[ columnType ];
594 
595  // test for numeric type and set to signed 64b integer. may have to revise this to float/double depending on what's found.
596  if (result == DBTYPE_ERROR)
597  if (IS_NUM(columnType))
598  result = DBTYPE_I8;
599 
600  /* if result is (wide) binary string, set result to DBTYPE_BYTES. */
601  if (((DBTYPE_STR == result) or (DBTYPE_WSTR == result)) and IsBinary(index))
602  result = DBTYPE_BYTES;
603 
604  /* debug check */
605  assert( result != DBTYPE_ERROR);
606  return result;
607 }
608 
609 
611 : mRow( nullptr ),
612 mLengths( nullptr ),
613 mResult( nullptr )
614 {
615 }
616 
617 void DBResultRow::SetData( DBQueryResult* res, MYSQL_ROW& row, const ulong* lengths )
618 {
619  mRow = row;
620  mResult = res;
621  mLengths = lengths;
622 }
623 
625 {
626  if (index >= mResult->ColumnCount()) {
627  _log(DATABASE__ERROR, " DBCore::GetColumnLength: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
628  EvE::traceStack();
629  return 0;
630  }
631 
632  return mLengths[ index ];
633 }
634 
636 {
637  if (index >= mResult->ColumnCount()) {
638  _log(DATABASE__ERROR, " DBCore::GetInt: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
639  EvE::traceStack();
640  return 0;
641  }
642 
643  //use base 0 on the obscure chance that this is a string column with an 0x hex number in it.
644  return strtol( mRow[index], nullptr, 0 );
645 }
646 
647 bool DBResultRow::GetBool( uint32 index ) const
648 {
649  if (index >= mResult->ColumnCount()) {
650  _log(DATABASE__ERROR, " DBCore::GetBool: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
651  EvE::traceStack();
652  return false;
653  }
654 
655  return (mRow[index][0] != 0);
656 }
657 
659 {
660  if (index >= mResult->ColumnCount()) {
661  _log(DATABASE__ERROR, " DBCore::GetUInt: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
662  EvE::traceStack();
663  return 0;
664  }
665 
666  //use base 0 on the obscure chance that this is a string column with an 0x hex number in it.
667  return strtoul( mRow[index], nullptr, 0 );
668 }
669 
671 {
672  if (index >= mResult->ColumnCount()) {
673  _log(DATABASE__ERROR, " DBCore::GetInt64: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
674  EvE::traceStack();
675  return 0;
676  }
677 
678  //use base 0 on the obscure chance that this is a string column with an 0x hex number in it.
679  return strtoll( mRow[index], nullptr, 0 );
680 }
681 
682 float DBResultRow::GetFloat( uint32 index ) const
683 {
684  if (index >= mResult->ColumnCount()) {
685  _log(DATABASE__ERROR, " DBCore::GetFloat: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
686  EvE::traceStack();
687  return 0.0f;
688  }
689 
690  return strtof( mRow[index], nullptr );
691 }
692 
693 double DBResultRow::GetDouble( uint32 index ) const
694 {
695  if (index >= mResult->ColumnCount()) {
696  _log(DATABASE__ERROR, " DBCore::GetDouble: Column index %u exceeds number of columns in row (%u)", index, mResult->ColumnCount() );
697  EvE::traceStack();
698  return 0.0;
699  }
700 
701  return strtod( mRow[index], nullptr );
702 }
static const DBTYPE MYSQL_DBTYPE_TABLE_UNSIGNED[]
Definition: dbcore.h:93
#define vsnprintf
Definition: eve-compat.h:188
unsigned __int8 uint8
Definition: eve-compat.h:46
static const DBTYPE MYSQL_DBTYPE_TABLE_SIGNED[]
Definition: dbcore.h:92
bool RunQueryLID(DBerror &err, uint32 &last_insert_id, const char *query_fmt,...)
Definition: dbcore.cpp:288
uint32 ColumnLength(uint32 index) const
Definition: dbcore.cpp:624
bool IsUnsigned(uint32 index) const
Definition: dbcore.cpp:526
void SetData(DBQueryResult *res, MYSQL_ROW &row, const ulong *lengths)
Definition: dbcore.cpp:617
int16 pPort
Definition: dbcore.h:191
bool TryLock()
Attempts to lock the mutex.
Definition: Mutex.cpp:66
#define _log(type, fmt,...)
Definition: logsys.h:124
float GetFloat(uint32 index) const
Definition: dbcore.cpp:682
int32 GetInt(uint32 index) const
Definition: dbcore.cpp:635
void ClearError()
Definition: dbcore.cpp:426
uint32 GetUInt(uint32 index) const
Definition: dbcore.cpp:658
double GetDouble(uint32 index) const
Definition: dbcore.cpp:693
A lock for a Lockable object.
Definition: Lock.h:70
int32 DoEscapeString(char *tobuf, const char *frombuf, int32 fromlen)
Definition: dbcore.cpp:358
std::string pHost
Definition: dbcore.h:193
#define sProfiler
Definition: dbcore.cpp:39
#define strtoll
Definition: eve-compat.h:243
bool RunQuery(DBQueryResult &into, const char *query_fmt,...)
Definition: dbcore.cpp:222
std::string pUser
Definition: dbcore.h:194
void AddTime(uint8 key, double value)
Definition: Profiler.cpp:25
bool DoQuery_locked(DBerror &err, const char *query, int querylen, bool retry=true)
Definition: dbcore.cpp:308
MYSQL_ROW mRow
Definition: dbcore.h:126
signed __int32 int32
Definition: eve-compat.h:49
void SetError(uint err, const char *str)
Definition: dbcore.cpp:420
* args
bool GetRow(DBResultRow &into)
Definition: dbcore.cpp:552
Mutex MDatabase
Definition: dbcore.h:182
bool GetBool(uint32 index) const
Definition: dbcore.cpp:647
int vasprintf(char **strp, const char *fmt, va_list ap)
Definition: eve-compat.cpp:70
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
std::string pPassword
Definition: dbcore.h:195
std::string mErrStr
Definition: dbcore.h:56
double GetTimeUSeconds()
Definition: utils_time.cpp:116
DBerror()
Definition: dbcore.cpp:410
const char * c_str() const
Definition: dbcore.h:48
uint32 ColumnCount() const
Definition: dbcore.h:76
DBQueryResult * mResult
Definition: dbcore.h:129
#define codelog(type, fmt,...)
Definition: logsys.h:128
bool pCompress
Definition: dbcore.h:185
DBTYPE
Definition: dbtype.h:67
#define snprintf
Definition: eve-compat.h:184
void SetResult(MYSQL_RES *res, uint32 colCount)
Definition: dbcore.cpp:538
~DBerror()
Definition: dbcore.cpp:415
bool IsBinary(uint32 index) const
Definition: dbcore.cpp:531
uint32 mColumnCount
Definition: dbcore.h:88
bool pReconnect
Definition: dbcore.h:187
std::string pDatabase
Definition: dbcore.h:196
void Unlock()
Unlocks the mutex.
Definition: Mutex.cpp:75
const char * ColumnName(uint32 index) const
Definition: dbcore.cpp:569
unsigned __int32 uint32
Definition: eve-compat.h:50
bool Reconnect()
Definition: dbcore.cpp:137
float strtof(const char *nptr, char **endptr)
Definition: eve-compat.cpp:176
void Close()
Definition: dbcore.cpp:192
void SafeDeleteArray(T *&p)
Deletes and nullifies an array pointer.
Definition: SafeMem.h:97
bool pSocket
Definition: dbcore.h:188
void ping()
Definition: dbcore.cpp:212
uint mErrNo
Definition: dbcore.h:57
MYSQL_FIELD ** mFields
Definition: dbcore.h:90
DBerror error
Definition: dbcore.h:69
signed __int64 int64
Definition: eve-compat.h:51
static bool IsSafeString(const char *str)
Definition: dbcore.cpp:373
const ulong * mLengths
Definition: dbcore.h:127
void Initialize(std::string host, std::string user, std::string password, std::string database, bool compress=false, bool SSL=false, int16 port=3306, bool socket=false, bool reconnect=false, bool profile=false)
Definition: dbcore.cpp:154
signed __int16 int16
Definition: eve-compat.h:47
Template used for singleton classes.
Definition: Singleton.h:43
MYSQL_RES * mResult
Definition: dbcore.h:89
uint32 GetErrNo() const
Definition: dbcore.h:45
void traceStack(void)
Definition: misc.cpp:169
bool pSSL
Definition: dbcore.h:189
eStatus pStatus
Definition: dbcore.h:183
typeID Spawn an NPC with the specified type text Search for items matching the specified query() type() key(value)-Send an OnRemoteMessage" ) COMMAND( setbpattr
void Connect(uint *errnum=0, char *errbuf=0)
Definition: dbcore.cpp:60
bool pProfile
Definition: dbcore.h:186
int64 GetInt64(uint32 index) const
Definition: dbcore.cpp:670
unsigned __int16 uint16
Definition: eve-compat.h:48
MYSQL * mysql
Definition: dbcore.h:181
DBTYPE ColumnType(uint32 index) const
Definition: dbcore.cpp:580
Definition: dbcore.h:39
void Reset()
Definition: dbcore.cpp:517
DBcore()
Definition: dbcore.cpp:49