OregonCore  revision 3611e8a-git
Your Favourite TBC server
Database.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "DatabaseEnv.h"
19 #include "Config/Config.h"
20 
21 #include "Common.h"
22 #include "../../game/UpdateFields.h"
23 
24 #include "Util.h"
25 #include "Platform/Define.h"
26 #include "Platform/CompilerDefs.h"
27 #include "Threading.h"
29 #include "Database/SqlOperations.h"
30 #include "Timer.h"
31 
32 #include <ctime>
33 #include <iostream>
34 #include <fstream>
35 #if PLATFORM == PLATFORM_UNIX
36 #include <sys/file.h>
37 #endif
38 
39 static const my_bool my_true = 1;
40 
41 size_t Database::db_count = 0;
42 
43 Database::Database() : mMysql(NULL), m_connected(false)
44 {
45  // before first connection
46  if (db_count++ == 0)
47  {
48  // Mysql Library Init
49  mysql_library_init(-1, NULL, NULL);
50 
51  if (!mysql_thread_safe())
52  sLog.outFatal("FATAL ERROR: Used MySQL library isn't thread-safe.");
53  }
54 }
55 
57 {
58  if (m_delayThread)
60 
61  for (PreparedStatementsMap::iterator it = m_preparedStatements.begin(); it != m_preparedStatements.end(); ++it)
62  {
63  mysql_stmt_close(it->second->stmt);
64  delete it->second;
65  }
66 
67  if (mMysql)
68  mysql_close(mMysql);
69 
70  // Free Mysql library pointers for last ~DB
71  if (--db_count == 0)
72  mysql_library_end();
73 }
74 
75 bool Database::Initialize(const char* infoString)
76 {
77  // Enable logging of SQL commands (usally only GM commands)
78  // (See method: PExecuteLog)
79  m_logSQL = sConfig.GetBoolDefault("LogSQL", false);
80  m_logsDir = sConfig.GetStringDefault("LogsDir", "");
81  if (!m_logsDir.empty())
82  {
83  if ((m_logsDir.at(m_logsDir.length() - 1) != '/') && (m_logsDir.at(m_logsDir.length() - 1) != '\\'))
84  m_logsDir.append("/");
85  }
86 
87  tranThread = NULL;
88  MYSQL* mysqlInit;
89  mysqlInit = mysql_init(NULL);
90  if (!mysqlInit)
91  {
92  sLog.outError("Could not initialize Mysql connection");
93  return false;
94  }
95 
97 
98  Tokens tokens = StrSplit(infoString, ";");
99 
100  Tokens::iterator iter;
101 
102  std::string host, port_or_socket, user, password, database;
103  int port;
104  char const* unix_socket;
105 
106  iter = tokens.begin();
107 
108  if (iter != tokens.end())
109  host = *iter++;
110  if (iter != tokens.end())
111  port_or_socket = *iter++;
112  if (iter != tokens.end())
113  user = *iter++;
114  if (iter != tokens.end())
115  password = *iter++;
116  if (iter != tokens.end())
117  database = *iter++;
118 
119  mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
120  #ifdef _WIN32
121  if (host == ".") // named pipe use option (Windows)
122  {
123  unsigned int opt = MYSQL_PROTOCOL_PIPE;
124  mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
125  port = 0;
126  unix_socket = 0;
127  }
128  else // generic case
129  {
130  port = atoi(port_or_socket.c_str());
131  unix_socket = 0;
132  }
133  #else
134  if (host == ".") // socket use option (Unix/Linux)
135  {
136  unsigned int opt = MYSQL_PROTOCOL_SOCKET;
137  mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
138  host = "localhost";
139  port = 0;
140  unix_socket = port_or_socket.c_str();
141  }
142  else // generic case
143  {
144  port = atoi(port_or_socket.c_str());
145  unix_socket = 0;
146  }
147  #endif
148 
149  mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(),
150  password.c_str(), database.c_str(), port, unix_socket, 0);
151 
152  if (mMysql)
153  {
154  sLog.outDetail("Connected to MySQL database at %s", host.c_str());
155  sLog.outDebug("MySQL client library: %s", mysql_get_client_info());
156  sLog.outDebug("MySQL server ver: %s ", mysql_get_server_info( mMysql));
157 
158  if (!mysql_autocommit(mMysql, 1))
159  sLog.outDebug("AUTOCOMMIT SUCCESSFULLY SET TO 1");
160  else
161  sLog.outDebug("AUTOCOMMIT NOT SET TO 1");
162 
163  // set connection properties to UTF8 to properly handle locales for different
164  // server configs - core sends data in UTF8, so MySQL must expect UTF8 too
165  // mysql_set_character_set is just like SET NAMES, but also sets encoding in client library
166  // which enforces mysql_real_escape_string to be safe
167  mysql_set_character_set(mMysql, "utf8");
168  Execute("SET CHARACTER SET `utf8`");
169 
170  #if MYSQL_VERSION_ID >= 50003
171  my_bool my_true = (my_bool)1;
172  if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true))
173  sLog.outDebug("Failed to turn on MYSQL_OPT_RECONNECT.");
174  else
175  sLog.outDebug("Successfully turned on MYSQL_OPT_RECONNECT.");
176  #else
177 #warning "Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem."
178  #endif
179 
180  m_connected = true;
181  return true;
182  }
183  else
184  {
185  sLog.outError("Could not connect to MySQL database at %s: %s", host.c_str(), mysql_error(mysqlInit));
186  mysql_close(mysqlInit);
187  return false;
188  }
189 }
190 
192 {
193  mysql_thread_init();
194 }
195 
197 {
198  mysql_thread_end();
199 }
200 
201 void Database::escape_string(std::string& str)
202 {
203  if (str.empty())
204  return;
205 
206  char* buf = new char[str.size() * 2 + 1];
207  escape_string(buf, str.c_str(), str.size());
208  str = buf;
209  delete[] buf;
210 }
211 
212 unsigned long Database::escape_string(char* to, const char* from, unsigned long length)
213 {
214  if (!mMysql || !to || !from || !length)
215  return 0;
216 
217  return (mysql_real_escape_string(mMysql, to, from, length));
218 }
219 
220 
221 bool Database::PExecuteLog(const char* format, ...)
222 {
223  if (!format)
224  return false;
225 
226  va_list ap;
227  char szQuery [MAX_QUERY_LEN];
228  va_start(ap, format);
229  int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
230  va_end(ap);
231 
232  if (res == -1)
233  {
234  sLog.outError("SQL Query truncated (and not execute) for format: %s", format);
235  return false;
236  }
237 
238  sLog.outSQL("%s", szQuery);
239  return Execute(szQuery);
240 }
241 
242 bool Database::PreparedExecuteLog(const char* sql, const char* format, ...)
243 {
244  if (format)
245  {
246  PreparedValues values(strlen(format));
247 
248  va_list ap;
249  va_start(ap, format);
250  _ConvertValistToPreparedValues(ap, values, format);
251  va_end(ap);
252 
253  return PreparedExecuteLog(sql, values);
254  }
255 
256  PreparedValues values(0);
257  return PreparedExecuteLog(sql, values);
258 }
259 
260 bool Database::PreparedExecuteLog(const char* sql, PreparedValues& values)
261 {
262  std::string query(sql);
263  std::stringstream ss;
264  size_t pos = 0, i = 0;
265 
266  while ((pos = query.find("?", pos)) != std::string::npos)
267  {
268  switch (values[i].type)
269  {
270  case ARG_TYPE_STRING:
271  case ARG_TYPE_STRING_ALT:
272  {
273  std::string safeStr(values[i].data.string);
274  escape_string(safeStr);
275  safeStr += '\'';
276  safeStr.insert(0, "'");
277  query.replace(pos, 1, safeStr);
278  }
279  break;
280  case ARG_TYPE_BINARY:
281  case ARG_TYPE_BINARY_ALT:
282  ss << "0x" << std::hex;
283  for (size_t j = 0; j < values[i].data.length; ++j)
284  ss << *reinterpret_cast<const char*>(values[i].data.binary);
285  ss.clear();
286  break;
287  case ARG_TYPE_FLOAT:
288  ss << values[i].data.float_;
289  query.replace(pos, 1, ss.str());
290  ss.clear();
291  break;
292  case ARG_TYPE_DOUBLE:
293  ss << values[i].data.double_;
294  query.replace(pos, 1, ss.str());
295  ss.clear();
296  break;
297  case ARG_TYPE_NUMBER:
298  case ARG_TYPE_NUMBER_ALT:
299  ss << values[i].data.number;
300  query.replace(pos, 1, ss.str());
301  ss.clear();
302  break;
304  ss << values[i].data.unsignedNumber;
305  query.replace(pos, 1, ss.str());
306  ss.clear();
307  break;
310  ss << values[i].data.largeNumber;
311  query.replace(pos, 1, ss.str());
312  ss.clear();
313  break;
315  ss << values[i].data.unsignedLargeNumber;
316  query.replace(pos, 1, ss.str());
317  ss.clear();
318  break;
319  }
320 
321  ++i;
322  }
323 
324  sLog.outSQL("%s", query.c_str());
325 
326  if (values.size())
327  return PreparedExecute(sql, values);
328  else
329  return PreparedExecute(sql);
330 }
331 
333 {
335 }
336 
337 bool Database::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount)
338 {
339  if (!mMysql)
340  return 0;
341 
342  {
343  // guarded block for thread-safe mySQL request
344  ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
345  #ifdef OREGON_DEBUG
346  uint32 _s = getMSTime();
347  #endif
348  if (mysql_query(mMysql, sql))
349  {
350  sLog.outErrorDb("SQL: %s", sql);
351  sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql));
352  return false;
353  }
354  else
355  {
356  #ifdef OREGON_DEBUG
357  // prevent recursive death
358  unsigned long oldMask = sLog.GetDBLogMask();
359  sLog.SetDBLogMask(oldMask & ~(1 << LOG_TYPE_DEBUG));
360  sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
361  sLog.SetDBLogMask(oldMask);
362  #endif
363  }
364 
365  *pResult = mysql_store_result(mMysql);
366  *pRowCount = mysql_affected_rows(mMysql);
367  *pFieldCount = mysql_field_count(mMysql);
368  }
369 
370  if (!*pResult )
371  return false;
372 
373  if (!*pRowCount)
374  {
375  mysql_free_result(*pResult);
376  return false;
377  }
378 
379  *pFields = mysql_fetch_fields(*pResult);
380  return true;
381 }
382 
384 {
385  MYSQL_RES* result = NULL;
386  MYSQL_FIELD* fields = NULL;
387  uint64 rowCount = 0;
388  uint32 fieldCount = 0;
389 
390  if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
391  return QueryResult_AutoPtr(NULL);
392 
393  QueryResult* queryResult = new QueryResult(result, fields, rowCount, fieldCount);
394 
395  queryResult->NextRow();
396 
397  return QueryResult_AutoPtr(queryResult);
398 }
399 
400 QueryResult_AutoPtr Database::PQuery(const char* format, ...)
401 {
402  if (!format)
403  return QueryResult_AutoPtr(NULL);
404 
405  va_list ap;
406  char szQuery [MAX_QUERY_LEN];
407  va_start(ap, format);
408  int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
409  va_end(ap);
410 
411  if (res == -1)
412  {
413  sLog.outError("SQL Query truncated (and not execute) for format: %s", format);
414  return QueryResult_AutoPtr(NULL);
415  }
416 
417  return Query(szQuery);
418 }
419 
420 bool Database::Execute(const char* sql)
421 {
422  if (!mMysql)
423  return false;
424 
425  // don't use queued execution if it has not been initialized
426  if (!m_threadBody)
427  return DirectExecute(sql);
428 
429  nMutex.acquire();
430  tranThread = ACE_Based::Thread::current(); // owner of this transaction
431  TransactionQueues::iterator i = m_tranQueues.find(tranThread);
432  if (i != m_tranQueues.end() && i->second != NULL)
433  i->second->DelayExecute(sql); // Statement for transaction
434  else
435  m_threadBody->Delay(new SqlStatement(sql)); // Simple sql statement
436 
437  nMutex.release();
438  return true;
439 }
440 
441 bool Database::PExecute(const char* format, ...)
442 {
443  if (!format)
444  return false;
445 
446  va_list ap;
447  char szQuery [MAX_QUERY_LEN];
448  va_start(ap, format);
449  int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
450  va_end(ap);
451 
452  if (res == -1)
453  {
454  sLog.outError("SQL Query truncated (and not execute) for format: %s", format);
455  return false;
456  }
457 
458  return Execute(szQuery);
459 }
460 
461 bool Database::DirectExecute(bool lock, const char* sql)
462 {
463  if (!mMysql)
464  return false;
465 
466  if (lock)
467  mMutex.acquire();
468 
469  #ifdef OREGON_DEBUG
470  uint32 _s = getMSTime();
471  #endif
472  if (mysql_query(mMysql, sql))
473  {
474  sLog.outErrorDb("SQL: %s", sql);
475  sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql));
476  if (lock)
477  mMutex.release();
478  return false;
479  }
480  else
481  {
482  #ifdef OREGON_DEBUG
483  // prevent recursive death
484  unsigned long oldMask = sLog.GetDBLogMask();
485  sLog.SetDBLogMask(oldMask & ~(1 << LOG_TYPE_DEBUG));
486  sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
487  sLog.SetDBLogMask(oldMask);
488  #endif
489  }
490 
491  if (lock)
492  mMutex.release();
493 
494  return true;
495 }
496 
497 bool Database::DirectPExecute(const char* format, ...)
498 {
499  if (!format)
500  return false;
501 
502  va_list ap;
503  char szQuery [MAX_QUERY_LEN];
504  va_start(ap, format);
505  int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
506  va_end(ap);
507 
508  if (res == -1)
509  {
510  sLog.outError("SQL Query truncated (and not execute) for format: %s", format);
511  return false;
512  }
513 
514  return DirectExecute(szQuery);
515 }
516 
517 bool Database::_TransactionCmd(const char* sql)
518 {
519  if (mysql_query(mMysql, sql))
520  {
521  sLog.outError("SQL: %s", sql);
522  sLog.outError("SQL ERROR: %s", mysql_error(mMysql));
523  return false;
524  }
525  #if OREGON_DEBUG
526  else
527  DEBUG_LOG("SQL: %s", sql);
528  #endif
529 
530  return true;
531 }
532 
534 {
535  if (!mMysql)
536  return false;
537 
538  nMutex.acquire();
539  tranThread = ACE_Based::Thread::current(); // owner of this transaction
540  TransactionQueues::iterator i = m_tranQueues.find(tranThread);
541  if (i != m_tranQueues.end() && i->second != NULL)
542  // If for thread exists queue and also contains transaction
543  // delete that transaction (not allow trans in trans)
544  delete i->second;
545 
547  nMutex.release();
548  return true;
549 }
550 
552 {
553  if (!mMysql)
554  return false;
555 
556  bool _res = false;
557 
558  nMutex.acquire();
560  TransactionQueues::iterator i = m_tranQueues.find(tranThread);
561  if (i != m_tranQueues.end() && i->second != NULL)
562  {
563  m_threadBody->Delay(i->second);
564  m_tranQueues.erase(i);
565  _res = true;
566  }
567  nMutex.release();
568  return _res;
569 }
570 
572 {
573  if (!mMysql)
574  return false;
575 
576  nMutex.acquire();
578  TransactionQueues::iterator i = m_tranQueues.find(tranThread);
579  if (i != m_tranQueues.end() && i->second != NULL)
580  {
581  delete i->second;
582  i->second = NULL;
583  m_tranQueues.erase(i);
584  }
585  nMutex.release();
586  return true;
587 }
588 
594 {
596 
597  ACE_Guard<ACE_Thread_Mutex> connection_guard(mMutex);
598  ACE_Guard<ACE_Thread_Mutex> transaction_guard(transaction->mutex);
599 
600  if (transaction->queue.empty())
601  return true;
602 
603  if (mysql_autocommit(mMysql, 0))
604  return false;
605 
606  if (mysql_real_query(mMysql, "START TRANSACTION", sizeof("START TRANSACTION")-1))
607  return false;
608 
609  while (!transaction->queue.empty())
610  {
611  item = transaction->queue.front();
612 
613  bool ok = (item.isStmt) ? _ExecutePreparedStatement(item.stmt, item.values, NULL, false) : DirectExecute(false, item.sql);
614  if (!ok)
615  {
616  transaction->queue.pop();
617  mysql_rollback(mMysql);
618  mysql_autocommit(mMysql, 1);
619  return false;
620  }
621 
622  if (!item.isStmt)
623  free (item.sql);
624  else
625  delete item.values;
626  transaction->queue.pop();
627  }
628 
629  if (mysql_commit(mMysql))
630  return false;
631 
632  mysql_autocommit(mMysql, 1);
633  return true;
634 }
635 
637 {
638  assert(!m_delayThread);
639 
640  //New delay thread for delay execute
641  m_threadBody = new SqlDelayThread(this); // will deleted at m_delayThread delete
643 }
644 
646 {
647  if (!m_threadBody || !m_delayThread)
648  return;
649 
650  m_threadBody->Stop(); //Stop event
651  m_delayThread->wait(); //Wait for flush to DB
652  delete m_delayThread; //This also deletes m_threadBody
653  m_delayThread = NULL;
654  m_threadBody = NULL;
655 }
656 
657 bool Database::ExecuteFile(const char* file)
658 {
659  if (!mMysql)
660  return false;
661 
662  if (mysql_set_server_option(mMysql, MYSQL_OPTION_MULTI_STATEMENTS_ON))
663  {
664  sLog.outErrorDb("Cannot turn multi-statements on: %s", mysql_error(mMysql));
665  return false;
666  }
667 
668  mysql_autocommit(mMysql, 0);
669  if (mysql_real_query(mMysql, "START TRANSACTION", sizeof("START TRANSACTION")-1))
670  {
671  sLog.outErrorDb("Couldn't start transaction for db update file: %s", file);
672  return false;
673  }
674 
675  bool in_transaction = true;
676  bool success = false;
677 
678  if (FILE* fp = ACE_OS::fopen(file, "rb"))
679  {
680  #if PLATFORM == PLATFORM_UNIX
681  flock(fileno(fp), LOCK_SH);
682  #endif
683  //------
684 
685  struct stat info;
686  fstat(fileno(fp), &info);
687 
688  // if less than 1MB allocate on stack, else on heap
689  char* contents = (info.st_size > 1024*1024) ? new char[info.st_size] : (char*) alloca(info.st_size);
690 
691  if (ACE_OS::fread(contents, info.st_size, 1, fp) == 1)
692  {
693  if (mysql_real_query(mMysql, contents, info.st_size))
694  {
695  sLog.outErrorDb("Cannot execute file %s, size: %lu: %s", file, info.st_size, mysql_error(mMysql));
696  }
697  else
698  {
699  do
700  {
701  if (mysql_field_count(mMysql))
702  if (MYSQL_RES* result = mysql_use_result(mMysql))
703  mysql_free_result(result);
704  }
705  while (0 == mysql_next_result(mMysql));
706 
707  // check whether the last mysql_next_result ended with an error
708  if (*mysql_error(mMysql))
709  {
710  success = false;
711  sLog.outErrorDb("Cannot execute file %s, size: %lu: %s", file, info.st_size, mysql_error(mMysql));
712  if (mysql_rollback(mMysql))
713  sLog.outErrorDb("ExecuteFile(): Rollback ended with an error!");
714  else
715  in_transaction = false;
716  }
717  else
718  {
719  if (mysql_commit(mMysql))
720  sLog.outErrorDb("mysql_commit() failed. Update %s will not be applied!", file);
721  else
722  in_transaction = false;
723  success = true;
724  }
725  }
726  }
727  else
728  {
729  sLog.outErrorDb("Couldn't read file %s, size: %lu", file, info.st_size);
730  return false;
731  }
732 
733  // if allocated on heap, free memory
734  if (info.st_size > 1024*1024)
735  delete [] contents;
736 
737  //------
738  #if PLATFORM == PLATFORM_UNIX
739  flock(fileno(fp), LOCK_UN);
740  #endif
741  ACE_OS::fclose(fp);
742  }
743 
744  mysql_set_server_option(mMysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
745  mysql_autocommit(mMysql, 1);
746  if (in_transaction)
747  mysql_rollback(mMysql);
748  return success;
749 }
750 
751 PreparedStatement* Database::_GetOrMakePreparedStatement(const char* query, const char* format, PreparedValues* values)
752 {
753  ACE_Guard<ACE_Thread_Mutex> guardian(pMutex);
754  PreparedStatementsMap::iterator it = m_preparedStatements.find(query);
755 
756  if (it != m_preparedStatements.end())
757  return it->second; // found, ok
758 
759  MYSQL_STMT* stmt = mysql_stmt_init(mMysql);
760 
761  if (!stmt)
762  {
763  sLog.outError("mysql_stmt_init() failed: %s", mysql_error(mMysql));
764  return 0;
765  }
766 
767  {
768  // set prefetch rows to maximum, thus making results buffered
769  unsigned long rows = (unsigned long) -1;
770  if (mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS, &rows))
771  sLog.outError("mysql_stmt_attr_set() failed.");
772 
773  if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &my_true))
774  sLog.outError("mysql_stmt_attr_set() failed.");
775  }
776 
777  PreparedStatement* prepStmt = new PreparedStatement;
778  prepStmt->stmt = stmt;
779 
780  if (format)
781  {
782  prepStmt->types = format;
783 
784  for (const char* it = format; *it != '\0'; ++it)
785  {
786  switch (*it)
787  {
788  case ARG_TYPE_STRING:
789  case ARG_TYPE_STRING_ALT:
790  case ARG_TYPE_NUMBER:
791  case ARG_TYPE_NUMBER_ALT:
796  case ARG_TYPE_FLOAT:
797  case ARG_TYPE_DOUBLE:
798  case ARG_TYPE_BINARY:
799  case ARG_TYPE_BINARY_ALT:
800  break;
801  default:
802  sLog.outError("Unknown format type '%c' for prepared statement (%s)", *it, query);
803  delete prepStmt;
804  return 0;
805  }
806  }
807  }
808  else if (values)
809  {
810  for (size_t i = 0; i < values->m_values.size(); ++i)
811  prepStmt->types.append(1, static_cast<char>(values->m_values[i].type));
812  }
813 
814  {
815  if (mysql_stmt_prepare(stmt, query, strlen(query)))
816  {
817  sLog.outErrorDb("mysql_stmt_prepare() failed: %s, sql: %s", mysql_stmt_error(stmt), query);
818  delete prepStmt;
819  return 0;
820  }
821  }
822 
823  return m_preparedStatements.insert(std::pair<std::string, PreparedStatement*>(query, prepStmt)).first->second;
824 }
825 
826 bool Database::_ExecutePreparedStatement(PreparedStatement* ps, PreparedValues* values, va_list* args, bool resultset)
827 {
828  size_t paramCount = mysql_stmt_param_count(ps->stmt);
829  MYSQL_BIND* binding = NULL;
830  bool myValues = false;
831 
832  if (paramCount)
833  {
834  if (args)
835  {
836  if (paramCount != ps->types.size())
837  {
838  sLog.outErrorDb("Count of parameters passed doesn't equal to count of parameters in prepared statement!");
839  delete[] binding;
840  return false;
841  }
842 
843  if (!values)
844  {
845  values = new PreparedValues(paramCount);
846  myValues = true;
847  }
848 
849  _ConvertValistToPreparedValues(*args, *values, ps->types.c_str());
850  }
851  else
852  {
853  ASSERT (values);
854 
855  if (paramCount != values->m_values.size())
856  {
857  sLog.outErrorDb("Count of parameters passed doesn't equal to count of parameters in prepared statement!");
858  delete[] binding;
859  return false;
860  }
861  }
862 
863  binding = new MYSQL_BIND[paramCount];
864  memset(binding, 0, sizeof(MYSQL_BIND)*paramCount);
865 
866  for (size_t i = 0; i < paramCount; ++i)
867  {
868  switch ((*values)[i].type)
869  {
870  case ARG_TYPE_STRING:
871  case ARG_TYPE_STRING_ALT:
872  binding[i].buffer_type = MYSQL_TYPE_STRING;
873  binding[i].buffer = const_cast<char*> ((*values)[i].data.string);
874  binding[i].buffer_length = (*values)[i].data.length;
875  break;
876  case ARG_TYPE_BINARY:
877  case ARG_TYPE_BINARY_ALT:
878  binding[i].buffer_type = MYSQL_TYPE_BLOB;
879  binding[i].buffer = const_cast<void*> ((*values)[i].data.binary);
880  binding[i].buffer_length = (*values)[i].data.length;
881  break;
883  binding[i].is_unsigned = my_true;
884  /* FALLTHROUGH */
885  case ARG_TYPE_NUMBER:
886  case ARG_TYPE_NUMBER_ALT:
887  binding[i].buffer_type = MYSQL_TYPE_LONG;
888  binding[i].buffer = &(*values)[i].data.number;
889  binding[i].buffer_length = sizeof((*values)[i].data.number);
890  break;
892  binding[i].is_unsigned = my_true;
893  /* FALLTHROUGH */
896  binding[i].buffer_type = MYSQL_TYPE_LONGLONG;
897  binding[i].buffer = &(*values)[i].data.largeNumber;
898  binding[i].buffer_length = sizeof(&(*values)[i].data.largeNumber);
899  break;
900  case ARG_TYPE_FLOAT:
901  binding[i].buffer_type = MYSQL_TYPE_FLOAT;
902  binding[i].buffer = &(*values)[i].data.float_;
903  binding[i].buffer_length = sizeof(&(*values)[i].data.float_);
904  break;
905  case ARG_TYPE_DOUBLE:
906  binding[i].buffer_type = MYSQL_TYPE_DOUBLE;
907  binding[i].buffer = &(*values)[i].data.double_;
908  binding[i].buffer_length = sizeof((*values)[i].data.double_);
909  break;
910  }
911  }
912 
913  if (mysql_stmt_bind_param(ps->stmt, binding))
914  {
915  sLog.outError("mysql_stmt_bind_param() failed: %s", mysql_stmt_error(ps->stmt));
916  delete[] binding;
917  if (myValues)
918  delete values;
919  return false;
920  }
921  }
922 
923  if (mysql_stmt_execute(ps->stmt))
924  {
925  sLog.outError("mysql_stmt_execute() failed: %s", mysql_stmt_error(ps->stmt));
926  delete[] binding;
927  if (myValues)
928  delete values;
929  return false;
930  }
931 
932  if (myValues)
933  delete values;
934 
935  // this is safe, even if there's no result
936  mysql_stmt_store_result(ps->stmt);
937 
938  if (resultset)
939  {
940  if (!mysql_stmt_field_count(ps->stmt))
941  {
942  delete[] binding;
943  return false;
944  }
945 
946  if (!mysql_stmt_num_rows(ps->stmt))
947  {
948  mysql_stmt_free_result(ps->stmt);
949  delete[] binding;
950  return false;
951  }
952  }
953  else
954  {
955  if (mysql_stmt_field_count(ps->stmt))
956  mysql_stmt_free_result(ps->stmt);
957  mysql_stmt_reset(ps->stmt);
958  }
959 
960  delete[] binding;
961  return true;
962 }
963 
964 void Database::_ConvertValistToPreparedValues(va_list args, PreparedValues& values, const char* fmt)
965 {
966  for (const char* i = fmt; *i != '\0'; ++i)
967  {
968  switch (*i)
969  {
970  case ARG_TYPE_STRING:
971  case ARG_TYPE_STRING_ALT:
972  values << va_arg(args, const char*);
973  break;
974  case ARG_TYPE_BINARY:
975  case ARG_TYPE_BINARY_ALT:
976  {
977  size_t size = va_arg(args, size_t);
978  const void* data = va_arg(args, const void*);
979  values << std::pair<const void*, size_t>(data, size);
980  }
981  break;
983  values << va_arg(args, uint32);
984  break;
985  case ARG_TYPE_NUMBER:
986  case ARG_TYPE_NUMBER_ALT:
987  values << va_arg(args, int32);
988  break;
990  values << va_arg(args, uint64);
991  break;
994  values << va_arg(args, int64);
995  break;
996  case ARG_TYPE_FLOAT:
997  // passed floats are promoted to doubles
998  values << (float) va_arg(args, double);
999  break;
1000  case ARG_TYPE_DOUBLE:
1001  values << va_arg(args, double);
1002  break;
1003  }
1004  }
1005 }
1006 
1024 PreparedQueryResult_AutoPtr Database::PreparedQuery(const char* sql, const char* format, ...)
1025 {
1026  ACE_Guard<ACE_Thread_Mutex> guardian(mMutex);
1027  PreparedStatement* stmt = _GetOrMakePreparedStatement(sql, format, NULL);
1028 
1029  if (!stmt)
1030  return PreparedQueryResult_AutoPtr(NULL);
1031 
1032  va_list ap;
1033  va_start(ap, format);
1034 
1035  if (!_ExecutePreparedStatement(stmt, NULL, &ap, true))
1036  {
1037  va_end(ap);
1038  return PreparedQueryResult_AutoPtr(NULL);
1039  }
1040 
1041  va_end(ap);
1042 
1044 }
1045 
1047 {
1048  ACE_Guard<ACE_Thread_Mutex> guardian(mMutex);
1049  PreparedStatement* stmt = _GetOrMakePreparedStatement(sql, NULL, &values);
1050 
1051  if (!stmt)
1052  return PreparedQueryResult_AutoPtr(NULL);
1053 
1054  if (!_ExecutePreparedStatement(stmt, &values, NULL, true))
1055  return PreparedQueryResult_AutoPtr(NULL);
1056 
1058 }
1059 
1060 bool Database::DirectExecute(PreparedStatement* stmt, PreparedValues& values, va_list* args)
1061 {
1062  ACE_Guard<ACE_Thread_Mutex> guardian(mMutex);
1063 
1064  return _ExecutePreparedStatement(stmt, &values, args, false);
1065 }
1066 
1067 
1071 bool Database::PreparedExecute(const char* sql, const char* format, ...)
1072 {
1073  if (!mMysql)
1074  return false;
1075 
1076  PreparedValues values(strlen(format));
1077 
1078  va_list args;
1079  va_start(args, format);
1080  _ConvertValistToPreparedValues(args, values, format);
1081  va_end(args);
1082 
1083  PreparedStatement* stmt = _GetOrMakePreparedStatement(sql, NULL, &values);
1084 
1085  // don't use queued execution if it has not been initialized
1086  if (!m_threadBody)
1087  return DirectExecute(stmt, values, NULL);
1088 
1089  nMutex.acquire();
1090  tranThread = ACE_Based::Thread::current(); // owner of this transaction
1091  TransactionQueues::iterator i = m_tranQueues.find(tranThread);
1092  if (i != m_tranQueues.end() && i->second != NULL)
1093  i->second->DelayExecute(stmt, values); // Statement for transaction
1094  else
1095  m_threadBody->Delay(new SqlPreparedStatement(stmt, values)); // Simple sql statement
1096 
1097  nMutex.release();
1098  return true;
1099 }
1100 
1104 bool Database::PreparedExecute(const char* sql, PreparedValues& values)
1105 {
1106  if (!mMysql)
1107  return false;
1108 
1109  PreparedStatement* stmt = _GetOrMakePreparedStatement(sql, NULL, &values);
1110 
1111  // don't use queued execution if it has not been initialized
1112  if (!m_threadBody)
1113  return DirectExecute(stmt, values, NULL);
1114 
1115  nMutex.acquire();
1116  tranThread = ACE_Based::Thread::current(); // owner of this transaction
1117  TransactionQueues::iterator i = m_tranQueues.find(tranThread);
1118  if (i != m_tranQueues.end() && i->second != NULL)
1119  i->second->DelayExecute(stmt, values); // Statement for transaction
1120  else
1121  m_threadBody->Delay(new SqlPreparedStatement(stmt, values)); // Simple sql statement
1122 
1123  nMutex.release();
1124  return true;
1125 }
#define sConfig
Definition: Config.h:52
void SetResultQueue(SqlResultQueue *queue)
Definition: Database.cpp:332
std::vector< Value > m_values
SqlDelayThread * m_threadBody
Definition: Database.h:51
std::queue< QueuedItem > queue
Definition: SqlOperations.h:97
std::string m_logsDir
Definition: Database.h:158
ACE_Refcounted_Auto_Ptr< PreparedQueryResult, ACE_Null_Mutex > PreparedQueryResult_AutoPtr
Definition: QueryResult.h:114
char const*, can be NULL
bool bool DirectExecute(const char *sql)
Definition: Database.h:115
std::vector< std::string > Tokens
Definition: Util.h:26
bool m_connected
Definition: Database.h:166
void _ConvertValistToPreparedValues(va_list ap, PreparedValues &values, const char *fmt)
Definition: Database.cpp:964
bool BeginTransaction()
Definition: Database.cpp:533
static Thread * current()
Definition: Threading.cpp:212
MYSQL * mMysql
Definition: Database.h:165
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
Definition: Timer.h:78
void ThreadEnd()
Definition: Database.cpp:196
QueryResult_AutoPtr PQuery(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:400
QueryResult_AutoPtr bool ExecuteFile(const char *file)
Definition: Database.cpp:657
bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64 *pRowCount, uint32 *pFieldCount)
Definition: Database.cpp:337
bool Execute(const char *sql)
Definition: Database.cpp:420
bool ExecuteTransaction(SqlTransaction *transaction)
Atomically executed SqlTransaction. Don&#39;t call this directly, use BeginTransaction and CommitTransact...
Definition: Database.cpp:593
uint32 getMSTime()
Definition: Timer.h:32
#define sLog
Log class singleton.
Definition: Log.h:187
Tokens StrSplit(const std::string &src, const std::string &sep)
Definition: Util.cpp:97
ACE_INT32 int32
Definition: Define.h:67
unsigned long escape_string(char *to, const char *from, unsigned long length)
Definition: Database.cpp:212
QueryResult_AutoPtr Query(const char *sql)
Definition: Database.cpp:383
NULL Dbg ErrDB Arena Chat Char Map MMap false
Definition: Log.cpp:556
bool RollbackTransaction()
Definition: Database.cpp:571
ACE_Thread_Mutex mutex
Definition: SqlOperations.h:98
size_t size() const
ACE_Based::Thread * tranThread
Definition: Database.h:163
bool CommitTransaction()
Definition: Database.cpp:551
bool PExecuteLog(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:221
bool bool PreparedExecuteLog(const char *sql, const char *format=NULL,...)
Definition: Database.cpp:242
bool PreparedExecute(const char *sql, const char *format=NULL,...)
Executes Query via Prepared Statements.
Definition: Database.cpp:1071
Database()
Definition: Database.cpp:43
~Database()
Definition: Database.cpp:56
static size_t db_count
Definition: Database.h:168
bool Initialize(const char *infoString)
Definition: Database.cpp:75
TransactionQueues m_tranQueues
Definition: Database.h:49
PreparedStatement * stmt
Definition: SqlOperations.h:89
PreparedValues * values
Definition: SqlOperations.h:90
#define DEBUG_LOG(...)
Definition: Log.h:194
64bit signed int
floating point
void ThreadStart()
Definition: Database.cpp:191
PreparedQueryResult_AutoPtr PreparedQuery(const char *sql, const char *format=NULL,...)
Runs Query via Prepared Statements. If statement doesn&#39;t exist, it shall be created. This function blocks calling thread until query is done, if your query is result-less use PreparedExecute instead.
Definition: Database.cpp:1024
bool PExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:441
bool m_logSQL
Definition: Database.h:157
void HaltDelayThread()
Definition: Database.cpp:645
ACE_Thread_Mutex mMutex
Definition: Database.h:159
NULL LOG_TYPE_DEBUG
Definition: Log.cpp:536
ACE_UINT64 uint64
Definition: Define.h:70
void InitDelayThread()
Definition: Database.cpp:636
ACE_Thread_Mutex pMutex
Definition: Database.h:161
bool Delay(SqlOperation *sql)
ACE_Refcounted_Auto_Ptr< QueryResult, ACE_Null_Mutex > QueryResult_AutoPtr
Definition: QueryResult.h:113
PreparedStatementsMap m_preparedStatements
Definition: Database.h:178
ACE_INT64 int64
Definition: Define.h:66
#define fileno
Definition: Common.h:135
bool DirectPExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:497
#define ASSERT
Definition: Errors.h:33
bool _ExecutePreparedStatement(PreparedStatement *ps, PreparedValues *values, va_list *args, bool resultset)
Definition: Database.cpp:826
ACE_UINT32 uint32
Definition: Define.h:71
bool NextRow()
ACE_Thread_Mutex nMutex
Definition: Database.h:160
bool _TransactionCmd(const char *sql)
Definition: Database.cpp:517
QueryQueues m_queryQueues
Definition: Database.h:50
ACE_Based::Thread * m_delayThread
Definition: Database.h:52
#define vsnprintf
Definition: Common.h:131
void const*, can be NULL
PreparedStatement * _GetOrMakePreparedStatement(const char *query, const char *format, PreparedValues *values)
Definition: Database.cpp:751
#define MAX_QUERY_LEN
Definition: Database.h:44