aboutsummaryrefslogtreecommitdiff
path: root/src/server/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/database')
-rw-r--r--src/server/database/CMakeLists.txt4
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h6
-rw-r--r--src/server/database/Database/Field.cpp13
-rw-r--r--src/server/database/Database/Field.h80
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp16
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h32
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h28
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.h28
-rw-r--r--src/server/database/Database/QueryResult.cpp117
-rw-r--r--src/server/database/Database/QueryResult.h11
-rw-r--r--src/server/database/Updater/DBUpdater.cpp125
-rw-r--r--src/server/database/Updater/DBUpdater.h14
12 files changed, 285 insertions, 189 deletions
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
index 2375f18d7b5..19fa0ee0acf 100644
--- a/src/server/database/CMakeLists.txt
+++ b/src/server/database/CMakeLists.txt
@@ -8,7 +8,9 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-find_package(MySQL REQUIRED)
+if (NOT MYSQL_FOUND)
+ message(SEND_ERROR "MySQL wasn't found on your system but it's required to build the servers!")
+endif()
if( USE_COREPCH )
include_directories(${CMAKE_CURRENT_BINARY_DIR})
diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h
index f5002c6943b..32837daf5da 100644
--- a/src/server/database/Database/DatabaseWorkerPool.h
+++ b/src/server/database/Database/DatabaseWorkerPool.h
@@ -428,10 +428,12 @@ class DatabaseWorkerPool
Other
*/
+ typedef typename T::Statements PreparedStatementIndex;
+
//! Automanaged (internally) pointer to a prepared statement object for usage in upper level code.
//! Pointer is deleted in this->DirectExecute(PreparedStatement*), this->Query(PreparedStatement*) or PreparedStatementTask::~PreparedStatementTask.
//! This object is not tied to the prepared statement on the MySQL context yet until execution.
- PreparedStatement* GetPreparedStatement(uint32 index)
+ PreparedStatement* GetPreparedStatement(PreparedStatementIndex index)
{
return new PreparedStatement(index);
}
@@ -482,7 +484,7 @@ class DatabaseWorkerPool
else if (type == IDX_SYNCH)
t = new T(*_connectionInfo);
else
- ASSERT(false);
+ ABORT();
_connections[type][i] = t;
++_connectionCount[type];
diff --git a/src/server/database/Database/Field.cpp b/src/server/database/Database/Field.cpp
index 89195b699b7..f7794bd8975 100644
--- a/src/server/database/Database/Field.cpp
+++ b/src/server/database/Database/Field.cpp
@@ -30,18 +30,11 @@ Field::~Field()
CleanUp();
}
-void Field::SetByteValue(const void* newValue, const size_t newSize, enum_field_types newType, uint32 length)
+void Field::SetByteValue(void* newValue, enum_field_types newType, uint32 length)
{
- if (data.value)
- CleanUp();
-
// This value stores raw bytes that have to be explicitly cast later
- if (newValue)
- {
- data.value = new char[newSize];
- memcpy(data.value, newValue, newSize);
- data.length = length;
- }
+ data.value = newValue;
+ data.length = length;
data.type = newType;
data.raw = true;
}
diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h
index 1bbd264482f..42a842e6283 100644
--- a/src/server/database/Database/Field.h
+++ b/src/server/database/Database/Field.h
@@ -29,6 +29,8 @@ class Field
friend class PreparedResultSet;
public:
+ Field();
+ ~Field();
bool GetBool() const // Wrapper, actually gets integer
{
@@ -43,7 +45,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_TINY))
{
- TC_LOG_WARN("sql.sql", "Warning: GetUInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt8() on non-tinyint field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -61,7 +64,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_TINY))
{
- TC_LOG_WARN("sql.sql", "Warning: GetInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetInt8() on non-tinyint field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -79,7 +83,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR))
{
- TC_LOG_WARN("sql.sql", "Warning: GetUInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt16() on non-smallint field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -97,7 +102,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR))
{
- TC_LOG_WARN("sql.sql", "Warning: GetInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetInt16() on non-smallint field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -115,7 +121,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG))
{
- TC_LOG_WARN("sql.sql", "Warning: GetUInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt32() on non-(medium)int field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -133,7 +140,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG))
{
- TC_LOG_WARN("sql.sql", "Warning: GetInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetInt32() on non-(medium)int field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -151,7 +159,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT))
{
- TC_LOG_WARN("sql.sql", "Warning: GetUInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt64() on non-bigint field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -169,7 +178,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT))
{
- TC_LOG_WARN("sql.sql", "Warning: GetInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetInt64() on non-bigint field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0;
}
#endif
@@ -187,7 +197,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_FLOAT))
{
- TC_LOG_WARN("sql.sql", "Warning: GetFloat() on non-float field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetFloat() on non-float field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0.0f;
}
#endif
@@ -205,7 +216,8 @@ class Field
#ifdef TRINITY_DEBUG
if (!IsType(MYSQL_TYPE_DOUBLE))
{
- TC_LOG_WARN("sql.sql", "Warning: GetDouble() on non-double field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Warning: GetDouble() on non-double field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return 0.0f;
}
#endif
@@ -223,7 +235,8 @@ class Field
#ifdef TRINITY_DEBUG
if (IsNumeric())
{
- TC_LOG_WARN("sql.sql", "Error: GetCString() on numeric field. Using type: %s.", FieldTypeToString(data.type));
+ TC_LOG_WARN("sql.sql", "Error: GetCString() on numeric field %s.%s (%s.%s) at index %u. Using type: %s.",
+ meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type);
return NULL;
}
#endif
@@ -236,14 +249,11 @@ class Field
if (!data.value)
return "";
- if (data.raw)
- {
- char const* string = GetCString();
- if (!string)
- string = "";
- return std::string(string, data.length);
- }
- return std::string((char*)data.value);
+ char const* string = GetCString();
+ if (!string)
+ return "";
+
+ return std::string(string, data.length);
}
bool IsNull() const
@@ -251,10 +261,17 @@ class Field
return data.value == NULL;
}
- protected:
- Field();
- ~Field();
+ struct Metadata
+ {
+ char const* TableName;
+ char const* TableAlias;
+ char const* Name;
+ char const* Alias;
+ char const* Type;
+ uint32 Index;
+ };
+ protected:
#pragma pack(push, 1)
struct
{
@@ -265,12 +282,14 @@ class Field
} data;
#pragma pack(pop)
- void SetByteValue(void const* newValue, size_t const newSize, enum_field_types newType, uint32 length);
+ void SetByteValue(void* newValue, enum_field_types newType, uint32 length);
void SetStructuredValue(char* newValue, enum_field_types newType);
void CleanUp()
{
- delete[] ((char*)data.value);
+ // Field does not own the data if fetched with prepared statement
+ if (!data.raw)
+ delete[] ((char*)data.value);
data.value = NULL;
}
@@ -375,6 +394,19 @@ class Field
default: return "-Unknown-";
}
}
+
+ void SetMetadata(MYSQL_FIELD* field, uint32 fieldIndex)
+ {
+ meta.TableName = field->org_table;
+ meta.TableAlias = field->table;
+ meta.Name = field->org_name;
+ meta.Alias = field->name;
+ meta.Type = FieldTypeToString(field->type);
+ meta.Index = fieldIndex;
+ }
+
+ Metadata meta;
+
#endif
};
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 1efdb16804f..14e7571dd42 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -127,8 +127,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid WHERE mail_id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, itemguid, itemEntry FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, auctioneerguid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH);
- PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, auctioneerguid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, houseid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, houseid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_AUCTION_BID, "UPDATE auctionhouse SET buyguid = ?, lastbid = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
@@ -316,11 +316,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_PLAYER_HOMEBIND, "DELETE FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
// Corpse
- PrepareStatement(CHAR_SEL_CORPSES, "SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask, corpseGuid, guid FROM corpse WHERE corpseType <> 0", CONNECTION_SYNCH);
- PrepareStatement(CHAR_INS_CORPSE, "INSERT INTO corpse (corpseGuid, guid, posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_CORPSE, "DELETE FROM corpse WHERE corpseGuid = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_PLAYER_CORPSES, "DELETE FROM corpse WHERE guid = ? AND corpseType <> 0", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_OLD_CORPSES, "DELETE FROM corpse WHERE corpseType = 0 OR time < (UNIX_TIMESTAMP(NOW()) - ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CORPSES, "SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask, guid FROM corpse WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_CORPSE, "INSERT INTO corpse (guid, posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CORPSE, "DELETE FROM corpse WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CORPSES_FROM_MAP, "DELETE FROM corpse WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CORPSE_LOCATION, "SELECT mapId, posX, posY, posZ, orientation FROM corpse WHERE guid = ?", CONNECTION_ASYNC);
// Creature respawn
PrepareStatement(CHAR_SEL_CREATURE_RESPAWNS, "SELECT guid, respawnTime FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH);
@@ -337,7 +337,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// GM Tickets
PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH);
- PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_ticket WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_ticket WHERE playerGuid = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index f88a912e022..c76c584a0a8 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -21,19 +21,6 @@
#include "DatabaseWorkerPool.h"
#include "MySQLConnection.h"
-class CharacterDatabaseConnection : public MySQLConnection
-{
- public:
- //- Constructors for sync and async connections
- CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
- CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
-
- //- Loads database type specific prepared statements
- void DoPrepareStatements() override;
-};
-
-typedef DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabaseWorkerPool;
-
enum CharacterDatabaseStatements
{
/* Naming standard for defines:
@@ -281,8 +268,8 @@ enum CharacterDatabaseStatements
CHAR_SEL_CORPSES,
CHAR_INS_CORPSE,
CHAR_DEL_CORPSE,
- CHAR_DEL_PLAYER_CORPSES,
- CHAR_DEL_OLD_CORPSES,
+ CHAR_DEL_CORPSES_FROM_MAP,
+ CHAR_SEL_CORPSE_LOCATION,
CHAR_SEL_CREATURE_RESPAWNS,
CHAR_REP_CREATURE_RESPAWN,
@@ -547,4 +534,19 @@ enum CharacterDatabaseStatements
MAX_CHARACTERDATABASE_STATEMENTS
};
+class CharacterDatabaseConnection : public MySQLConnection
+{
+public:
+ typedef CharacterDatabaseStatements Statements;
+
+ //- Constructors for sync and async connections
+ CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
+ CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+typedef DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabaseWorkerPool;
+
#endif
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index 7f6cffa520f..79b7a53cb6e 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -21,19 +21,6 @@
#include "DatabaseWorkerPool.h"
#include "MySQLConnection.h"
-class LoginDatabaseConnection : public MySQLConnection
-{
- public:
- //- Constructors for sync and async connections
- LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
- LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
-
- //- Loads database type specific prepared statements
- void DoPrepareStatements() override;
-};
-
-typedef DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabaseWorkerPool;
-
enum LoginDatabaseStatements
{
/* Naming standard for defines:
@@ -133,4 +120,19 @@ enum LoginDatabaseStatements
MAX_LOGINDATABASE_STATEMENTS
};
+class LoginDatabaseConnection : public MySQLConnection
+{
+public:
+ typedef LoginDatabaseStatements Statements;
+
+ //- Constructors for sync and async connections
+ LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
+ LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+typedef DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabaseWorkerPool;
+
#endif
diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h
index 36fd6fbb186..c5475835fbd 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.h
+++ b/src/server/database/Database/Implementation/WorldDatabase.h
@@ -21,19 +21,6 @@
#include "DatabaseWorkerPool.h"
#include "MySQLConnection.h"
-class WorldDatabaseConnection : public MySQLConnection
-{
- public:
- //- Constructors for sync and async connections
- WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
- WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
-
- //- Loads database type specific prepared statements
- void DoPrepareStatements() override;
-};
-
-typedef DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabaseWorkerPool;
-
enum WorldDatabaseStatements
{
/* Naming standard for defines:
@@ -116,4 +103,19 @@ enum WorldDatabaseStatements
MAX_WORLDDATABASE_STATEMENTS
};
+class WorldDatabaseConnection : public MySQLConnection
+{
+public:
+ typedef WorldDatabaseStatements Statements;
+
+ //- Constructors for sync and async connections
+ WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
+ WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+typedef DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabaseWorkerPool;
+
#endif
diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp
index 02352f221a0..983ffee24c4 100644
--- a/src/server/database/Database/QueryResult.cpp
+++ b/src/server/database/Database/QueryResult.cpp
@@ -26,7 +26,10 @@ _result(result),
_fields(fields)
{
_currentRow = new Field[_fieldCount];
- ASSERT(_currentRow);
+#ifdef TRINITY_DEBUG
+ for (uint32 i = 0; i < _fieldCount; i++)
+ _currentRow[i].SetMetadata(&_fields[i], i);
+#endif
}
PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) :
@@ -35,11 +38,11 @@ m_rowPosition(0),
m_fieldCount(fieldCount),
m_rBind(NULL),
m_stmt(stmt),
-m_res(result),
+m_metadataResult(result),
m_isNull(NULL),
m_length(NULL)
{
- if (!m_res)
+ if (!m_metadataResult)
return;
if (m_stmt->bind_result_done)
@@ -66,50 +69,52 @@ m_length(NULL)
return;
}
+ m_rowCount = mysql_stmt_num_rows(m_stmt);
+
//- This is where we prepare the buffer based on metadata
- uint32 i = 0;
- MYSQL_FIELD* field = mysql_fetch_field(m_res);
- while (field)
+ MYSQL_FIELD* field = mysql_fetch_fields(m_metadataResult);
+ std::size_t rowSize = 0;
+ for (uint32 i = 0; i < m_fieldCount; ++i)
{
- size_t size = Field::SizeForType(field);
+ size_t size = Field::SizeForType(&field[i]);
+ rowSize += size;
- m_rBind[i].buffer_type = field->type;
- m_rBind[i].buffer = malloc(size);
- memset(m_rBind[i].buffer, 0, size);
+ m_rBind[i].buffer_type = field[i].type;
m_rBind[i].buffer_length = size;
m_rBind[i].length = &m_length[i];
m_rBind[i].is_null = &m_isNull[i];
m_rBind[i].error = NULL;
- m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG;
+ m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG;
+ }
- ++i;
- field = mysql_fetch_field(m_res);
+ char* dataBuffer = new char[rowSize * m_rowCount];
+ for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i)
+ {
+ m_rBind[i].buffer = dataBuffer + offset;
+ offset += m_rBind[i].buffer_length;
}
//- This is where we bind the bind the buffer to the statement
if (mysql_stmt_bind_result(m_stmt, m_rBind))
{
TC_LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt));
- delete[] m_rBind;
+ mysql_stmt_free_result(m_stmt);
+ CleanUp();
delete[] m_isNull;
delete[] m_length;
return;
}
- m_rowCount = mysql_stmt_num_rows(m_stmt);
-
- m_rows.resize(uint32(m_rowCount));
+ m_rows.resize(uint32(m_rowCount) * m_fieldCount);
while (_NextRow())
{
- m_rows[uint32(m_rowPosition)] = new Field[m_fieldCount];
- for (uint64 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
+ for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
{
+ unsigned long buffer_length = m_rBind[fIndex].buffer_length;
+ unsigned long fetched_length = *m_rBind[fIndex].length;
if (!*m_rBind[fIndex].is_null)
- m_rows[uint32(m_rowPosition)][fIndex].SetByteValue(m_rBind[fIndex].buffer,
- m_rBind[fIndex].buffer_length,
- m_rBind[fIndex].buffer_type,
- *m_rBind[fIndex].length);
- else
+ {
+ void* buffer = m_stmt->bind[fIndex].buffer;
switch (m_rBind[fIndex].buffer_type)
{
case MYSQL_TYPE_TINY_BLOB:
@@ -118,24 +123,44 @@ m_length(NULL)
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
- m_rows[uint32(m_rowPosition)][fIndex].SetByteValue("",
- m_rBind[fIndex].buffer_length,
- m_rBind[fIndex].buffer_type,
- *m_rBind[fIndex].length);
- break;
+ // warning - the string will not be null-terminated if there is no space for it in the buffer
+ // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED
+ // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string
+ // in this case using Field::GetCString will result in garbage
+ // TODO: remove Field::GetCString and use boost::string_ref (currently proposed for TS as string_view, maybe in C++17)
+ if (fetched_length < buffer_length)
+ *((char*)buffer + fetched_length) = '\0';
+ break;
default:
- m_rows[uint32(m_rowPosition)][fIndex].SetByteValue(nullptr,
- m_rBind[fIndex].buffer_length,
- m_rBind[fIndex].buffer_type,
- *m_rBind[fIndex].length);
+ break;
}
+
+ m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(
+ buffer,
+ m_rBind[fIndex].buffer_type,
+ fetched_length);
+
+ // move buffer pointer to next part
+ m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize;
+ }
+ else
+ {
+ m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(
+ nullptr,
+ m_rBind[fIndex].buffer_type,
+ *m_rBind[fIndex].length);
+ }
+
+#ifdef TRINITY_DEBUG
+ m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&field[fIndex], fIndex);
+#endif
}
m_rowPosition++;
}
m_rowPosition = 0;
/// All data is buffered, let go of mysql c api structures
- CleanUp();
+ mysql_stmt_free_result(m_stmt);
}
ResultSet::~ResultSet()
@@ -145,8 +170,7 @@ ResultSet::~ResultSet()
PreparedResultSet::~PreparedResultSet()
{
- for (uint32 i = 0; i < uint32(m_rowCount); ++i)
- delete[] m_rows[i];
+ CleanUp();
}
bool ResultSet::NextRow()
@@ -207,18 +231,13 @@ void ResultSet::CleanUp()
void PreparedResultSet::CleanUp()
{
- /// More of the in our code allocated sources are deallocated by the poorly documented mysql c api
- if (m_res)
- mysql_free_result(m_res);
+ if (m_metadataResult)
+ mysql_free_result(m_metadataResult);
- FreeBindBuffer();
- mysql_stmt_free_result(m_stmt);
-
- delete[] m_rBind;
-}
-
-void PreparedResultSet::FreeBindBuffer()
-{
- for (uint32 i = 0; i < m_fieldCount; ++i)
- free (m_rBind[i].buffer);
+ if (m_rBind)
+ {
+ delete[](char*)m_rBind->buffer;
+ delete[] m_rBind;
+ m_rBind = nullptr;
+ }
}
diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h
index a61fb6331c1..0447ecaae5a 100644
--- a/src/server/database/Database/QueryResult.h
+++ b/src/server/database/Database/QueryResult.h
@@ -73,18 +73,18 @@ class PreparedResultSet
Field* Fetch() const
{
ASSERT(m_rowPosition < m_rowCount);
- return m_rows[uint32(m_rowPosition)];
+ return const_cast<Field*>(&m_rows[uint32(m_rowPosition) * m_fieldCount]);
}
- const Field & operator [] (uint32 index) const
+ Field const& operator[](uint32 index) const
{
ASSERT(m_rowPosition < m_rowCount);
ASSERT(index < m_fieldCount);
- return m_rows[uint32(m_rowPosition)][index];
+ return m_rows[uint32(m_rowPosition) * m_fieldCount + index];
}
protected:
- std::vector<Field*> m_rows;
+ std::vector<Field> m_rows;
uint64 m_rowCount;
uint64 m_rowPosition;
uint32 m_fieldCount;
@@ -92,12 +92,11 @@ class PreparedResultSet
private:
MYSQL_BIND* m_rBind;
MYSQL_STMT* m_stmt;
- MYSQL_RES* m_res;
+ MYSQL_RES* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata
my_bool* m_isNull;
unsigned long* m_length;
- void FreeBindBuffer();
void CleanUp();
bool _NextRow();
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index ebdd6604fef..d90d71c5594 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -26,6 +26,8 @@
#include <iostream>
#include <unordered_map>
#include <boost/process.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/system/system_error.hpp>
@@ -33,24 +35,64 @@ using namespace boost::process;
using namespace boost::process::initializers;
using namespace boost::iostreams;
-template<class T>
-std::string DBUpdater<T>::GetSourceDirectory()
+std::string DBUpdaterUtil::GetMySqlCli()
{
- std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
- if (!entry.empty())
- return entry;
+ if (!corrected_path().empty())
+ return corrected_path();
else
- return GitRevision::GetSourceDirectory();
+ {
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
+ if (!entry.empty())
+ return entry;
+ else
+ return GitRevision::GetMySQLExecutable();
+ }
+}
+
+bool DBUpdaterUtil::CheckExecutable()
+{
+ boost::filesystem::path exe(GetMySqlCli());
+ if (!exists(exe))
+ {
+ exe.clear();
+
+ try
+ {
+ exe = search_path("mysql");
+ }
+ catch (std::runtime_error&)
+ {
+ }
+
+ if (!exe.empty() && exists(exe))
+ {
+ // Correct the path to the cli
+ corrected_path() = absolute(exe).generic_string();
+ return true;
+ }
+
+ TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
+ absolute(exe).generic_string().c_str());
+
+ return false;
+ }
+ return true;
+}
+
+std::string& DBUpdaterUtil::corrected_path()
+{
+ static std::string path;
+ return path;
}
template<class T>
-std::string DBUpdater<T>::GetMySqlCli()
+std::string DBUpdater<T>::GetSourceDirectory()
{
- std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
if (!entry.empty())
return entry;
else
- return GitRevision::GetMySQLExecutable();
+ return GitRevision::GetSourceDirectory();
}
// Auth Database
@@ -145,36 +187,6 @@ BaseLocation DBUpdater<T>::GetBaseLocationType()
}
template<class T>
-bool DBUpdater<T>::CheckExecutable()
-{
- DBUpdater<T>::Path const exe(DBUpdater<T>::GetMySqlCli());
- if (!exists(exe))
- {
- // Check for mysql in path
- std::vector<std::string> args = {"--version"};
- uint32 ret;
- try
- {
- child c = execute(run_exe("mysql"), set_args(args), throw_on_error(), close_stdout());
- ret = wait_for_exit(c);
- }
- catch (boost::system::system_error&)
- {
- ret = EXIT_FAILURE;
- }
-
- if (ret == EXIT_FAILURE)
- {
- TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\', correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
- absolute(exe).generic_string().c_str());
-
- return false;
- }
- }
- return true;
-}
-
-template<class T>
bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
{
TC_LOG_INFO("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ",
@@ -222,7 +234,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
template<class T>
bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
{
- if (!DBUpdater<T>::CheckExecutable())
+ if (!DBUpdaterUtil::CheckExecutable())
return false;
TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
@@ -273,7 +285,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
return true;
}
- if (!DBUpdater<T>::CheckExecutable())
+ if (!DBUpdaterUtil::CheckExecutable())
return false;
TC_LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str());
@@ -346,7 +358,10 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path)
{
std::vector<std::string> args;
- args.reserve(7);
+ args.reserve(8);
+
+ // args[0] represents the program name
+ args.push_back("mysql");
// CLI Client connection info
args.push_back("-h" + host);
@@ -391,9 +406,29 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
uint32 ret;
try
{
- child c = execute(run_exe(DBUpdater<T>::GetMySqlCli().empty() ? "mysql" :
- boost::filesystem::absolute(DBUpdater<T>::GetMySqlCli()).generic_string()),
- set_args(args), bind_stdin(source), throw_on_error());
+ boost::process::pipe outPipe = create_pipe();
+ boost::process::pipe errPipe = create_pipe();
+
+ child c = execute(run_exe(
+ boost::filesystem::absolute(DBUpdaterUtil::GetMySqlCli()).generic_string()),
+ set_args(args), bind_stdin(source), throw_on_error(),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+
+ file_descriptor_source mysqlOutfd(outPipe.source, close_handle);
+ file_descriptor_source mysqlErrfd(errPipe.source, close_handle);
+
+ stream<file_descriptor_source> mysqlOutStream(mysqlOutfd);
+ stream<file_descriptor_source> mysqlErrStream(mysqlErrfd);
+
+ std::stringstream out;
+ std::stringstream err;
+
+ copy(mysqlOutStream, out);
+ copy(mysqlErrStream, err);
+
+ TC_LOG_INFO("sql.updates", "%s", out.str().c_str());
+ TC_LOG_ERROR("sql.updates", "%s", err.str().c_str());
ret = wait_for_exit(c);
}
diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h
index a2b12bed235..c8aa5d69fbc 100644
--- a/src/server/database/Updater/DBUpdater.h
+++ b/src/server/database/Updater/DBUpdater.h
@@ -54,6 +54,17 @@ struct UpdateResult
size_t archived;
};
+class DBUpdaterUtil
+{
+public:
+ static std::string GetMySqlCli();
+
+ static bool CheckExecutable();
+
+private:
+ static std::string& corrected_path();
+};
+
template <class T>
class DBUpdater
{
@@ -79,9 +90,6 @@ public:
static bool Populate(DatabaseWorkerPool<T>& pool);
private:
- static std::string GetMySqlCli();
- static bool CheckExecutable();
-
static QueryResult Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query);
static void Apply(DatabaseWorkerPool<T>& pool, std::string const& query);
static void ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path);