1 /*****************************************************************************
2  * PokerTH - The open source texas holdem engine                             *
3  * Copyright (C) 2006-2016 Felix Hammer, Florian Thauer, Lothar May          *
4  *                                                                           *
5  * This program is free software: you can redistribute it and/or modify      *
6  * it under the terms of the GNU Affero General Public License as            *
7  * published by the Free Software Foundation, either version 3 of the        *
8  * License, or (at your option) any later version.                           *
9  *                                                                           *
10  * This program is distributed in the hope that it will be useful,           *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
13  * GNU Affero General Public License for more details.                       *
14  *                                                                           *
15  * You should have received a copy of the GNU Affero General Public License  *
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
17  *                                                                           *
18  *                                                                           *
19  * Additional permission under GNU AGPL version 3 section 7                  *
20  *                                                                           *
21  * If you modify this program, or any covered work, by linking or            *
22  * combining it with the OpenSSL project's OpenSSL library (or a             *
23  * modified version of that library), containing parts covered by the        *
24  * terms of the OpenSSL or SSLeay licenses, the authors of PokerTH           *
25  * (Felix Hammer, Florian Thauer, Lothar May) grant you additional           *
26  * permission to convey the resulting work.                                  *
27  * Corresponding Source for a non-source form of such a combination          *
28  * shall include the source code for the parts of OpenSSL used as well       *
29  * as that of the covered work.                                              *
30  *****************************************************************************/
31 
32 #include <dbofficial/serverdbthread.h>
33 #include <dbofficial/asyncdbauth.h>
34 #include <dbofficial/asyncdblogin.h>
35 #include <dbofficial/asyncdbavatarblacklist.h>
36 #include <dbofficial/asyncdbcreategame.h>
37 #include <dbofficial/asyncdbendgame.h>
38 #include <dbofficial/asyncdbgameplace.h>
39 #include <dbofficial/asyncdbupdatescore.h>
40 #include <dbofficial/asyncdbreportavatar.h>
41 #include <dbofficial/asyncdbreportgame.h>
42 #include <dbofficial/asyncdbadminplayers.h>
43 #include <dbofficial/asyncdbblockplayer.h>
44 #include <dbofficial/compositeasyncdbquery.h>
45 #include <dbofficial/db_table_defs.h>
46 #include <ctime>
47 #include <sstream>
48 #include <mysql++.h>
49 
50 #define QUERY_NICK_PREPARE				"nick_template"
51 #define QUERY_LOGIN_PREPARE				"login_template"
52 #define QUERY_AVATAR_BLACKLIST_PREPARE	"avatar_blacklist_template"
53 #define QUERY_CREATE_GAME_PREPARE		"create_game_template"
54 #define QUERY_END_GAME_PREPARE			"end_game_template"
55 #define QUERY_GAME_PLAYER_PREPARE		"game_player_template"
56 #define QUERY_UPDATE_SCORE_PREPARE		"update_score_template"
57 #define QUERY_REPORT_AVATAR_PREPARE		"report_avatar_template"
58 #define QUERY_REPORT_GAME_PREPARE		"report_game_template"
59 #define QUERY_ADMIN_PLAYER_PREPARE		"admin_player_template"
60 #define QUERY_BLOCK_PLAYER_PREPARE		"block_player_template"
61 
62 using namespace std;
63 
64 struct DBConnectionData {
DBConnectionDataDBConnectionData65 	DBConnectionData() : conn(false) {}
66 	string host;
67 	string user;
68 	string pwd;
69 	string database;
70 	string encryptionKey;
71 	mysqlpp::Connection conn;
72 };
73 
ServerDBThread(ServerDBCallback & cb,boost::shared_ptr<boost::asio::io_service> ioService)74 ServerDBThread::ServerDBThread(ServerDBCallback &cb, boost::shared_ptr<boost::asio::io_service> ioService)
75 	: m_ioService(ioService), m_semaphore(0), m_callback(cb), m_isConnected(false), m_permanentError(false), m_previouslyConnected(false)
76 {
77 	m_connData.reset(new DBConnectionData);
78 }
79 
~ServerDBThread()80 ServerDBThread::~ServerDBThread()
81 {
82 }
83 
84 void
SignalTermination()85 ServerDBThread::SignalTermination()
86 {
87 	Thread::SignalTermination();
88 	m_semaphore.post();
89 }
90 
91 void
Init(const string & host,const string & user,const string & pwd,const string & database,const string & encryptionKey)92 ServerDBThread::Init(const string &host, const string &user, const string &pwd,
93 					 const string &database, const string &encryptionKey)
94 {
95 	m_connData->host = host;
96 	m_connData->user = user;
97 	m_connData->pwd = pwd;
98 	m_connData->database = database;
99 	m_connData->encryptionKey = encryptionKey;
100 }
101 
102 void
Start()103 ServerDBThread::Start()
104 {
105 	Run();
106 }
107 
108 void
Stop()109 ServerDBThread::Stop()
110 {
111 	SignalTermination();
112 	this->Join(THREAD_WAIT_INFINITE);
113 }
114 
115 void
AsyncPlayerLogin(unsigned requestId,const string & playerName)116 ServerDBThread::AsyncPlayerLogin(unsigned requestId, const string &playerName)
117 {
118 	if (IsConnected()) {
119 		list<string> params;
120 		params.push_back(m_connData->encryptionKey);
121 		params.push_back(playerName);
122 		boost::shared_ptr<AsyncDBQuery> asyncQuery(
123 			new AsyncDBAuth(
124 				requestId,
125 				QUERY_NICK_PREPARE,
126 				params));
127 
128 		{
129 			boost::mutex::scoped_lock lock(m_asyncQueueMutex);
130 			m_asyncQueue.push(asyncQuery);
131 		}
132 		m_semaphore.post();
133 	} else {
134 		// If not connected to database, login fails.
135 		m_ioService->post(boost::bind(&ServerDBCallback::PlayerLoginFailed, &m_callback, requestId));
136 	}
137 }
138 
139 void
AsyncCheckAvatarBlacklist(unsigned requestId,const std::string & avatarHash)140 ServerDBThread::AsyncCheckAvatarBlacklist(unsigned requestId, const std::string &avatarHash)
141 {
142 	if (IsConnected()) {
143 		list<string> params;
144 		params.push_back(avatarHash);
145 		boost::shared_ptr<AsyncDBQuery> asyncQuery(
146 			new AsyncDBAvatarBlacklist(
147 				requestId,
148 				QUERY_AVATAR_BLACKLIST_PREPARE,
149 				params));
150 
151 		{
152 			boost::mutex::scoped_lock lock(m_asyncQueueMutex);
153 			m_asyncQueue.push(asyncQuery);
154 		}
155 		m_semaphore.post();
156 	} else {
157 		// If not connected to database, all avatars are blacklisted.
158 		m_ioService->post(boost::bind(&ServerDBCallback::AvatarIsBlacklisted, &m_callback, requestId));
159 	}
160 }
161 
162 void
PlayerPostLogin(DB_id playerId,const std::string & avatarHash,const std::string & avatarType)163 ServerDBThread::PlayerPostLogin(DB_id playerId, const std::string &avatarHash, const std::string &avatarType)
164 {
165 	list<string> params;
166 	params.push_back(mysqlpp::DateTime(time(NULL)));
167 	params.push_back(avatarHash);
168 	params.push_back(avatarType);
169 	ostringstream paramStream;
170 	paramStream << playerId;
171 	params.push_back(paramStream.str());
172 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
173 		new AsyncDBLogin(
174 			playerId,
175 			QUERY_LOGIN_PREPARE,
176 			params));
177 
178 	{
179 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
180 		m_asyncQueue.push(asyncQuery);
181 	}
182 	m_semaphore.post();
183 }
184 
185 void
PlayerLogout(DB_id)186 ServerDBThread::PlayerLogout(DB_id /*playerId*/)
187 {
188 }
189 
190 void
AsyncCreateGame(unsigned requestId,const string & gameName)191 ServerDBThread::AsyncCreateGame(unsigned requestId, const string &gameName)
192 {
193 	list<string> params;
194 	params.push_back(gameName);
195 	params.push_back(mysqlpp::DateTime(time(NULL)));
196 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
197 		new AsyncDBCreateGame(
198 			requestId,
199 			QUERY_CREATE_GAME_PREPARE,
200 			params));
201 
202 	{
203 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
204 		m_asyncQueue.push(asyncQuery);
205 	}
206 	m_semaphore.post();
207 }
208 
209 void
SetGamePlayerPlace(unsigned requestId,DB_id playerId,unsigned place)210 ServerDBThread::SetGamePlayerPlace(unsigned requestId, DB_id playerId, unsigned place)
211 {
212 	// The game id param is added later (during init of the async op), because it may be unknown.
213 	list<string> params;
214 	ostringstream paramStream;
215 	paramStream << playerId;
216 	params.push_back(paramStream.str());
217 	paramStream.str("");
218 	paramStream << place;
219 	params.push_back(paramStream.str());
220 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
221 		new AsyncDBGamePlace(
222 			requestId,
223 			QUERY_GAME_PLAYER_PREPARE,
224 			params));
225 
226 	{
227 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
228 		m_asyncQueue.push(asyncQuery);
229 	}
230 	m_semaphore.post();
231 }
232 
233 void
EndGame(unsigned requestId)234 ServerDBThread::EndGame(unsigned requestId)
235 {
236 	// Set the end time of the game.
237 	{
238 		list<string> params;
239 		params.push_back(mysqlpp::DateTime(time(NULL)));
240 		boost::shared_ptr<AsyncDBQuery> asyncQuery(
241 			new AsyncDBEndGame(
242 				requestId,
243 				QUERY_END_GAME_PREPARE,
244 				params));
245 
246 		{
247 			boost::mutex::scoped_lock lock(m_asyncQueueMutex);
248 			m_asyncQueue.push(asyncQuery);
249 		}
250 		m_semaphore.post();
251 	}
252 	// Update the player scores.
253 	{
254 		list<string> params;
255 		boost::shared_ptr<AsyncDBQuery> asyncQuery(
256 			new AsyncDBUpdateScore(
257 				requestId,
258 				QUERY_UPDATE_SCORE_PREPARE,
259 				params));
260 
261 		{
262 			boost::mutex::scoped_lock lock(m_asyncQueueMutex);
263 			m_asyncQueue.push(asyncQuery);
264 		}
265 		m_semaphore.post();
266 	}
267 }
268 
269 void
AsyncReportAvatar(unsigned requestId,unsigned replyId,DB_id reportedPlayerId,const std::string & avatarHash,const std::string & avatarType,DB_id * byPlayerId)270 ServerDBThread::AsyncReportAvatar(unsigned requestId, unsigned replyId, DB_id reportedPlayerId, const std::string &avatarHash, const std::string &avatarType, DB_id *byPlayerId)
271 {
272 	list<string> params;
273 	ostringstream paramStream;
274 	paramStream << reportedPlayerId;
275 	params.push_back(paramStream.str());
276 	params.push_back(avatarHash);
277 	params.push_back(avatarType);
278 	if (byPlayerId) {
279 		paramStream.str("");
280 		paramStream << *byPlayerId;
281 		params.push_back(paramStream.str());
282 	} else {
283 		params.push_back("NULL");
284 	}
285 	params.push_back(mysqlpp::DateTime(time(NULL)));
286 
287 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
288 		new AsyncDBReportAvatar(
289 			requestId,
290 			replyId,
291 			QUERY_REPORT_AVATAR_PREPARE,
292 			params));
293 
294 	{
295 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
296 		m_asyncQueue.push(asyncQuery);
297 	}
298 	m_semaphore.post();
299 }
300 
301 void
AsyncReportGame(unsigned requestId,unsigned replyId,DB_id * creatorPlayerId,unsigned gameId,const std::string & gameName,DB_id * byPlayerId)302 ServerDBThread::AsyncReportGame(unsigned requestId, unsigned replyId, DB_id *creatorPlayerId, unsigned gameId, const std::string &gameName, DB_id *byPlayerId)
303 {
304 	list<string> params;
305 	ostringstream paramStream;
306 	if (creatorPlayerId) {
307 		paramStream << *creatorPlayerId;
308 		params.push_back(paramStream.str());
309 	} else {
310 		params.push_back("NULL");
311 	}
312 	params.push_back(gameName);
313 	if (byPlayerId) {
314 		paramStream.str("");
315 		paramStream << *byPlayerId;
316 		params.push_back(paramStream.str());
317 	} else {
318 		params.push_back("NULL");
319 	}
320 	params.push_back(mysqlpp::DateTime(time(NULL)));
321 
322 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
323 		new AsyncDBReportGame(
324 			requestId,
325 			replyId,
326 			gameId,
327 			QUERY_REPORT_GAME_PREPARE,
328 			params));
329 
330 	{
331 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
332 		m_asyncQueue.push(asyncQuery);
333 	}
334 
335 	m_semaphore.post();
336 }
337 
338 void
AsyncQueryAdminPlayers(unsigned requestId)339 ServerDBThread::AsyncQueryAdminPlayers(unsigned requestId)
340 {
341 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
342 		new AsyncDBAdminPlayers(
343 			requestId,
344 			QUERY_ADMIN_PLAYER_PREPARE));
345 	{
346 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
347 		m_asyncQueue.push(asyncQuery);
348 	}
349 
350 	m_semaphore.post();
351 }
352 
353 void
AsyncBlockPlayer(unsigned requestId,unsigned replyId,DB_id playerId,int valid,int active)354 ServerDBThread::AsyncBlockPlayer(unsigned requestId, unsigned replyId, DB_id playerId, int valid, int active)
355 {
356 	list<string> params;
357 	ostringstream paramStream;
358 	paramStream << valid;
359 	params.push_back(paramStream.str());
360 	paramStream.str("");
361 	paramStream << active;
362 	params.push_back(paramStream.str());
363 	paramStream.str("");
364 	paramStream << playerId;
365 	params.push_back(paramStream.str());
366 	boost::shared_ptr<AsyncDBQuery> asyncQuery(
367 		new AsyncDBBlockPlayer(
368 			requestId,
369 			replyId,
370 			QUERY_BLOCK_PLAYER_PREPARE,
371 			params));
372 
373 	{
374 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
375 		m_asyncQueue.push(asyncQuery);
376 	}
377 	m_semaphore.post();
378 }
379 
380 bool
IsConnected() const381 ServerDBThread::IsConnected() const
382 {
383 	boost::mutex::scoped_lock lock(m_isConnectedMutex);
384 	return m_isConnected;
385 }
386 
387 void
Main()388 ServerDBThread::Main()
389 {
390 	while (!ShouldTerminate() && !HasPermanentError()) {
391 		if (HasDBConnection()) {
392 			SetConnected(true);
393 			m_semaphore.wait();
394 			HandleNextQuery();
395 		} else {
396 			SetConnected(false);
397 			EstablishDBConnection();
398 		}
399 	}
400 	m_connData->conn.disconnect();
401 }
402 
403 bool
HasPermanentError() const404 ServerDBThread::HasPermanentError() const
405 {
406 	return m_permanentError;
407 }
408 
409 bool
HasDBConnection() const410 ServerDBThread::HasDBConnection() const
411 {
412 	return m_connData->conn.connected();
413 }
414 
415 void
EstablishDBConnection()416 ServerDBThread::EstablishDBConnection()
417 {
418 	m_connData->conn.set_option(new mysqlpp::SetCharsetNameOption("utf8"));
419 	if (!m_connData->conn.connect(
420 				m_connData->database.c_str(), m_connData->host.c_str(), m_connData->user.c_str(), m_connData->pwd.c_str())) {
421 		m_ioService->post(boost::bind(&ServerDBCallback::ConnectFailed, &m_callback, m_connData->conn.error()));
422 		if (!m_previouslyConnected)
423 			m_permanentError = true;
424 		else
425 			Msleep(250);
426 	} else {
427 		mysqlpp::Query prepareNick = m_connData->conn.query();
428 		/*
429 		prepareNick
430 				<< "PREPARE " QUERY_NICK_PREPARE " FROM " << mysqlpp::quote
431 				<< "SELECT " DB_TABLE_PLAYER_COL_ID ", AES_DECRYPT(" DB_TABLE_PLAYER_COL_PASSWORD ", ?), " DB_TABLE_PLAYER_COL_VALID ", TRIM(" DB_TABLE_PLAYER_COL_COUNTRY "), " DB_TABLE_PLAYER_COL_LASTLOGIN ", " DB_TABLE_PLAYER_COL_ACTIVE " FROM " DB_TABLE_PLAYER " WHERE BINARY " DB_TABLE_PLAYER_COL_USERNAME " = ?";
432 		*/
433 		prepareNick
434 				<< "PREPARE " QUERY_NICK_PREPARE " FROM " << mysqlpp::quote
435 				<< "SELECT " DB_TABLE_PLAYER_COL_ID ", AES_DECRYPT(" DB_TABLE_PLAYER_COL_PASSWORD ", ?), " DB_TABLE_PLAYER_COL_VALID ", TRIM(" DB_TABLE_PLAYER_COL_COUNTRY "), " DB_TABLE_PLAYER_COL_LASTLOGIN ", " DB_TABLE_PLAYER_COL_ACTIVE " FROM " DB_TABLE_PLAYER " WHERE " DB_TABLE_PLAYER_COL_USERNAME " = ?";
436 
437 		mysqlpp::Query prepareAvatarBlacklist = m_connData->conn.query();
438 		prepareAvatarBlacklist
439 				<< "PREPARE " QUERY_AVATAR_BLACKLIST_PREPARE " FROM " << mysqlpp::quote
440 				<< "SELECT " DB_TABLE_AVATAR_BLACKLIST_COL_ID " FROM " DB_TABLE_AVATAR_BLACKLIST " WHERE BINARY " DB_TABLE_AVATAR_BLACKLIST_COL_AVATAR_HASH " = ?";
441 
442 		mysqlpp::Query prepareLogin = m_connData->conn.query();
443 		prepareLogin
444 				<< "PREPARE " QUERY_LOGIN_PREPARE " FROM " << mysqlpp::quote
445 				<< "UPDATE " DB_TABLE_PLAYER " SET " DB_TABLE_PLAYER_COL_LASTLOGIN " = ?, " DB_TABLE_PLAYER_COL_AVATARHASH " = ?, " DB_TABLE_PLAYER_COL_AVATARTYPE " = ? WHERE " DB_TABLE_PLAYER_COL_ID " = ?";
446 		mysqlpp::Query prepareCreateGame = m_connData->conn.query();
447 		prepareCreateGame
448 				<< "PREPARE " QUERY_CREATE_GAME_PREPARE " FROM " << mysqlpp::quote
449 				<< "INSERT INTO " DB_TABLE_GAME " (" DB_TABLE_GAME_COL_NAME ", " DB_TABLE_GAME_COL_STARTTIME ") VALUES (?, ?)";
450 		mysqlpp::Query prepareEndGame = m_connData->conn.query();
451 		prepareEndGame
452 				<< "PREPARE " QUERY_END_GAME_PREPARE " FROM " << mysqlpp::quote
453 				<< "UPDATE " DB_TABLE_GAME " SET "DB_TABLE_GAME_COL_ENDTIME " = ? WHERE " DB_TABLE_GAME_COL_ID " = ?";
454 		mysqlpp::Query prepareRelation = m_connData->conn.query();
455 		prepareRelation
456 				<< "PREPARE " QUERY_GAME_PLAYER_PREPARE " FROM " << mysqlpp::quote
457 				<< "INSERT INTO " DB_TABLE_GAMEPLAYER " (" DB_TABLE_GAMEPLAYER_COL_GAMEID ", " DB_TABLE_GAMEPLAYER_COL_PLAYERID ", " DB_TABLE_GAMEPLAYER_COL_PLACE ") VALUES (?, ?, ?)";
458 		mysqlpp::Query prepareScore = m_connData->conn.query();
459 		prepareScore
460 				<< "PREPARE " QUERY_UPDATE_SCORE_PREPARE " FROM " << mysqlpp::quote
461 				<< "CALL updatePointsForGame(?)";
462 
463 		mysqlpp::Query prepareReportAvatar = m_connData->conn.query();
464 		prepareReportAvatar
465 				<< "PREPARE " QUERY_REPORT_AVATAR_PREPARE " FROM " << mysqlpp::quote
466 				<< "INSERT INTO " DB_TABLE_REP_AVATAR " (" DB_TABLE_REP_AVATAR_COL_PLAYERID ", " DB_TABLE_REP_AVATAR_COL_AVATARHASH ", " DB_TABLE_REP_AVATAR_COL_AVATARTYPE ", " DB_TABLE_REP_AVATAR_COL_BY_PLAYERID ", " DB_TABLE_REP_AVATAR_COL_TIMESTAMP ") VALUES (?, ?, ?, ?, ?)";
467 
468 		mysqlpp::Query prepareReportGame = m_connData->conn.query();
469 		prepareReportGame
470 				<< "PREPARE " QUERY_REPORT_GAME_PREPARE " FROM " << mysqlpp::quote
471 				<< "INSERT INTO " DB_TABLE_REP_GAME " (" DB_TABLE_REP_GAME_COL_CREATOR ", " DB_TABLE_REP_GAME_COL_GAMENAME ", " DB_TABLE_REP_GAME_COL_BY_PLAYERID ", " DB_TABLE_REP_GAME_COL_TIMESTAMP ", " DB_TABLE_REP_GAME_COL_GAMEID ") VALUES (?, ?, ?, ?, ?)";
472 
473 		mysqlpp::Query prepareAdminPlayer = m_connData->conn.query();
474 		prepareAdminPlayer
475 				<< "PREPARE " QUERY_ADMIN_PLAYER_PREPARE " FROM " << mysqlpp::quote
476 				<< "SELECT " DB_TABLE_ADMIN_PLAYER_COL_PLAYERID " FROM " DB_TABLE_ADMIN_PLAYER;
477 
478 		mysqlpp::Query prepareBlockPlayer = m_connData->conn.query();
479 		prepareBlockPlayer
480 				<< "PREPARE " QUERY_BLOCK_PLAYER_PREPARE " FROM " << mysqlpp::quote
481 				<< "UPDATE " DB_TABLE_PLAYER " SET " DB_TABLE_PLAYER_COL_VALID " = ?, " DB_TABLE_PLAYER_COL_ACTIVE " = ? WHERE " DB_TABLE_PLAYER_COL_ID " = ?";
482 		if (!prepareNick.exec() || !prepareAvatarBlacklist.exec() || !prepareLogin.exec() || !prepareCreateGame.exec()
483 				|| !prepareEndGame.exec() || !prepareRelation.exec() || !prepareScore.exec() || !prepareReportAvatar.exec()
484 				|| !prepareReportGame.exec() || !prepareAdminPlayer.exec() || !prepareBlockPlayer.exec()) {
485 			string tmpError = string(prepareNick.error()) + prepareAvatarBlacklist.error() + prepareLogin.error() + prepareCreateGame.error() +
486 							  prepareEndGame.error() + prepareRelation.error() + prepareScore.error() + prepareReportAvatar.error() +
487 							  prepareReportGame.error() + prepareAdminPlayer.error() + prepareBlockPlayer.error();
488 			m_connData->conn.disconnect();
489 			m_ioService->post(boost::bind(&ServerDBCallback::ConnectFailed, &m_callback, tmpError));
490 			m_permanentError = true;
491 		} else {
492 			m_ioService->post(boost::bind(&ServerDBCallback::ConnectSuccess, &m_callback));
493 			m_previouslyConnected = true;
494 		}
495 	}
496 }
497 
498 void
HandleNextQuery()499 ServerDBThread::HandleNextQuery()
500 {
501 	boost::shared_ptr<AsyncDBQuery> nextQuery;
502 	{
503 		boost::mutex::scoped_lock lock(m_asyncQueueMutex);
504 		if (!m_asyncQueue.empty()) {
505 			nextQuery = m_asyncQueue.front();
506 			m_asyncQueue.pop();
507 		}
508 	}
509 	if (nextQuery) {
510 		do {
511 			nextQuery->Init(m_dbIdManager);
512 			mysqlpp::Query executeQuery = m_connData->conn.query();
513 			executeQuery << "EXECUTE " << nextQuery->GetPreparedName();
514 
515 			list<string> paramList;
516 			nextQuery->GetParams(paramList);
517 			if (!paramList.empty()) {
518 				executeQuery << " using ";
519 				mysqlpp::Query paramQuery = m_connData->conn.query();
520 				paramQuery << "SET ";
521 				unsigned counter = 1;
522 				list<string>::iterator i = paramList.begin();
523 				list<string>::iterator end = paramList.end();
524 				while (i != end) {
525 					if (counter > 1) {
526 						paramQuery << ", ";
527 						executeQuery << ", ";
528 					}
529 					paramQuery << "@param" << counter << " = ";
530 					if (*i == "NULL") {
531 						paramQuery << "NULL";
532 					} else {
533 						paramQuery << "_utf8" << mysqlpp::quote << *i;
534 					}
535 					executeQuery << "@param" << counter;
536 					++counter;
537 					++i;
538 				}
539 				if (!paramQuery.exec()) {
540 					string tmpError = paramQuery.error();
541 					m_connData->conn.disconnect();
542 					m_ioService->post(boost::bind(&ServerDBCallback::QueryError, &m_callback, tmpError));
543 					break;
544 				}
545 			}
546 			if (nextQuery->RequiresResultSet()) {
547 				mysqlpp::StoreQueryResult res = executeQuery.store();
548 				if (res)
549 					nextQuery->HandleResult(executeQuery, m_dbIdManager, res, *m_ioService, m_callback);
550 				else
551 					nextQuery->HandleError(*m_ioService, m_callback);
552 			} else {
553 				if (executeQuery.exec())
554 					nextQuery->HandleNoResult(executeQuery, m_dbIdManager, *m_ioService, m_callback);
555 				else
556 					nextQuery->HandleError(*m_ioService, m_callback);
557 			}
558 		} while (nextQuery->Next()); // Consider composite queries.
559 	}
560 }
561 
562 void
SetConnected(bool isConnected)563 ServerDBThread::SetConnected(bool isConnected)
564 {
565 	boost::mutex::scoped_lock lock(m_isConnectedMutex);
566 	m_isConnected = isConnected;
567 }
568 
569