mirror of
https://github.com/mangosfour/server.git
synced 2025-12-12 10:37:03 +00:00
[11284] Implement prepared statements for INSERT+DELETE+UPDATE SQL requests. Should improve player save performance + lower MySQL server CPU usage.
Note: PostgreSQL does not have prepared statements implemented using native APIs. Huge thanks to Undergarun, kero99 and Vinolentus. Signed-off-by: Ambal <pogrebniak@gala.net>
This commit is contained in:
parent
d9374d936f
commit
40ef9cbf2f
24 changed files with 1823 additions and 488 deletions
|
|
@ -19,6 +19,31 @@
|
||||||
#ifndef MANGOS_CALLBACK_H
|
#ifndef MANGOS_CALLBACK_H
|
||||||
#define MANGOS_CALLBACK_H
|
#define MANGOS_CALLBACK_H
|
||||||
|
|
||||||
|
//defines to simplify multi param templates code and readablity
|
||||||
|
#define TYPENAMES_1 typename T1
|
||||||
|
#define TYPENAMES_2 TYPENAMES_1, typename T2
|
||||||
|
#define TYPENAMES_3 TYPENAMES_2, typename T3
|
||||||
|
#define TYPENAMES_4 TYPENAMES_3, typename T4
|
||||||
|
#define TYPENAMES_5 TYPENAMES_4, typename T5
|
||||||
|
#define TYPENAMES_6 TYPENAMES_5, typename T6
|
||||||
|
#define TYPENAMES_7 TYPENAMES_6, typename T7
|
||||||
|
#define TYPENAMES_8 TYPENAMES_7, typename T8
|
||||||
|
#define TYPENAMES_9 TYPENAMES_8, typename T9
|
||||||
|
#define TYPENAMES_10 TYPENAMES_9, typename T10
|
||||||
|
|
||||||
|
#define PARAMS_1 T1 param1
|
||||||
|
#define PARAMS_2 PARAMS_1, T2 param2
|
||||||
|
#define PARAMS_3 PARAMS_2, T3 param3
|
||||||
|
#define PARAMS_4 PARAMS_3, T4 param4
|
||||||
|
#define PARAMS_5 PARAMS_4, T5 param5
|
||||||
|
#define PARAMS_6 PARAMS_5, T6 param6
|
||||||
|
#define PARAMS_7 PARAMS_6, T7 param7
|
||||||
|
#define PARAMS_8 PARAMS_7, T8 param8
|
||||||
|
#define PARAMS_9 PARAMS_8, T9 param9
|
||||||
|
#define PARAMS_10 PARAMS_9, T10 param10
|
||||||
|
|
||||||
|
//empty struct to use in templates instead of void type
|
||||||
|
struct null { null() {} };
|
||||||
/// ------------ BASE CLASSES ------------
|
/// ------------ BASE CLASSES ------------
|
||||||
|
|
||||||
namespace MaNGOS
|
namespace MaNGOS
|
||||||
|
|
|
||||||
|
|
@ -478,77 +478,45 @@ void AchievementMgr::DeleteFromDB(ObjectGuid guid)
|
||||||
|
|
||||||
void AchievementMgr::SaveToDB()
|
void AchievementMgr::SaveToDB()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID delComplAchievements ;
|
||||||
|
static SqlStatementID insComplAchievements ;
|
||||||
|
static SqlStatementID delProgress ;
|
||||||
|
static SqlStatementID insProgress ;
|
||||||
|
|
||||||
if(!m_completedAchievements.empty())
|
if(!m_completedAchievements.empty())
|
||||||
{
|
{
|
||||||
bool need_execute = false;
|
//delete existing achievements in the loop
|
||||||
std::ostringstream ssdel;
|
|
||||||
std::ostringstream ssins;
|
|
||||||
for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
|
for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(!iter->second.changed)
|
if(!iter->second.changed)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/// first new/changed record prefix
|
|
||||||
if(!need_execute)
|
|
||||||
{
|
|
||||||
ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
|
|
||||||
ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
|
|
||||||
need_execute = true;
|
|
||||||
}
|
|
||||||
/// next new/changed record prefix
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ssdel << ", ";
|
|
||||||
ssins << ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// new/changed record data
|
|
||||||
ssdel << iter->first;
|
|
||||||
ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
|
|
||||||
|
|
||||||
/// mark as saved in db
|
/// mark as saved in db
|
||||||
iter->second.changed = false;
|
iter->second.changed = false;
|
||||||
}
|
|
||||||
|
|
||||||
if(need_execute)
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delComplAchievements, "DELETE FROM character_achievement WHERE guid = ? AND achievement = ?");
|
||||||
ssdel << ")";
|
stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first);
|
||||||
|
|
||||||
if(need_execute)
|
stmt = CharacterDatabase.CreateStatement(insComplAchievements, "INSERT INTO character_achievement (guid, achievement, date) VALUES (?, ?, ?)");
|
||||||
{
|
stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first, uint64(iter->second.date));
|
||||||
CharacterDatabase.Execute( ssdel.str().c_str() );
|
|
||||||
CharacterDatabase.Execute( ssins.str().c_str() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!m_criteriaProgress.empty())
|
if(!m_criteriaProgress.empty())
|
||||||
{
|
{
|
||||||
/// prepare deleting and insert
|
//insert achievements
|
||||||
bool need_execute_del = false;
|
|
||||||
bool need_execute_ins = false;
|
|
||||||
std::ostringstream ssdel;
|
|
||||||
std::ostringstream ssins;
|
|
||||||
for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
|
for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(!iter->second.changed)
|
if(!iter->second.changed)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// deleted data (including 0 progress state)
|
/// mark as updated in db
|
||||||
{
|
iter->second.changed = false;
|
||||||
/// first new/changed record prefix (for any counter value)
|
|
||||||
if(!need_execute_del)
|
|
||||||
{
|
|
||||||
ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
|
|
||||||
need_execute_del = true;
|
|
||||||
}
|
|
||||||
/// next new/changed record prefix
|
|
||||||
else
|
|
||||||
ssdel << ", ";
|
|
||||||
|
|
||||||
// new/changed record data
|
// new/changed record data
|
||||||
ssdel << iter->first;
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delProgress, "DELETE FROM character_achievement_progress WHERE guid = ? AND criteria = ?");
|
||||||
}
|
stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first);
|
||||||
|
|
||||||
// store data only for real progress, exceptions are timedCriterias, they are also stored for counter == 0
|
|
||||||
bool needSave = iter->second.counter != 0;
|
bool needSave = iter->second.counter != 0;
|
||||||
if (!needSave)
|
if (!needSave)
|
||||||
{
|
{
|
||||||
|
|
@ -558,33 +526,9 @@ void AchievementMgr::SaveToDB()
|
||||||
|
|
||||||
if (needSave)
|
if (needSave)
|
||||||
{
|
{
|
||||||
/// first new/changed record prefix
|
stmt = CharacterDatabase.CreateStatement(insProgress, "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES (?, ?, ?, ?)");
|
||||||
if(!need_execute_ins)
|
stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first, iter->second.counter, uint64(iter->second.date));
|
||||||
{
|
|
||||||
ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
|
|
||||||
need_execute_ins = true;
|
|
||||||
}
|
}
|
||||||
/// next new/changed record prefix
|
|
||||||
else
|
|
||||||
ssins << ", ";
|
|
||||||
|
|
||||||
// new/changed record data
|
|
||||||
ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// mark as updated in db
|
|
||||||
iter->second.changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(need_execute_del) // DELETE ... IN (.... _)_
|
|
||||||
ssdel << ")";
|
|
||||||
|
|
||||||
if(need_execute_del || need_execute_ins)
|
|
||||||
{
|
|
||||||
if(need_execute_del)
|
|
||||||
CharacterDatabase.Execute( ssdel.str().c_str() );
|
|
||||||
if(need_execute_ins)
|
|
||||||
CharacterDatabase.Execute( ssins.str().c_str() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,40 +295,58 @@ void Item::SaveToDB()
|
||||||
{
|
{
|
||||||
case ITEM_NEW:
|
case ITEM_NEW:
|
||||||
{
|
{
|
||||||
std::string text = m_text;
|
static SqlStatementID delItem ;
|
||||||
CharacterDatabase.escape_string(text);
|
static SqlStatementID insItem ;
|
||||||
CharacterDatabase.PExecute( "DELETE FROM item_instance WHERE guid = '%u'", guid );
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delItem, "DELETE FROM item_instance WHERE guid = ?");
|
||||||
|
stmt.PExecute(guid);
|
||||||
|
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << "INSERT INTO item_instance (guid,owner_guid,data,text) VALUES (" << guid << "," << GetOwnerGuid().GetCounter() << ",'";
|
|
||||||
for(uint16 i = 0; i < m_valuesCount; ++i )
|
for(uint16 i = 0; i < m_valuesCount; ++i )
|
||||||
ss << GetUInt32Value(i) << " ";
|
ss << GetUInt32Value(i) << " ";
|
||||||
ss << "', '" << text << "')";
|
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
stmt = CharacterDatabase.CreateStatement(insItem, "INSERT INTO item_instance (guid,owner_guid,data,text) VALUES (?, ?, ?, ?)");
|
||||||
|
stmt.PExecute(guid, GetOwnerGuid().GetCounter(), ss.str().c_str(), m_text.c_str());
|
||||||
} break;
|
} break;
|
||||||
case ITEM_CHANGED:
|
case ITEM_CHANGED:
|
||||||
{
|
{
|
||||||
std::string text = m_text;
|
static SqlStatementID updInstance ;
|
||||||
CharacterDatabase.escape_string(text);
|
static SqlStatementID updGifts ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updInstance, "UPDATE item_instance SET data = ?, owner_guid = ?, text = ? WHERE guid = ?");
|
||||||
|
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << "UPDATE item_instance SET data = '";
|
|
||||||
for(uint16 i = 0; i < m_valuesCount; ++i )
|
for(uint16 i = 0; i < m_valuesCount; ++i )
|
||||||
ss << GetUInt32Value(i) << " ";
|
ss << GetUInt32Value(i) << " ";
|
||||||
ss << "', owner_guid = '" << GetOwnerGuid().GetCounter();
|
|
||||||
ss << "', text = '" << text << "' WHERE guid = '" << guid << "'";
|
|
||||||
|
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
stmt.PExecute(ss.str().c_str(), GetOwnerGuid().GetCounter(), m_text.c_str(), guid);
|
||||||
|
|
||||||
if (HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
|
if (HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
|
||||||
CharacterDatabase.PExecute("UPDATE character_gifts SET guid = '%u' WHERE item_guid = '%u'", GetOwnerGuid().GetCounter(), GetGUIDLow());
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(updGifts, "UPDATE character_gifts SET guid = ? WHERE item_guid = ?");
|
||||||
|
stmt.PExecute(GetOwnerGuid().GetCounter(), GetGUIDLow());
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case ITEM_REMOVED:
|
case ITEM_REMOVED:
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", guid);
|
static SqlStatementID delInst ;
|
||||||
|
static SqlStatementID delGifts ;
|
||||||
|
static SqlStatementID delLoot ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delInst, "DELETE FROM item_instance WHERE guid = ?");
|
||||||
|
stmt.PExecute(guid);
|
||||||
|
|
||||||
if (HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
|
if (HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow());
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delGifts, "DELETE FROM character_gifts WHERE item_guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
}
|
||||||
|
|
||||||
if (HasSavedLoot())
|
if (HasSavedLoot())
|
||||||
CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u'", GetGUIDLow());
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delLoot, "DELETE FROM item_loot WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
}
|
||||||
|
|
||||||
delete this;
|
delete this;
|
||||||
return;
|
return;
|
||||||
|
|
@ -338,17 +356,28 @@ void Item::SaveToDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_lootState == ITEM_LOOT_CHANGED || m_lootState == ITEM_LOOT_REMOVED)
|
if (m_lootState == ITEM_LOOT_CHANGED || m_lootState == ITEM_LOOT_REMOVED)
|
||||||
CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u'", GetGUIDLow());
|
{
|
||||||
|
static SqlStatementID delLoot ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delLoot, "DELETE FROM item_loot WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
}
|
||||||
|
|
||||||
if (m_lootState == ITEM_LOOT_NEW || m_lootState == ITEM_LOOT_CHANGED)
|
if (m_lootState == ITEM_LOOT_NEW || m_lootState == ITEM_LOOT_CHANGED)
|
||||||
{
|
{
|
||||||
if(Player* owner = GetOwner())
|
if(Player* owner = GetOwner())
|
||||||
{
|
{
|
||||||
|
static SqlStatementID saveGold ;
|
||||||
|
static SqlStatementID saveLoot ;
|
||||||
|
|
||||||
// save money as 0 itemid data
|
// save money as 0 itemid data
|
||||||
if (loot.gold)
|
if (loot.gold)
|
||||||
CharacterDatabase.PExecute("INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) "
|
{
|
||||||
"VALUES (%u, %u, 0, %u, 0, 0)",
|
SqlStatement stmt = CharacterDatabase.CreateStatement(saveGold, "INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) VALUES (?, ?, 0, ?, 0, 0)");
|
||||||
GetGUIDLow(), owner->GetGUIDLow(), loot.gold);
|
stmt.PExecute(GetGUIDLow(), owner->GetGUIDLow(), loot.gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(saveLoot, "INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
// save items and quest items (at load its all will added as normal, but this not important for item loot case)
|
// save items and quest items (at load its all will added as normal, but this not important for item loot case)
|
||||||
for (size_t i = 0; i < loot.GetMaxSlotInLootFor(owner); ++i)
|
for (size_t i = 0; i < loot.GetMaxSlotInLootFor(owner); ++i)
|
||||||
|
|
@ -363,9 +392,14 @@ void Item::SaveToDB()
|
||||||
if (!qitem && item->is_blocked)
|
if (!qitem && item->is_blocked)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CharacterDatabase.PExecute("INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) "
|
stmt.addUInt32(GetGUIDLow());
|
||||||
"VALUES (%u, %u, %u, %u, %u, %i)",
|
stmt.addUInt32(owner->GetGUIDLow());
|
||||||
GetGUIDLow(), owner->GetGUIDLow(), item->itemid, item->count, item->randomSuffix, item->randomPropertyId);
|
stmt.addUInt32(item->itemid);
|
||||||
|
stmt.addUInt8(item->count);
|
||||||
|
stmt.addUInt32(item->randomSuffix);
|
||||||
|
stmt.addInt32(item->randomPropertyId);
|
||||||
|
|
||||||
|
stmt.Execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,20 +486,28 @@ bool Item::LoadFromDB(uint32 guidLow, Field *fields, ObjectGuid ownerGuid)
|
||||||
RemoveFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED);
|
RemoveFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED);
|
||||||
need_save = true;
|
need_save = true;
|
||||||
|
|
||||||
|
static SqlStatementID delGifts ;
|
||||||
|
|
||||||
// also cleanup for sure gift table
|
// also cleanup for sure gift table
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow());
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delGifts, "DELETE FROM character_gifts WHERE item_guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (need_save) // normal item changed state set not work at loading
|
if (need_save) // normal item changed state set not work at loading
|
||||||
{
|
{
|
||||||
|
static SqlStatementID updItem ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updItem, "UPDATE item_instance SET data = ?, owner_guid = ? WHERE guid = ?");
|
||||||
|
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << "UPDATE item_instance SET data = '";
|
|
||||||
for(uint16 i = 0; i < m_valuesCount; ++i )
|
for(uint16 i = 0; i < m_valuesCount; ++i )
|
||||||
ss << GetUInt32Value(i) << " ";
|
ss << GetUInt32Value(i) << " ";
|
||||||
ss << "', owner_guid = '" << GetOwnerGuid().GetCounter() << "' WHERE guid = '" << guidLow << "'";
|
|
||||||
|
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
stmt.addString(ss);
|
||||||
|
stmt.addUInt32(GetOwnerGuid().GetCounter());
|
||||||
|
stmt.addUInt32(guidLow);
|
||||||
|
stmt.Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -504,12 +546,18 @@ void Item::LoadLootFromDB(Field *fields)
|
||||||
|
|
||||||
void Item::DeleteFromDB()
|
void Item::DeleteFromDB()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'",GetGUIDLow());
|
static SqlStatementID delItem ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delItem, "DELETE FROM item_instance WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item::DeleteFromInventoryDB()
|
void Item::DeleteFromInventoryDB()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'",GetGUIDLow());
|
static SqlStatementID delInv ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delInv, "DELETE FROM character_inventory WHERE item = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemPrototype const *Item::GetProto() const
|
ItemPrototype const *Item::GetProto() const
|
||||||
|
|
|
||||||
|
|
@ -77,9 +77,19 @@ void MapPersistentState::SaveCreatureRespawnTime(uint32 loguid, time_t t)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CharacterDatabase.BeginTransaction();
|
CharacterDatabase.BeginTransaction();
|
||||||
CharacterDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, m_instanceid);
|
|
||||||
|
static SqlStatementID delSpawnTime ;
|
||||||
|
static SqlStatementID insSpawnTime ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSpawnTime, "DELETE FROM creature_respawn WHERE guid = ? AND instance = ?");
|
||||||
|
stmt.PExecute(loguid, m_instanceid);
|
||||||
|
|
||||||
if(t > sWorld.GetGameTime())
|
if(t > sWorld.GetGameTime())
|
||||||
CharacterDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" UI64FMTD "', '%u' )", loguid, uint64(t), m_instanceid);
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(insSpawnTime, "INSERT INTO creature_respawn VALUES ( ?, ?, ? )");
|
||||||
|
stmt.PExecute(loguid, uint64(t), m_instanceid);
|
||||||
|
}
|
||||||
|
|
||||||
CharacterDatabase.CommitTransaction();
|
CharacterDatabase.CommitTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,9 +102,19 @@ void MapPersistentState::SaveGORespawnTime(uint32 loguid, time_t t)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CharacterDatabase.BeginTransaction();
|
CharacterDatabase.BeginTransaction();
|
||||||
CharacterDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, m_instanceid);
|
|
||||||
|
static SqlStatementID delSpawnTime ;
|
||||||
|
static SqlStatementID insSpawnTime ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSpawnTime, "DELETE FROM gameobject_respawn WHERE guid = ? AND instance = ?");
|
||||||
|
stmt.PExecute(loguid, m_instanceid);
|
||||||
|
|
||||||
if(t > sWorld.GetGameTime())
|
if(t > sWorld.GetGameTime())
|
||||||
CharacterDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" UI64FMTD "', '%u' )", loguid, uint64(t), m_instanceid);
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(insSpawnTime, "INSERT INTO gameobject_respawn VALUES ( ?, ?, ? )");
|
||||||
|
stmt.PExecute(loguid, uint64(t), m_instanceid);
|
||||||
|
}
|
||||||
|
|
||||||
CharacterDatabase.CommitTransaction();
|
CharacterDatabase.CommitTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
165
src/game/Pet.cpp
165
src/game/Pet.cpp
|
|
@ -377,52 +377,65 @@ void Pet::SavePetToDB(PetSaveMode mode)
|
||||||
_SaveAuras();
|
_SaveAuras();
|
||||||
|
|
||||||
uint32 ownerLow = GetOwnerGuid().GetCounter();
|
uint32 ownerLow = GetOwnerGuid().GetCounter();
|
||||||
std::string name = m_name;
|
|
||||||
CharacterDatabase.escape_string(name);
|
|
||||||
// remove current data
|
// remove current data
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", ownerLow, m_charmInfo->GetPetNumber());
|
static SqlStatementID delPet ;
|
||||||
|
static SqlStatementID insPet ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delPet, "DELETE FROM character_pet WHERE owner = ? AND id = ?");
|
||||||
|
stmt.PExecute(ownerLow, m_charmInfo->GetPetNumber());
|
||||||
|
|
||||||
// prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
|
// prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
|
||||||
if (mode <= PET_SAVE_LAST_STABLE_SLOT)
|
if (mode <= PET_SAVE_LAST_STABLE_SLOT)
|
||||||
CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u'",
|
{
|
||||||
PET_SAVE_NOT_IN_SLOT, ownerLow, uint32(mode) );
|
static SqlStatementID updPet ;
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(updPet, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ?");
|
||||||
|
stmt.PExecute(uint32(PET_SAVE_NOT_IN_SLOT), ownerLow, uint32(mode));
|
||||||
|
}
|
||||||
|
|
||||||
// prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
|
// prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
|
||||||
if (getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode > PET_SAVE_LAST_STABLE_SLOT))
|
if (getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode > PET_SAVE_LAST_STABLE_SLOT))
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u')",
|
{
|
||||||
ownerLow, PET_SAVE_AS_CURRENT, PET_SAVE_LAST_STABLE_SLOT);
|
static SqlStatementID del ;
|
||||||
// save pet
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) "
|
|
||||||
<< "VALUES ("
|
|
||||||
<< m_charmInfo->GetPetNumber() << ", "
|
|
||||||
<< GetEntry() << ", "
|
|
||||||
<< ownerLow << ", "
|
|
||||||
<< GetNativeDisplayId() << ", "
|
|
||||||
<< getLevel() << ", "
|
|
||||||
<< GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
|
|
||||||
<< uint32(m_charmInfo->GetReactState()) << ", "
|
|
||||||
<< uint32(mode) << ", '"
|
|
||||||
<< name.c_str() << "', "
|
|
||||||
<< uint32(HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1) << ", "
|
|
||||||
<< (curhealth < 1 ? 1 : curhealth) << ", "
|
|
||||||
<< curmana << ", "
|
|
||||||
<< GetPower(POWER_HAPPINESS) << ", '";
|
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(del, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)");
|
||||||
|
stmt.PExecute(ownerLow, uint32(PET_SAVE_AS_CURRENT), uint32(PET_SAVE_LAST_STABLE_SLOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// save pet
|
||||||
|
SqlStatement savePet = CharacterDatabase.CreateStatement(insPet, "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, "
|
||||||
|
"curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) "
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
savePet.addUInt32(m_charmInfo->GetPetNumber());
|
||||||
|
savePet.addUInt32(GetEntry());
|
||||||
|
savePet.addUInt32(ownerLow);
|
||||||
|
savePet.addUInt32(GetNativeDisplayId());
|
||||||
|
savePet.addUInt32(getLevel());
|
||||||
|
savePet.addUInt32(GetUInt32Value(UNIT_FIELD_PETEXPERIENCE));
|
||||||
|
savePet.addUInt32(uint32(m_charmInfo->GetReactState()));
|
||||||
|
savePet.addUInt32(uint32(mode));
|
||||||
|
savePet.addString(m_name);
|
||||||
|
savePet.addUInt32(uint32(HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1));
|
||||||
|
savePet.addUInt32((curhealth < 1 ? 1 : curhealth));
|
||||||
|
savePet.addUInt32(curmana);
|
||||||
|
savePet.addUInt32(GetPower(POWER_HAPPINESS));
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
for(uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
|
for(uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
|
||||||
{
|
{
|
||||||
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << " "
|
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << " "
|
||||||
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << " ";
|
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << " ";
|
||||||
};
|
};
|
||||||
|
savePet.addString(ss);
|
||||||
|
|
||||||
ss << "', "
|
savePet.addUInt64(uint64(time(NULL)));
|
||||||
<< time(NULL) << ", "
|
savePet.addUInt32(uint32(m_resetTalentsCost));
|
||||||
<< uint32(m_resetTalentsCost) << ", "
|
savePet.addUInt64(uint64(m_resetTalentsTime));
|
||||||
<< uint64(m_resetTalentsTime) << ", "
|
savePet.addUInt32(GetUInt32Value(UNIT_CREATED_BY_SPELL));
|
||||||
<< GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
|
savePet.addUInt32(uint32(getPetType()));
|
||||||
<< uint32(getPetType()) << ")";
|
|
||||||
|
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
savePet.Execute();
|
||||||
CharacterDatabase.CommitTransaction();
|
CharacterDatabase.CommitTransaction();
|
||||||
}
|
}
|
||||||
// delete
|
// delete
|
||||||
|
|
@ -438,11 +451,26 @@ void Pet::DeleteFromDB(uint32 guidlow, bool separate_transaction)
|
||||||
if(separate_transaction)
|
if(separate_transaction)
|
||||||
CharacterDatabase.BeginTransaction();
|
CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
|
static SqlStatementID delPet ;
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow);
|
static SqlStatementID delDeclName ;
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow);
|
static SqlStatementID delAuras ;
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow);
|
static SqlStatementID delSpells ;
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow);
|
static SqlStatementID delSpellCD ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delPet, "DELETE FROM character_pet WHERE id = ?");
|
||||||
|
stmt.PExecute(guidlow);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delDeclName, "DELETE FROM character_pet_declinedname WHERE id = ?");
|
||||||
|
stmt.PExecute(guidlow);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delAuras, "DELETE FROM pet_aura WHERE guid = ?");
|
||||||
|
stmt.PExecute(guidlow);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delSpells, "DELETE FROM pet_spell WHERE guid = ?");
|
||||||
|
stmt.PExecute(guidlow);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delSpellCD, "DELETE FROM pet_spell_cooldown WHERE guid = ?");
|
||||||
|
stmt.PExecute(guidlow);
|
||||||
|
|
||||||
if(separate_transaction)
|
if(separate_transaction)
|
||||||
CharacterDatabase.CommitTransaction();
|
CharacterDatabase.CommitTransaction();
|
||||||
|
|
@ -1126,7 +1154,11 @@ void Pet::_LoadSpellCooldowns()
|
||||||
|
|
||||||
void Pet::_SaveSpellCooldowns()
|
void Pet::_SaveSpellCooldowns()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber());
|
static SqlStatementID delSpellCD ;
|
||||||
|
static SqlStatementID insSpellCD ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSpellCD, "DELETE FROM pet_spell_cooldown WHERE guid = ?");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber());
|
||||||
|
|
||||||
time_t curTime = time(NULL);
|
time_t curTime = time(NULL);
|
||||||
|
|
||||||
|
|
@ -1137,7 +1169,8 @@ void Pet::_SaveSpellCooldowns()
|
||||||
m_CreatureSpellCooldowns.erase(itr++);
|
m_CreatureSpellCooldowns.erase(itr++);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" UI64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
|
stmt = CharacterDatabase.CreateStatement(insSpellCD, "INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES (?, ?, ?)");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1163,6 +1196,9 @@ void Pet::_LoadSpells()
|
||||||
|
|
||||||
void Pet::_SaveSpells()
|
void Pet::_SaveSpells()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID delSpell ;
|
||||||
|
static SqlStatementID insSpell ;
|
||||||
|
|
||||||
for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
|
for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
|
||||||
{
|
{
|
||||||
++next;
|
++next;
|
||||||
|
|
@ -1174,15 +1210,26 @@ void Pet::_SaveSpells()
|
||||||
switch(itr->second.state)
|
switch(itr->second.state)
|
||||||
{
|
{
|
||||||
case PETSPELL_REMOVED:
|
case PETSPELL_REMOVED:
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSpell, "DELETE FROM pet_spell WHERE guid = ? and spell = ?");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber(), itr->first);
|
||||||
m_spells.erase(itr);
|
m_spells.erase(itr);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
case PETSPELL_CHANGED:
|
case PETSPELL_CHANGED:
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
|
{
|
||||||
CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second.active);
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSpell, "DELETE FROM pet_spell WHERE guid = ? and spell = ?");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber(), itr->first);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(insSpell, "INSERT INTO pet_spell (guid,spell,active) VALUES (?, ?, ?)");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber(), itr->first, uint32(itr->second.active));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PETSPELL_NEW:
|
case PETSPELL_NEW:
|
||||||
CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second.active);
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insSpell, "INSERT INTO pet_spell (guid,spell,active) VALUES (?, ?, ?)");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber(), itr->first, uint32(itr->second.active));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PETSPELL_UNCHANGED:
|
case PETSPELL_UNCHANGED:
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1286,7 +1333,11 @@ void Pet::_LoadAuras(uint32 timediff)
|
||||||
|
|
||||||
void Pet::_SaveAuras()
|
void Pet::_SaveAuras()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", m_charmInfo->GetPetNumber());
|
static SqlStatementID delAuras ;
|
||||||
|
static SqlStatementID insAuras ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delAuras, "DELETE FROM pet_aura WHERE guid = ?");
|
||||||
|
stmt.PExecute(m_charmInfo->GetPetNumber());
|
||||||
|
|
||||||
SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap();
|
SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap();
|
||||||
|
|
||||||
|
|
@ -1341,13 +1392,27 @@ void Pet::_SaveAuras()
|
||||||
if (!effIndexMask)
|
if (!effIndexMask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CharacterDatabase.PExecute("INSERT INTO pet_aura (guid, caster_guid, item_guid, spell, stackcount, remaincharges, basepoints0, basepoints1, basepoints2, maxduration0, maxduration1, maxduration2, remaintime0, remaintime1, remaintime2, effIndexMask) VALUES "
|
stmt = CharacterDatabase.CreateStatement(insAuras, "INSERT INTO pet_aura (guid, caster_guid, item_guid, spell, stackcount, remaincharges, basepoints0, basepoints1, basepoints2, maxduration0, maxduration1, maxduration2, remaintime0, remaintime1, remaintime2, effIndexMask) "
|
||||||
"('%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%u')",
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
m_charmInfo->GetPetNumber(), holder->GetCasterGuid().GetRawValue(), holder->GetCastItemGuid().GetCounter(), holder->GetId(), holder->GetStackAmount(), holder->GetAuraCharges(),
|
|
||||||
damage[EFFECT_INDEX_0], damage[EFFECT_INDEX_1], damage[EFFECT_INDEX_2],
|
stmt.addUInt32(m_charmInfo->GetPetNumber());
|
||||||
maxduration[EFFECT_INDEX_0], maxduration[EFFECT_INDEX_1], maxduration[EFFECT_INDEX_2],
|
stmt.addUInt64(holder->GetCasterGuid().GetRawValue());
|
||||||
remaintime[EFFECT_INDEX_0], remaintime[EFFECT_INDEX_1], remaintime[EFFECT_INDEX_2],
|
stmt.addUInt32(holder->GetCastItemGuid().GetCounter());
|
||||||
effIndexMask);
|
stmt.addUInt32(holder->GetId());
|
||||||
|
stmt.addUInt32(holder->GetStackAmount());
|
||||||
|
stmt.addUInt8(holder->GetAuraCharges());
|
||||||
|
|
||||||
|
for (int i = EFFECT_INDEX_0; i <= EFFECT_INDEX_2; ++i )
|
||||||
|
stmt.addInt32(damage[i]);
|
||||||
|
|
||||||
|
for (int i = EFFECT_INDEX_0; i <= EFFECT_INDEX_2; ++i )
|
||||||
|
stmt.addInt32(maxduration[i]);
|
||||||
|
|
||||||
|
for (int i = EFFECT_INDEX_0; i <= EFFECT_INDEX_2; ++i )
|
||||||
|
stmt.addInt32(remaintime[i]);
|
||||||
|
|
||||||
|
stmt.addUInt32(effIndexMask);
|
||||||
|
stmt.Execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,10 +267,8 @@ uint32 PlayerTaxi::GetCurrentTaxiPath() const
|
||||||
|
|
||||||
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
|
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
|
||||||
{
|
{
|
||||||
ss << "'";
|
|
||||||
for(int i = 0; i < TaxiMaskSize; ++i)
|
for(int i = 0; i < TaxiMaskSize; ++i)
|
||||||
ss << taxi.m_taximask[i] << " ";
|
ss << taxi.m_taximask[i] << " ";
|
||||||
ss << "'";
|
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3714,14 +3712,15 @@ void Player::_LoadSpellCooldowns(QueryResult *result)
|
||||||
|
|
||||||
void Player::_SaveSpellCooldowns()
|
void Player::_SaveSpellCooldowns()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
|
static SqlStatementID deleteSpellCooldown ;
|
||||||
|
static SqlStatementID insertSpellCooldown ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteSpellCooldown, "DELETE FROM character_spell_cooldown WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
time_t curTime = time(NULL);
|
time_t curTime = time(NULL);
|
||||||
time_t infTime = curTime + infinityCooldownDelayCheck;
|
time_t infTime = curTime + infinityCooldownDelayCheck;
|
||||||
|
|
||||||
bool first_round = true;
|
|
||||||
std::ostringstream ss;
|
|
||||||
|
|
||||||
// remove outdated and save active
|
// remove outdated and save active
|
||||||
for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
|
for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
|
||||||
{
|
{
|
||||||
|
|
@ -3729,24 +3728,13 @@ void Player::_SaveSpellCooldowns()
|
||||||
m_spellCooldowns.erase(itr++);
|
m_spellCooldowns.erase(itr++);
|
||||||
else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
|
else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
|
||||||
{
|
{
|
||||||
if (first_round)
|
stmt = CharacterDatabase.CreateStatement(insertSpellCooldown, "INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES( ?, ?, ?, ?)");
|
||||||
{
|
stmt.PExecute(GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end));
|
||||||
ss << "INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ";
|
|
||||||
first_round = false;
|
|
||||||
}
|
|
||||||
// next new/changed record prefix
|
|
||||||
else
|
|
||||||
ss << ", ";
|
|
||||||
ss << "(" << GetGUIDLow() << "," << itr->first << "," << itr->second.itemid << "," << uint64(itr->second.end) << ")";
|
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
++itr;
|
++itr;
|
||||||
|
|
||||||
}
|
}
|
||||||
// if something changed execute
|
|
||||||
if (!first_round)
|
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Player::resetTalentsCost() const
|
uint32 Player::resetTalentsCost() const
|
||||||
|
|
@ -11472,7 +11460,12 @@ void Player::DestroyItem( uint8 bag, uint8 slot, bool update )
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
|
if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
|
{
|
||||||
|
static SqlStatementID delGifts ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delGifts, "DELETE FROM character_gifts WHERE item_guid = ?");
|
||||||
|
stmt.PExecute(pItem->GetGUIDLow());
|
||||||
|
}
|
||||||
|
|
||||||
RemoveEnchantmentDurations(pItem);
|
RemoveEnchantmentDurations(pItem);
|
||||||
RemoveItemDurations(pItem);
|
RemoveItemDurations(pItem);
|
||||||
|
|
@ -17091,144 +17084,156 @@ void Player::SaveToDB()
|
||||||
|
|
||||||
CharacterDatabase.BeginTransaction();
|
CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow());
|
static SqlStatementID delChar ;
|
||||||
|
static SqlStatementID insChar ;
|
||||||
|
|
||||||
std::string sql_name = m_name;
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delChar, "DELETE FROM characters WHERE guid = ?");
|
||||||
CharacterDatabase.escape_string(sql_name);
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
std::ostringstream ss;
|
SqlStatement uberInsert = CharacterDatabase.CreateStatement(insChar, "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
|
||||||
ss << "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
|
|
||||||
"map, dungeon_difficulty, position_x, position_y, position_z, orientation, "
|
"map, dungeon_difficulty, position_x, position_y, position_z, orientation, "
|
||||||
"taximask, online, cinematic, "
|
"taximask, online, cinematic, "
|
||||||
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
|
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
|
||||||
"trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
|
"trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
|
||||||
"death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, "
|
"death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, "
|
||||||
"todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, "
|
"todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, "
|
||||||
"power4, power5, power6, power7, specCount, activeSpec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars) VALUES ("
|
"power4, power5, power6, power7, specCount, activeSpec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars) "
|
||||||
<< GetGUIDLow() << ", "
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
|
||||||
<< GetSession()->GetAccountId() << ", '"
|
"?, ?, ?, ?, ?, ?, "
|
||||||
<< sql_name << "', "
|
"?, ?, ?, "
|
||||||
<< (uint32)getRace() << ", "
|
"?, ?, ?, ?, ?, ?, ?, "
|
||||||
<< (uint32)getClass() << ", "
|
"?, ?, ?, ?, ?, ?, ?, ?, ?, "
|
||||||
<< (uint32)getGender() << ", "
|
"?, ?, ?, ?, ?, ?, ?, "
|
||||||
<< getLevel() << ", "
|
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
|
||||||
<< GetUInt32Value(PLAYER_XP) << ", "
|
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ");
|
||||||
<< GetMoney() << ", "
|
|
||||||
<< GetUInt32Value(PLAYER_BYTES) << ", "
|
uberInsert.addUInt32(GetGUIDLow());
|
||||||
<< GetUInt32Value(PLAYER_BYTES_2) << ", "
|
uberInsert.addUInt32(GetSession()->GetAccountId());
|
||||||
<< GetUInt32Value(PLAYER_FLAGS) << ", ";
|
uberInsert.addString(m_name);
|
||||||
|
uberInsert.addUInt8(getRace());
|
||||||
|
uberInsert.addUInt8(getClass());
|
||||||
|
uberInsert.addUInt8(getGender());
|
||||||
|
uberInsert.addUInt32(getLevel());
|
||||||
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_XP));
|
||||||
|
uberInsert.addUInt32(GetMoney());
|
||||||
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_BYTES));
|
||||||
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_BYTES_2));
|
||||||
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_FLAGS));
|
||||||
|
|
||||||
if(!IsBeingTeleported())
|
if(!IsBeingTeleported())
|
||||||
{
|
{
|
||||||
ss << GetMapId() << ", "
|
uberInsert.addUInt32(GetMapId());
|
||||||
<< (uint32)GetDungeonDifficulty() << ", "
|
uberInsert.addUInt32(uint32(GetDungeonDifficulty()));
|
||||||
<< finiteAlways(GetPositionX()) << ", "
|
uberInsert.addFloat(finiteAlways(GetPositionX()));
|
||||||
<< finiteAlways(GetPositionY()) << ", "
|
uberInsert.addFloat(finiteAlways(GetPositionY()));
|
||||||
<< finiteAlways(GetPositionZ()) << ", "
|
uberInsert.addFloat(finiteAlways(GetPositionZ()));
|
||||||
<< finiteAlways(GetOrientation()) << ", ";
|
uberInsert.addFloat(finiteAlways(GetOrientation()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ss << GetTeleportDest().mapid << ", "
|
uberInsert.addUInt32(GetTeleportDest().mapid);
|
||||||
<< (uint32)GetDungeonDifficulty() << ", "
|
uberInsert.addUInt32(uint32(GetDungeonDifficulty()));
|
||||||
<< finiteAlways(GetTeleportDest().coord_x) << ", "
|
uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_x));
|
||||||
<< finiteAlways(GetTeleportDest().coord_y) << ", "
|
uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_y));
|
||||||
<< finiteAlways(GetTeleportDest().coord_z) << ", "
|
uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_z));
|
||||||
<< finiteAlways(GetTeleportDest().orientation) << ", ";
|
uberInsert.addFloat(finiteAlways(GetTeleportDest().orientation));
|
||||||
}
|
}
|
||||||
|
|
||||||
ss << m_taxi << ", "; // string with TaxiMaskSize numbers
|
std::ostringstream ss;
|
||||||
|
ss << m_taxi; // string with TaxiMaskSize numbers
|
||||||
|
uberInsert.addString(ss);
|
||||||
|
|
||||||
ss << (IsInWorld() ? 1 : 0) << ", ";
|
uberInsert.addUInt32(IsInWorld() ? 1 : 0);
|
||||||
|
|
||||||
ss << m_cinematic << ", ";
|
uberInsert.addUInt32(m_cinematic);
|
||||||
|
|
||||||
ss << m_Played_time[PLAYED_TIME_TOTAL] << ", ";
|
uberInsert.addUInt32(m_Played_time[PLAYED_TIME_TOTAL]);
|
||||||
ss << m_Played_time[PLAYED_TIME_LEVEL] << ", ";
|
uberInsert.addUInt32(m_Played_time[PLAYED_TIME_LEVEL]);
|
||||||
|
|
||||||
ss << finiteAlways(m_rest_bonus) << ", ";
|
uberInsert.addFloat(finiteAlways(m_rest_bonus));
|
||||||
ss << (uint64)time(NULL) << ", ";
|
uberInsert.addUInt64(uint64(time(NULL)));
|
||||||
ss << (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0) << ", ";
|
uberInsert.addUInt32(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0);
|
||||||
//save, far from tavern/city
|
//save, far from tavern/city
|
||||||
//save, but in tavern/city
|
//save, but in tavern/city
|
||||||
ss << m_resetTalentsCost << ", ";
|
uberInsert.addUInt32(m_resetTalentsCost);
|
||||||
ss << (uint64)m_resetTalentsTime << ", ";
|
uberInsert.addUInt64(uint64(m_resetTalentsTime));
|
||||||
|
|
||||||
ss << finiteAlways(m_movementInfo.GetTransportPos()->x) << ", ";
|
uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->x));
|
||||||
ss << finiteAlways(m_movementInfo.GetTransportPos()->y) << ", ";
|
uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->y));
|
||||||
ss << finiteAlways(m_movementInfo.GetTransportPos()->z) << ", ";
|
uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->z));
|
||||||
ss << finiteAlways(m_movementInfo.GetTransportPos()->o) << ", ";
|
uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->o));
|
||||||
if (m_transport)
|
if (m_transport)
|
||||||
ss << m_transport->GetGUIDLow();
|
uberInsert.addUInt32(m_transport->GetGUIDLow());
|
||||||
else
|
else
|
||||||
ss << "0";
|
uberInsert.addUInt32(0);
|
||||||
ss << ", ";
|
|
||||||
|
|
||||||
ss << m_ExtraFlags << ", ";
|
uberInsert.addUInt32(m_ExtraFlags);
|
||||||
|
|
||||||
ss << uint32(m_stableSlots) << ", "; // to prevent save uint8 as char
|
uberInsert.addUInt32(uint32(m_stableSlots)); // to prevent save uint8 as char
|
||||||
|
|
||||||
ss << uint32(m_atLoginFlags) << ", ";
|
uberInsert.addUInt32(uint32(m_atLoginFlags));
|
||||||
|
|
||||||
ss << (IsInWorld() ? GetZoneId() : GetCachedZoneId()) << ", ";
|
uberInsert.addUInt32(IsInWorld() ? GetZoneId() : GetCachedZoneId());
|
||||||
|
|
||||||
ss << (uint64)m_deathExpireTime << ", '";
|
uberInsert.addUInt64(uint64(m_deathExpireTime));
|
||||||
|
|
||||||
ss << m_taxi.SaveTaxiDestinationsToString() << "', ";
|
ss << m_taxi.SaveTaxiDestinationsToString(); //string
|
||||||
|
uberInsert.addString(ss);
|
||||||
|
|
||||||
ss << GetArenaPoints() << ", ";
|
uberInsert.addUInt32(GetArenaPoints());
|
||||||
|
|
||||||
ss << GetHonorPoints() << ", ";
|
uberInsert.addUInt32(GetHonorPoints());
|
||||||
|
|
||||||
ss << GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION) << ", ";
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION) );
|
||||||
|
|
||||||
ss << GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION) << ", ";
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION));
|
||||||
|
|
||||||
ss << GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS) << ", ";
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
|
||||||
|
|
||||||
ss << GetUInt16Value(PLAYER_FIELD_KILLS, 0) << ", ";
|
uberInsert.addUInt16(GetUInt16Value(PLAYER_FIELD_KILLS, 0));
|
||||||
|
|
||||||
ss << GetUInt16Value(PLAYER_FIELD_KILLS, 1) << ", ";
|
uberInsert.addUInt16(GetUInt16Value(PLAYER_FIELD_KILLS, 1));
|
||||||
|
|
||||||
ss << GetUInt32Value(PLAYER_CHOSEN_TITLE) << ", ";
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_CHOSEN_TITLE));
|
||||||
|
|
||||||
ss << GetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES) << ", ";
|
uberInsert.addUInt64(GetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES));
|
||||||
|
|
||||||
// FIXME: at this moment send to DB as unsigned, including unit32(-1)
|
// FIXME: at this moment send to DB as unsigned, including unit32(-1)
|
||||||
ss << GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX) << ", ";
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX));
|
||||||
|
|
||||||
ss << (uint16)(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE) << ", ";
|
uberInsert.addUInt16(uint16(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
|
||||||
|
|
||||||
ss << GetHealth();
|
uberInsert.addUInt32(GetHealth());
|
||||||
|
|
||||||
for(uint32 i = 0; i < MAX_POWERS; ++i)
|
for(uint32 i = 0; i < MAX_POWERS; ++i)
|
||||||
ss << "," << GetPower(Powers(i));
|
uberInsert.addUInt32(GetPower(Powers(i)));
|
||||||
|
|
||||||
ss << ", ";
|
uberInsert.addUInt32(uint32(m_specsCount));
|
||||||
ss << uint32(m_specsCount) << ", ";
|
uberInsert.addUInt32(uint32(m_activeSpec));
|
||||||
ss << uint32(m_activeSpec) << ", '";
|
|
||||||
for(uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i )
|
for(uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i ) //string
|
||||||
{
|
{
|
||||||
ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << " ";
|
ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << " ";
|
||||||
}
|
}
|
||||||
|
uberInsert.addString(ss);
|
||||||
|
|
||||||
ss << "', '";
|
for(uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i ) //string
|
||||||
for(uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i )
|
|
||||||
{
|
{
|
||||||
ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << " ";
|
ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << " ";
|
||||||
}
|
}
|
||||||
|
uberInsert.addString(ss);
|
||||||
|
|
||||||
ss << "',";
|
uberInsert.addUInt32(GetUInt32Value(PLAYER_AMMO_ID));
|
||||||
ss << GetUInt32Value(PLAYER_AMMO_ID) << ", '";
|
|
||||||
for(uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i )
|
for(uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i ) //string
|
||||||
{
|
{
|
||||||
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << " ";
|
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << " ";
|
||||||
}
|
}
|
||||||
ss << "',";
|
uberInsert.addString(ss);
|
||||||
ss << uint32(GetByteValue(PLAYER_FIELD_BYTES, 2));
|
|
||||||
ss << ")";
|
|
||||||
|
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
uberInsert.addUInt32(uint32(GetByteValue(PLAYER_FIELD_BYTES, 2)));
|
||||||
|
|
||||||
|
uberInsert.Execute();
|
||||||
|
|
||||||
if (m_mailsUpdated) //save mails only when needed
|
if (m_mailsUpdated) //save mails only when needed
|
||||||
_SaveMail();
|
_SaveMail();
|
||||||
|
|
@ -17272,11 +17277,18 @@ void Player::SaveInventoryAndGoldToDB()
|
||||||
|
|
||||||
void Player::SaveGoldToDB()
|
void Player::SaveGoldToDB()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("UPDATE characters SET money = '%u' WHERE guid = '%u'", GetMoney(), GetGUIDLow());
|
static SqlStatementID updateGold ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updateGold, "UPDATE characters SET money = ? WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetMoney(), GetGUIDLow());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::_SaveActions()
|
void Player::_SaveActions()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID insertAction ;
|
||||||
|
static SqlStatementID updateAction ;
|
||||||
|
static SqlStatementID deleteAction ;
|
||||||
|
|
||||||
for(int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
|
for(int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
|
||||||
{
|
{
|
||||||
for(ActionButtonList::iterator itr = m_actionButtons[i].begin(); itr != m_actionButtons[i].end(); )
|
for(ActionButtonList::iterator itr = m_actionButtons[i].begin(); itr != m_actionButtons[i].end(); )
|
||||||
|
|
@ -17284,20 +17296,40 @@ void Player::_SaveActions()
|
||||||
switch (itr->second.uState)
|
switch (itr->second.uState)
|
||||||
{
|
{
|
||||||
case ACTIONBUTTON_NEW:
|
case ACTIONBUTTON_NEW:
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_action (guid,spec, button,action,type) VALUES ('%u', '%u', '%u', '%u', '%u')",
|
{
|
||||||
GetGUIDLow(), i, (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType() );
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insertAction, "INSERT INTO character_action (guid,spec, button,action,type) VALUES (?, ?, ?, ?, ?)");
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(i);
|
||||||
|
stmt.addUInt32(uint32(itr->first));
|
||||||
|
stmt.addUInt32(itr->second.GetAction());
|
||||||
|
stmt.addUInt32(uint32(itr->second.GetType()));
|
||||||
|
stmt.Execute();
|
||||||
itr->second.uState = ACTIONBUTTON_UNCHANGED;
|
itr->second.uState = ACTIONBUTTON_UNCHANGED;
|
||||||
++itr;
|
++itr;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ACTIONBUTTON_CHANGED:
|
case ACTIONBUTTON_CHANGED:
|
||||||
CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u' WHERE guid= '%u' AND button= '%u' AND spec = '%u'",
|
{
|
||||||
(uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first, i );
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updateAction, "UPDATE character_action SET action = ?, type = ? WHERE guid = ? AND button = ? AND spec = ?");
|
||||||
|
stmt.addUInt32(itr->second.GetAction());
|
||||||
|
stmt.addUInt32(uint32(itr->second.GetType()));
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(uint32(itr->first));
|
||||||
|
stmt.addUInt32(i);
|
||||||
|
stmt.Execute();
|
||||||
itr->second.uState = ACTIONBUTTON_UNCHANGED;
|
itr->second.uState = ACTIONBUTTON_UNCHANGED;
|
||||||
++itr;
|
++itr;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ACTIONBUTTON_DELETED:
|
case ACTIONBUTTON_DELETED:
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' AND button = '%u' AND spec = '%u'", GetGUIDLow(), (uint32)itr->first, i);
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteAction, "DELETE FROM character_action WHERE guid = ? AND button = ? AND spec = ?");
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(uint32(itr->first));
|
||||||
|
stmt.addUInt32(i);
|
||||||
|
stmt.Execute();
|
||||||
m_actionButtons[i].erase(itr++);
|
m_actionButtons[i].erase(itr++);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
++itr;
|
++itr;
|
||||||
|
|
@ -17309,13 +17341,21 @@ void Player::_SaveActions()
|
||||||
|
|
||||||
void Player::_SaveAuras()
|
void Player::_SaveAuras()
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",GetGUIDLow());
|
static SqlStatementID deleteAuras ;
|
||||||
|
static SqlStatementID insertAuras ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteAuras, "DELETE FROM character_aura WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap();
|
SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap();
|
||||||
|
|
||||||
if (auraHolders.empty())
|
if (auraHolders.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(insertAuras, "INSERT INTO character_aura (guid, caster_guid, item_guid, spell, stackcount, "
|
||||||
|
"remaincharges, basepoints0,basepoints1, basepoints2, maxduration0, maxduration1, maxduration2, remaintime0, remaintime1, remaintime2, effIndexMask) "
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
for(SpellAuraHolderMap::const_iterator itr = auraHolders.begin(); itr != auraHolders.end(); ++itr)
|
for(SpellAuraHolderMap::const_iterator itr = auraHolders.begin(); itr != auraHolders.end(); ++itr)
|
||||||
{
|
{
|
||||||
SpellAuraHolder *holder = itr->second;
|
SpellAuraHolder *holder = itr->second;
|
||||||
|
|
@ -17350,19 +17390,29 @@ void Player::_SaveAuras()
|
||||||
if (!effIndexMask)
|
if (!effIndexMask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_aura (guid, caster_guid, item_guid, spell, stackcount, remaincharges, basepoints0, basepoints1, basepoints2, maxduration0, maxduration1, maxduration2, remaintime0, remaintime1, remaintime2, effIndexMask) VALUES "
|
stmt.addUInt32(GetGUIDLow());
|
||||||
"('%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%u')",
|
stmt.addUInt64(holder->GetCasterGuid().GetRawValue());
|
||||||
GetGUIDLow(), holder->GetCasterGuid().GetRawValue(), holder->GetCastItemGuid().GetCounter(), holder->GetId(), holder->GetStackAmount(), holder->GetAuraCharges(),
|
stmt.addUInt32(holder->GetCastItemGuid().GetCounter());
|
||||||
damage[EFFECT_INDEX_0], damage[EFFECT_INDEX_1], damage[EFFECT_INDEX_2],
|
stmt.addUInt32(holder->GetId());
|
||||||
maxduration[EFFECT_INDEX_0], maxduration[EFFECT_INDEX_1], maxduration[EFFECT_INDEX_2],
|
stmt.addUInt32(holder->GetStackAmount());
|
||||||
remaintime[EFFECT_INDEX_0], remaintime[EFFECT_INDEX_1], remaintime[EFFECT_INDEX_2],
|
stmt.addUInt8(holder->GetAuraCharges());
|
||||||
effIndexMask);
|
for (int k = 0; k < MAX_EFFECT_INDEX; ++k)
|
||||||
|
stmt.addInt32(damage[k]);
|
||||||
|
for (int k = 0; k < MAX_EFFECT_INDEX; ++k)
|
||||||
|
stmt.addInt32(maxduration[k]);
|
||||||
|
for (int k = 0; k < MAX_EFFECT_INDEX; ++k)
|
||||||
|
stmt.addInt32(remaintime[k]);
|
||||||
|
stmt.addUInt32(effIndexMask);
|
||||||
|
stmt.Execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::_SaveGlyphs()
|
void Player::_SaveGlyphs()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID insertGlyph ;
|
||||||
|
static SqlStatementID updateGlyph ;
|
||||||
|
static SqlStatementID deleteGlyph ;
|
||||||
|
|
||||||
for (uint8 spec = 0; spec < m_specsCount; ++spec)
|
for (uint8 spec = 0; spec < m_specsCount; ++spec)
|
||||||
{
|
{
|
||||||
|
|
@ -17371,13 +17421,22 @@ void Player::_SaveGlyphs()
|
||||||
switch(m_glyphs[spec][slot].uState)
|
switch(m_glyphs[spec][slot].uState)
|
||||||
{
|
{
|
||||||
case GLYPH_NEW:
|
case GLYPH_NEW:
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_glyphs (guid, spec, slot, glyph) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), spec, slot, m_glyphs[spec][slot].GetId());
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insertGlyph, "INSERT INTO character_glyphs (guid, spec, slot, glyph) VALUES (?, ?, ?, ?)");
|
||||||
|
stmt.PExecute(GetGUIDLow(), spec, slot, m_glyphs[spec][slot].GetId());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GLYPH_CHANGED:
|
case GLYPH_CHANGED:
|
||||||
CharacterDatabase.PExecute("UPDATE character_glyphs SET glyph = '%u' WHERE guid='%u' AND spec = '%u' AND slot = '%u'", m_glyphs[spec][slot].GetId(), GetGUIDLow(), spec, slot);
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updateGlyph, "UPDATE character_glyphs SET glyph = ? WHERE guid = ? AND spec = ? AND slot = ?");
|
||||||
|
stmt.PExecute(m_glyphs[spec][slot].GetId(), GetGUIDLow(), spec, slot);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GLYPH_DELETED:
|
case GLYPH_DELETED:
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid='%u' AND spec = '%u' AND slot = '%u'",GetGUIDLow(), spec, slot);
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteGlyph, "DELETE FROM character_glyphs WHERE guid = ? AND spec = ? AND slot = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow(), spec, slot);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GLYPH_UNCHANGED:
|
case GLYPH_UNCHANGED:
|
||||||
break;
|
break;
|
||||||
|
|
@ -17395,8 +17454,16 @@ void Player::_SaveInventory()
|
||||||
{
|
{
|
||||||
Item *item = m_items[i];
|
Item *item = m_items[i];
|
||||||
if (!item || item->GetState() == ITEM_NEW) continue;
|
if (!item || item->GetState() == ITEM_NEW) continue;
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
|
|
||||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item->GetGUIDLow());
|
static SqlStatementID delInv ;
|
||||||
|
static SqlStatementID delItemInst ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delInv, "DELETE FROM character_inventory WHERE item = ?");
|
||||||
|
stmt.PExecute(item->GetGUIDLow());
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(delItemInst, "DELETE FROM item_instance WHERE guid = ?");
|
||||||
|
stmt.PExecute(item->GetGUIDLow());
|
||||||
|
|
||||||
m_items[i]->FSetState(ITEM_NEW);
|
m_items[i]->FSetState(ITEM_NEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17436,6 +17503,10 @@ void Player::_SaveInventory()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SqlStatementID insertInventory ;
|
||||||
|
static SqlStatementID updateInventory ;
|
||||||
|
static SqlStatementID deleteInventory ;
|
||||||
|
|
||||||
for(size_t i = 0; i < m_itemUpdateQueue.size(); ++i)
|
for(size_t i = 0; i < m_itemUpdateQueue.size(); ++i)
|
||||||
{
|
{
|
||||||
Item *item = m_itemUpdateQueue[i];
|
Item *item = m_itemUpdateQueue[i];
|
||||||
|
|
@ -17447,13 +17518,32 @@ void Player::_SaveInventory()
|
||||||
switch(item->GetState())
|
switch(item->GetState())
|
||||||
{
|
{
|
||||||
case ITEM_NEW:
|
case ITEM_NEW:
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES ('%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetGUIDLow(), item->GetEntry());
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insertInventory, "INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES (?, ?, ?, ?, ?)");
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(bag_guid);
|
||||||
|
stmt.addUInt8(item->GetSlot());
|
||||||
|
stmt.addUInt32(item->GetGUIDLow());
|
||||||
|
stmt.addUInt32(item->GetEntry());
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ITEM_CHANGED:
|
case ITEM_CHANGED:
|
||||||
CharacterDatabase.PExecute("UPDATE character_inventory SET guid='%u', bag='%u', slot='%u', item_template='%u' WHERE item='%u'", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetEntry(), item->GetGUIDLow());
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updateInventory, "UPDATE character_inventory SET guid = ?, bag = ?, slot = ?, item_template = ? WHERE item = ?");
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(bag_guid);
|
||||||
|
stmt.addUInt8(item->GetSlot());
|
||||||
|
stmt.addUInt32(item->GetEntry());
|
||||||
|
stmt.addUInt32(item->GetGUIDLow());
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ITEM_REMOVED:
|
case ITEM_REMOVED:
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteInventory, "DELETE FROM character_inventory WHERE item = ?");
|
||||||
|
stmt.PExecute(item->GetGUIDLow());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ITEM_UNCHANGED:
|
case ITEM_UNCHANGED:
|
||||||
break;
|
break;
|
||||||
|
|
@ -17466,17 +17556,35 @@ void Player::_SaveInventory()
|
||||||
|
|
||||||
void Player::_SaveMail()
|
void Player::_SaveMail()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID updateMail ;
|
||||||
|
static SqlStatementID deleteMailItems ;
|
||||||
|
|
||||||
|
static SqlStatementID deleteItem ;
|
||||||
|
static SqlStatementID deleteMain ;
|
||||||
|
static SqlStatementID deleteItems ;
|
||||||
|
|
||||||
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
|
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
|
||||||
{
|
{
|
||||||
Mail *m = (*itr);
|
Mail *m = (*itr);
|
||||||
if (m->state == MAIL_STATE_CHANGED)
|
if (m->state == MAIL_STATE_CHANGED)
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("UPDATE mail SET has_items = '%u',expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',money = '%u',cod = '%u',checked = '%u' WHERE id = '%u'",
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updateMail, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?");
|
||||||
m->HasItems() ? 1 : 0, (uint64)m->expire_time, (uint64)m->deliver_time, m->money, m->COD, m->checked, m->messageID);
|
stmt.addUInt32(m->HasItems() ? 1 : 0);
|
||||||
|
stmt.addUInt64(uint64(m->expire_time));
|
||||||
|
stmt.addUInt64(uint64(m->deliver_time));
|
||||||
|
stmt.addUInt32(m->money);
|
||||||
|
stmt.addUInt32(m->COD);
|
||||||
|
stmt.addUInt32(m->checked);
|
||||||
|
stmt.addUInt32(m->messageID);
|
||||||
|
stmt.Execute();
|
||||||
|
|
||||||
if(m->removedItems.size())
|
if(m->removedItems.size())
|
||||||
{
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(deleteMailItems, "DELETE FROM mail_items WHERE item_guid = ?");
|
||||||
|
|
||||||
for(std::vector<uint32>::const_iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
|
for(std::vector<uint32>::const_iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
|
||||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", *itr2);
|
stmt.PExecute(*itr2);
|
||||||
|
|
||||||
m->removedItems.clear();
|
m->removedItems.clear();
|
||||||
}
|
}
|
||||||
m->state = MAIL_STATE_UNCHANGED;
|
m->state = MAIL_STATE_UNCHANGED;
|
||||||
|
|
@ -17484,11 +17592,17 @@ void Player::_SaveMail()
|
||||||
else if (m->state == MAIL_STATE_DELETED)
|
else if (m->state == MAIL_STATE_DELETED)
|
||||||
{
|
{
|
||||||
if (m->HasItems())
|
if (m->HasItems())
|
||||||
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteItem, "DELETE FROM item_instance WHERE guid = ?");
|
||||||
for(MailItemInfoVec::const_iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
|
for(MailItemInfoVec::const_iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
|
||||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
|
stmt.PExecute(itr2->item_guid);
|
||||||
|
}
|
||||||
|
|
||||||
CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
|
SqlStatement stmt = CharacterDatabase.CreateStatement(deleteMain, "DELETE FROM mail WHERE id = ?");
|
||||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", m->messageID);
|
stmt.PExecute(m->messageID);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(deleteItems, "DELETE FROM mail_items WHERE mail_id = ?");
|
||||||
|
stmt.PExecute(m->messageID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17511,19 +17625,50 @@ void Player::_SaveMail()
|
||||||
|
|
||||||
void Player::_SaveQuestStatus()
|
void Player::_SaveQuestStatus()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID insertQuestStatus ;
|
||||||
|
|
||||||
|
static SqlStatementID updateQuestStatus ;
|
||||||
|
|
||||||
// we don't need transactions here.
|
// we don't need transactions here.
|
||||||
for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
|
for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
|
||||||
{
|
{
|
||||||
switch (i->second.uState)
|
switch (i->second.uState)
|
||||||
{
|
{
|
||||||
case QUEST_NEW :
|
case QUEST_NEW :
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
|
{
|
||||||
"VALUES ('%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insertQuestStatus, "INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
|
||||||
GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILLISECONDS+ sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(i->first);
|
||||||
|
stmt.addUInt8(i->second.m_status);
|
||||||
|
stmt.addUInt8(i->second.m_rewarded);
|
||||||
|
stmt.addUInt8(i->second.m_explored);
|
||||||
|
stmt.addUInt64(uint64(i->second.m_timer / IN_MILLISECONDS+ sWorld.GetGameTime()));
|
||||||
|
for (int k = 0; k < QUEST_OBJECTIVES_COUNT; ++k)
|
||||||
|
stmt.addUInt32(i->second.m_creatureOrGOcount[k]);
|
||||||
|
for (int k = 0; k < QUEST_OBJECTIVES_COUNT; ++k)
|
||||||
|
stmt.addUInt32(i->second.m_itemcount[k]);
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QUEST_CHANGED :
|
case QUEST_CHANGED :
|
||||||
CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" UI64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ",
|
{
|
||||||
i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILLISECONDS + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updateQuestStatus, "UPDATE character_queststatus SET status = ?,rewarded = ?,explored = ?,timer = ?,"
|
||||||
|
"mobcount1 = ?,mobcount2 = ?,mobcount3 = ?,mobcount4 = ?,itemcount1 = ?,itemcount2 = ?,itemcount3 = ?,itemcount4 = ? WHERE guid = ? AND quest = ?");
|
||||||
|
|
||||||
|
stmt.addUInt8(i->second.m_status);
|
||||||
|
stmt.addUInt8(i->second.m_rewarded);
|
||||||
|
stmt.addUInt8(i->second.m_explored);
|
||||||
|
stmt.addUInt64(uint64(i->second.m_timer / IN_MILLISECONDS + sWorld.GetGameTime()));
|
||||||
|
for (int k = 0; k < QUEST_OBJECTIVES_COUNT; ++k)
|
||||||
|
stmt.addUInt32(i->second.m_creatureOrGOcount[k]);
|
||||||
|
for (int k = 0; k < QUEST_OBJECTIVES_COUNT; ++k)
|
||||||
|
stmt.addUInt32(i->second.m_itemcount[k]);
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(i->first);
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QUEST_UNCHANGED:
|
case QUEST_UNCHANGED:
|
||||||
break;
|
break;
|
||||||
|
|
@ -17538,11 +17683,17 @@ void Player::_SaveDailyQuestStatus()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we don't need transactions here.
|
// we don't need transactions here.
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",GetGUIDLow());
|
static SqlStatementID delQuestStatus ;
|
||||||
|
static SqlStatementID insQuestStatus ;
|
||||||
|
|
||||||
|
SqlStatement stmtDel = CharacterDatabase.CreateStatement(delQuestStatus, "DELETE FROM character_queststatus_daily WHERE guid = ?");
|
||||||
|
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insQuestStatus, "INSERT INTO character_queststatus_daily (guid,quest) VALUES (?, ?)");
|
||||||
|
|
||||||
|
stmtDel.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
|
for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
|
||||||
if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
|
if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest) VALUES ('%u', '%u')",
|
stmtIns.PExecute(GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
|
||||||
GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
|
|
||||||
|
|
||||||
m_DailyQuestChanged = false;
|
m_DailyQuestChanged = false;
|
||||||
}
|
}
|
||||||
|
|
@ -17553,13 +17704,18 @@ void Player::_SaveWeeklyQuestStatus()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we don't need transactions here.
|
// we don't need transactions here.
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus_weekly WHERE guid = '%u'",GetGUIDLow());
|
static SqlStatementID delQuestStatus ;
|
||||||
|
static SqlStatementID insQuestStatus ;
|
||||||
|
|
||||||
|
SqlStatement stmtDel = CharacterDatabase.CreateStatement(delQuestStatus, "DELETE FROM character_queststatus_weekly WHERE guid = ?");
|
||||||
|
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insQuestStatus, "INSERT INTO character_queststatus_weekly (guid,quest) VALUES (?, ?)");
|
||||||
|
|
||||||
|
stmtDel.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
|
for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
|
||||||
{
|
{
|
||||||
uint32 quest_id = *iter;
|
uint32 quest_id = *iter;
|
||||||
|
stmtIns.PExecute(GetGUIDLow(), quest_id);
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_queststatus_weekly (guid,quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_WeeklyQuestChanged = false;
|
m_WeeklyQuestChanged = false;
|
||||||
|
|
@ -17571,13 +17727,18 @@ void Player::_SaveMonthlyQuestStatus()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we don't need transactions here.
|
// we don't need transactions here.
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus_monthly WHERE guid = '%u'", GetGUIDLow());
|
static SqlStatementID deleteQuest ;
|
||||||
|
static SqlStatementID insertQuest ;
|
||||||
|
|
||||||
|
SqlStatement stmtDel = CharacterDatabase.CreateStatement(deleteQuest, "DELETE FROM character_queststatus_monthly WHERE guid = ?");
|
||||||
|
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insertQuest, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)");
|
||||||
|
|
||||||
|
stmtDel.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter)
|
for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter)
|
||||||
{
|
{
|
||||||
uint32 quest_id = *iter;
|
uint32 quest_id = *iter;
|
||||||
|
stmtIns.PExecute(GetGUIDLow(), quest_id);
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_queststatus_monthly (guid, quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_MonthlyQuestChanged = false;
|
m_MonthlyQuestChanged = false;
|
||||||
|
|
@ -17585,6 +17746,10 @@ void Player::_SaveMonthlyQuestStatus()
|
||||||
|
|
||||||
void Player::_SaveSkills()
|
void Player::_SaveSkills()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID delSkills ;
|
||||||
|
static SqlStatementID insSkills ;
|
||||||
|
static SqlStatementID updSkills ;
|
||||||
|
|
||||||
// we don't need transactions here.
|
// we don't need transactions here.
|
||||||
for( SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); )
|
for( SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); )
|
||||||
{
|
{
|
||||||
|
|
@ -17596,7 +17761,8 @@ void Player::_SaveSkills()
|
||||||
|
|
||||||
if(itr->second.uState == SKILL_DELETED)
|
if(itr->second.uState == SKILL_DELETED)
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), itr->first );
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSkills, "DELETE FROM character_skills WHERE guid = ? AND skill = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow(), itr->first );
|
||||||
mSkillStatus.erase(itr++);
|
mSkillStatus.erase(itr++);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -17608,12 +17774,16 @@ void Player::_SaveSkills()
|
||||||
switch (itr->second.uState)
|
switch (itr->second.uState)
|
||||||
{
|
{
|
||||||
case SKILL_NEW:
|
case SKILL_NEW:
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_skills (guid, skill, value, max) VALUES ('%u', '%u', '%u', '%u')",
|
{
|
||||||
GetGUIDLow(), itr->first, value, max);
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insSkills, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)");
|
||||||
|
stmt.PExecute(GetGUIDLow(), itr->first, value, max);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SKILL_CHANGED:
|
case SKILL_CHANGED:
|
||||||
CharacterDatabase.PExecute("UPDATE character_skills SET value = '%u',max = '%u'WHERE guid = '%u' AND skill = '%u' ",
|
{
|
||||||
value, max, GetGUIDLow(), itr->first );
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updSkills, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?");
|
||||||
|
stmt.PExecute(value, max, GetGUIDLow(), itr->first );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SKILL_UNCHANGED:
|
case SKILL_UNCHANGED:
|
||||||
case SKILL_DELETED:
|
case SKILL_DELETED:
|
||||||
|
|
@ -17628,6 +17798,12 @@ void Player::_SaveSkills()
|
||||||
|
|
||||||
void Player::_SaveSpells()
|
void Player::_SaveSpells()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID delSpells ;
|
||||||
|
static SqlStatementID insSpells ;
|
||||||
|
|
||||||
|
SqlStatement stmtDel = CharacterDatabase.CreateStatement(delSpells, "DELETE FROM character_spell WHERE guid = ? and spell = ?");
|
||||||
|
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insSpells, "INSERT INTO character_spell (guid,spell,active,disabled) VALUES (?, ?, ?, ?)");
|
||||||
|
|
||||||
for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)
|
for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)
|
||||||
{
|
{
|
||||||
uint32 talentCosts = GetTalentSpellCost(itr->first);
|
uint32 talentCosts = GetTalentSpellCost(itr->first);
|
||||||
|
|
@ -17635,11 +17811,11 @@ void Player::_SaveSpells()
|
||||||
if (!talentCosts)
|
if (!talentCosts)
|
||||||
{
|
{
|
||||||
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
|
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
|
stmtDel.PExecute(GetGUIDLow(), itr->first);
|
||||||
|
|
||||||
// add only changed/new not dependent spells
|
// add only changed/new not dependent spells
|
||||||
if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
|
if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED))
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.active ? 1 : 0,itr->second.disabled ? 1 : 0);
|
stmtIns.PExecute(GetGUIDLow(), itr->first, uint8(itr->second.active ? 1 : 0), uint8(itr->second.disabled ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itr->second.state == PLAYERSPELL_REMOVED)
|
if (itr->second.state == PLAYERSPELL_REMOVED)
|
||||||
|
|
@ -17655,16 +17831,22 @@ void Player::_SaveSpells()
|
||||||
|
|
||||||
void Player::_SaveTalents()
|
void Player::_SaveTalents()
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
|
static SqlStatementID delTalents ;
|
||||||
|
static SqlStatementID insTalents ;
|
||||||
|
|
||||||
|
SqlStatement stmtDel = CharacterDatabase.CreateStatement(delTalents, "DELETE FROM character_talent WHERE guid = ? and talent_id = ? and spec = ?");
|
||||||
|
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insTalents, "INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES (?, ?, ?, ?)");
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i)
|
||||||
{
|
{
|
||||||
for (PlayerTalentMap::iterator itr = m_talents[i].begin(); itr != m_talents[i].end();)
|
for (PlayerTalentMap::iterator itr = m_talents[i].begin(); itr != m_talents[i].end();)
|
||||||
{
|
{
|
||||||
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
|
if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED)
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' and talent_id = '%u' and spec = '%u'", GetGUIDLow(),itr->first, i);
|
stmtDel.PExecute(GetGUIDLow(),itr->first, i);
|
||||||
|
|
||||||
// add only changed/new talents
|
// add only changed/new talents
|
||||||
if (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED)
|
if (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED)
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.currentRank, i);
|
stmtIns.PExecute(GetGUIDLow(), itr->first, itr->second.currentRank, i);
|
||||||
|
|
||||||
if (itr->second.state == PLAYERSPELL_REMOVED)
|
if (itr->second.state == PLAYERSPELL_REMOVED)
|
||||||
m_talents[i].erase(itr++);
|
m_talents[i].erase(itr++);
|
||||||
|
|
@ -17685,30 +17867,37 @@ void Player::_SaveStats()
|
||||||
if(!sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE))
|
if(!sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_stats WHERE guid = '%u'", GetGUIDLow());
|
static SqlStatementID delStats ;
|
||||||
std::ostringstream ss;
|
static SqlStatementID insertStats ;
|
||||||
ss << "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5, maxpower6, maxpower7, "
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delStats, "DELETE FROM character_stats WHERE guid = ?");
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.CreateStatement(insertStats, "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5, maxpower6, maxpower7, "
|
||||||
"strength, agility, stamina, intellect, spirit, armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, "
|
"strength, agility, stamina, intellect, spirit, armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, "
|
||||||
"blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) VALUES ("
|
"blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) "
|
||||||
<< GetGUIDLow() << ", "
|
"VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
<< GetMaxHealth() << ", ";
|
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt32(GetMaxHealth());
|
||||||
for(int i = 0; i < MAX_POWERS; ++i)
|
for(int i = 0; i < MAX_POWERS; ++i)
|
||||||
ss << GetMaxPower(Powers(i)) << ", ";
|
stmt.addUInt32(GetMaxPower(Powers(i)));
|
||||||
for(int i = 0; i < MAX_STATS; ++i)
|
for(int i = 0; i < MAX_STATS; ++i)
|
||||||
ss << GetStat(Stats(i)) << ", ";
|
stmt.addFloat(GetStat(Stats(i)));
|
||||||
// armor + school resistances
|
// armor + school resistances
|
||||||
for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
|
for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
|
||||||
ss << GetResistance(SpellSchools(i)) << ",";
|
stmt.addUInt32(GetResistance(SpellSchools(i)));
|
||||||
ss << GetFloatValue(PLAYER_BLOCK_PERCENTAGE) << ", "
|
stmt.addFloat(GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
|
||||||
<< GetFloatValue(PLAYER_DODGE_PERCENTAGE) << ", "
|
stmt.addFloat(GetFloatValue(PLAYER_DODGE_PERCENTAGE));
|
||||||
<< GetFloatValue(PLAYER_PARRY_PERCENTAGE) << ", "
|
stmt.addFloat(GetFloatValue(PLAYER_PARRY_PERCENTAGE));
|
||||||
<< GetFloatValue(PLAYER_CRIT_PERCENTAGE) << ", "
|
stmt.addFloat(GetFloatValue(PLAYER_CRIT_PERCENTAGE));
|
||||||
<< GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE) << ", "
|
stmt.addFloat(GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE));
|
||||||
<< GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1) << ", "
|
stmt.addFloat(GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1));
|
||||||
<< GetUInt32Value(UNIT_FIELD_ATTACK_POWER) << ", "
|
stmt.addUInt32(GetUInt32Value(UNIT_FIELD_ATTACK_POWER));
|
||||||
<< GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER) << ", "
|
stmt.addUInt32(GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER));
|
||||||
<< GetBaseSpellPowerBonus() << ")";
|
stmt.addUInt32(GetBaseSpellPowerBonus());
|
||||||
CharacterDatabase.Execute( ss.str().c_str() );
|
|
||||||
|
stmt.Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::outDebugStatsValues() const
|
void Player::outDebugStatsValues() const
|
||||||
|
|
@ -22218,6 +22407,10 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
|
||||||
|
|
||||||
void Player::_SaveEquipmentSets()
|
void Player::_SaveEquipmentSets()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID updSets ;
|
||||||
|
static SqlStatementID insSets ;
|
||||||
|
static SqlStatementID delSets ;
|
||||||
|
|
||||||
for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
|
for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
|
||||||
{
|
{
|
||||||
uint32 index = itr->first;
|
uint32 index = itr->first;
|
||||||
|
|
@ -22229,39 +22422,54 @@ void Player::_SaveEquipmentSets()
|
||||||
break; // nothing do
|
break; // nothing do
|
||||||
case EQUIPMENT_SET_CHANGED:
|
case EQUIPMENT_SET_CHANGED:
|
||||||
{
|
{
|
||||||
// prevent SQL injection
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updSets, "UPDATE character_equipmentsets SET name=?, iconname=?, item0=?, item1=?, item2=?, item3=?, item4=?, "
|
||||||
std::string db_IconName = eqset.IconName;
|
"item5=?, item6=?, item7=?, item8=?, item9=?, item10=?, item11=?, item12=?, item13=?, item14=?, "
|
||||||
std::string db_Name = eqset.Name;
|
"item15=?, item16=?, item17=?, item18=? WHERE guid=? AND setguid=? AND setindex=?");
|
||||||
CharacterDatabase.escape_string(db_IconName);
|
|
||||||
CharacterDatabase.escape_string(db_Name);
|
stmt.addString(eqset.IconName);
|
||||||
CharacterDatabase.PExecute("UPDATE character_equipmentsets SET name='%s', iconname='%s', item0='%u', item1='%u', item2='%u', item3='%u', item4='%u', item5='%u', item6='%u', item7='%u', item8='%u', item9='%u', item10='%u', item11='%u', item12='%u', item13='%u', item14='%u', item15='%u', item16='%u', item17='%u', item18='%u' WHERE guid='%u' AND setguid='"UI64FMTD"' AND setindex='%u'",
|
stmt.addString(eqset.Name);
|
||||||
db_Name.c_str(), db_IconName.c_str(), eqset.Items[0], eqset.Items[1], eqset.Items[2], eqset.Items[3], eqset.Items[4], eqset.Items[5], eqset.Items[6], eqset.Items[7],
|
|
||||||
eqset.Items[8], eqset.Items[9], eqset.Items[10], eqset.Items[11], eqset.Items[12], eqset.Items[13], eqset.Items[14], eqset.Items[15], eqset.Items[16], eqset.Items[17], eqset.Items[18], GetGUIDLow(), eqset.Guid, index);
|
for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
|
||||||
|
stmt.addUInt32(eqset.Items[i]);
|
||||||
|
|
||||||
|
stmt.addUInt32(GetGUIDLow());
|
||||||
|
stmt.addUInt64(eqset.Guid);
|
||||||
|
stmt.addUInt32(index);
|
||||||
|
|
||||||
|
stmt.Execute();
|
||||||
|
|
||||||
eqset.state = EQUIPMENT_SET_UNCHANGED;
|
eqset.state = EQUIPMENT_SET_UNCHANGED;
|
||||||
++itr;
|
++itr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EQUIPMENT_SET_NEW:
|
case EQUIPMENT_SET_NEW:
|
||||||
{
|
{
|
||||||
// prevent SQL injection
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insSets, "INSERT INTO character_equipmentsets VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
std::string db_IconName = eqset.IconName;
|
stmt.addUInt32(GetGUIDLow());
|
||||||
std::string db_Name = eqset.Name;
|
stmt.addUInt64(eqset.Guid);
|
||||||
CharacterDatabase.escape_string(db_IconName);
|
stmt.addUInt32(index);
|
||||||
CharacterDatabase.escape_string(db_Name);
|
stmt.addString(eqset.IconName);
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_equipmentsets VALUES ('%u', '"UI64FMTD"', '%u', '%s', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
|
stmt.addString(eqset.Name);
|
||||||
GetGUIDLow(), eqset.Guid, index, db_Name.c_str(), db_IconName.c_str(), eqset.Items[0], eqset.Items[1], eqset.Items[2], eqset.Items[3], eqset.Items[4], eqset.Items[5], eqset.Items[6], eqset.Items[7],
|
|
||||||
eqset.Items[8], eqset.Items[9], eqset.Items[10], eqset.Items[11], eqset.Items[12], eqset.Items[13], eqset.Items[14], eqset.Items[15], eqset.Items[16], eqset.Items[17], eqset.Items[18]);
|
for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
|
||||||
|
stmt.addUInt32(eqset.Items[i]);
|
||||||
|
|
||||||
|
stmt.Execute();
|
||||||
|
|
||||||
eqset.state = EQUIPMENT_SET_UNCHANGED;
|
eqset.state = EQUIPMENT_SET_UNCHANGED;
|
||||||
++itr;
|
++itr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EQUIPMENT_SET_DELETED:
|
case EQUIPMENT_SET_DELETED:
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE setguid="UI64FMTD, eqset.Guid);
|
{
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delSets, "DELETE FROM character_equipmentsets WHERE setguid = ?");
|
||||||
|
stmt.PExecute(eqset.Guid);
|
||||||
m_EquipmentSets.erase(itr++);
|
m_EquipmentSets.erase(itr++);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Player::_SaveBGData()
|
void Player::_SaveBGData()
|
||||||
{
|
{
|
||||||
|
|
@ -22269,13 +22477,30 @@ void Player::_SaveBGData()
|
||||||
if (!m_bgData.m_needSave)
|
if (!m_bgData.m_needSave)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_battleground_data WHERE guid='%u'", GetGUIDLow());
|
static SqlStatementID delBGData ;
|
||||||
|
static SqlStatementID insBGData ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delBGData, "DELETE FROM character_battleground_data WHERE guid = ?");
|
||||||
|
|
||||||
|
stmt.PExecute(GetGUIDLow());
|
||||||
|
|
||||||
if (m_bgData.bgInstanceID)
|
if (m_bgData.bgInstanceID)
|
||||||
{
|
{
|
||||||
|
stmt = CharacterDatabase.CreateStatement(insBGData, "INSERT INTO character_battleground_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
/* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
|
/* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_battleground_data VALUES ('%u', '%u', '%u', '%f', '%f', '%f', '%f', '%u', '%u', '%u', '%u')",
|
stmt.addUInt32(GetGUIDLow());
|
||||||
GetGUIDLow(), m_bgData.bgInstanceID, uint32(m_bgData.bgTeam), m_bgData.joinPos.coord_x, m_bgData.joinPos.coord_y, m_bgData.joinPos.coord_z,
|
stmt.addUInt32(m_bgData.bgInstanceID);
|
||||||
m_bgData.joinPos.orientation, m_bgData.joinPos.mapid, m_bgData.taxiPath[0], m_bgData.taxiPath[1], m_bgData.mountSpell);
|
stmt.addUInt32(uint32(m_bgData.bgTeam));
|
||||||
|
stmt.addFloat(m_bgData.joinPos.coord_x);
|
||||||
|
stmt.addFloat(m_bgData.joinPos.coord_y);
|
||||||
|
stmt.addFloat(m_bgData.joinPos.coord_z);
|
||||||
|
stmt.addFloat(m_bgData.joinPos.orientation);
|
||||||
|
stmt.addUInt32(m_bgData.joinPos.mapid);
|
||||||
|
stmt.addUInt32(m_bgData.taxiPath[0]);
|
||||||
|
stmt.addUInt32(m_bgData.taxiPath[1]);
|
||||||
|
stmt.addUInt32(m_bgData.mountSpell);
|
||||||
|
|
||||||
|
stmt.Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bgData.m_needSave = false;
|
m_bgData.m_needSave = false;
|
||||||
|
|
|
||||||
|
|
@ -1744,14 +1744,8 @@ class MANGOS_DLL_SPEC Player : public Unit
|
||||||
bool isRessurectRequested() const { return !m_resurrectGuid.IsEmpty(); }
|
bool isRessurectRequested() const { return !m_resurrectGuid.IsEmpty(); }
|
||||||
void ResurectUsingRequestData();
|
void ResurectUsingRequestData();
|
||||||
|
|
||||||
int getCinematic()
|
uint32 getCinematic() { return m_cinematic; }
|
||||||
{
|
void setCinematic(uint32 cine) { m_cinematic = cine; }
|
||||||
return m_cinematic;
|
|
||||||
}
|
|
||||||
void setCinematic(int cine)
|
|
||||||
{
|
|
||||||
m_cinematic = cine;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg = true);
|
static bool IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg = true);
|
||||||
ActionButton* addActionButton(uint8 spec, uint8 button, uint32 action, uint8 type);
|
ActionButton* addActionButton(uint8 spec, uint8 button, uint32 action, uint8 type);
|
||||||
|
|
@ -2567,7 +2561,7 @@ class MANGOS_DLL_SPEC Player : public Unit
|
||||||
typedef std::list<Channel*> JoinedChannelsList;
|
typedef std::list<Channel*> JoinedChannelsList;
|
||||||
JoinedChannelsList m_channels;
|
JoinedChannelsList m_channels;
|
||||||
|
|
||||||
int m_cinematic;
|
uint32 m_cinematic;
|
||||||
|
|
||||||
TradeData* m_trade;
|
TradeData* m_trade;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -503,12 +503,18 @@ void ReputationMgr::LoadFromDB(QueryResult *result)
|
||||||
|
|
||||||
void ReputationMgr::SaveToDB()
|
void ReputationMgr::SaveToDB()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID delRep ;
|
||||||
|
static SqlStatementID insRep ;
|
||||||
|
|
||||||
|
SqlStatement stmtDel = CharacterDatabase.CreateStatement(delRep, "DELETE FROM character_reputation WHERE guid = ? AND faction=?");
|
||||||
|
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insRep, "INSERT INTO character_reputation (guid,faction,standing,flags) VALUES (?, ?, ?, ?)");
|
||||||
|
|
||||||
for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
||||||
{
|
{
|
||||||
if (itr->second.needSave)
|
if (itr->second.needSave)
|
||||||
{
|
{
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", m_player->GetGUIDLow(), itr->second.ID);
|
stmtDel.PExecute(m_player->GetGUIDLow(), itr->second.ID);
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", m_player->GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
|
stmtIns.PExecute(m_player->GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
|
||||||
itr->second.needSave = false;
|
itr->second.needSave = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,11 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
|
||||||
pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
|
|
||||||
|
static SqlStatementID delGifts ;
|
||||||
|
|
||||||
|
SqlStatement stmt = CharacterDatabase.CreateStatement(delGifts, "DELETE FROM character_gifts WHERE item_guid = ?");
|
||||||
|
stmt.PExecute(pItem->GetGUIDLow());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pUser->SendLoot(pItem->GetObjectGuid(),LOOT_CORPSE);
|
pUser->SendLoot(pItem->GetObjectGuid(),LOOT_CORPSE);
|
||||||
|
|
|
||||||
|
|
@ -749,15 +749,32 @@ void WorldSession::SendTutorialsData()
|
||||||
|
|
||||||
void WorldSession::SaveTutorialsData()
|
void WorldSession::SaveTutorialsData()
|
||||||
{
|
{
|
||||||
|
static SqlStatementID updTutorial ;
|
||||||
|
static SqlStatementID insTutorial ;
|
||||||
|
|
||||||
switch(m_tutorialState)
|
switch(m_tutorialState)
|
||||||
{
|
{
|
||||||
case TUTORIALDATA_CHANGED:
|
case TUTORIALDATA_CHANGED:
|
||||||
CharacterDatabase.PExecute("UPDATE character_tutorial SET tut0='%u', tut1='%u', tut2='%u', tut3='%u', tut4='%u', tut5='%u', tut6='%u', tut7='%u' WHERE account = '%u'",
|
{
|
||||||
m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7], GetAccountId());
|
SqlStatement stmt = CharacterDatabase.CreateStatement(updTutorial, "UPDATE character_tutorial SET tut0=?, tut1=?, tut2=?, tut3=?, tut4=?, tut5=?, tut6=?, tut7=? WHERE account = ?");
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
stmt.addUInt32(m_Tutorials[i]);
|
||||||
|
|
||||||
|
stmt.addUInt32(GetAccountId());
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TUTORIALDATA_NEW:
|
case TUTORIALDATA_NEW:
|
||||||
CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
|
{
|
||||||
GetAccountId(), m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]);
|
SqlStatement stmt = CharacterDatabase.CreateStatement(insTutorial, "INSERT INTO character_tutorial (account,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
stmt.addUInt32(GetAccountId());
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
stmt.addUInt32(m_Tutorials[i]);
|
||||||
|
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TUTORIALDATA_UNCHANGED:
|
case TUTORIALDATA_UNCHANGED:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,77 @@
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#define MIN_CONNECTION_POOL_SIZE 1
|
#define MIN_CONNECTION_POOL_SIZE 1
|
||||||
#define MAX_CONNECTION_POOL_SIZE 16
|
#define MAX_CONNECTION_POOL_SIZE 16
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
SqlPreparedStatement * SqlConnection::CreateStatement( const std::string& fmt )
|
||||||
|
{
|
||||||
|
return new SqlPlainPreparedStatement(fmt, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlConnection::FreePreparedStatements()
|
||||||
|
{
|
||||||
|
SqlConnection::Lock guard(this);
|
||||||
|
|
||||||
|
size_t nStmts = m_holder.size();
|
||||||
|
for (size_t i = 0; i < nStmts; ++i)
|
||||||
|
delete m_holder[i];
|
||||||
|
|
||||||
|
m_holder.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlPreparedStatement * SqlConnection::GetStmt( int nIndex )
|
||||||
|
{
|
||||||
|
if(nIndex < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
//resize stmt container
|
||||||
|
if(m_holder.size() <= nIndex)
|
||||||
|
m_holder.resize(nIndex + 1, NULL);
|
||||||
|
|
||||||
|
SqlPreparedStatement * pStmt = NULL;
|
||||||
|
|
||||||
|
//create stmt if needed
|
||||||
|
if(m_holder[nIndex] == NULL)
|
||||||
|
{
|
||||||
|
//obtain SQL request string
|
||||||
|
std::string fmt = m_db.GetStmtString(nIndex);
|
||||||
|
MANGOS_ASSERT(fmt.length());
|
||||||
|
//allocate SQlPreparedStatement object
|
||||||
|
pStmt = CreateStatement(fmt);
|
||||||
|
//prepare statement
|
||||||
|
if(!pStmt->prepare())
|
||||||
|
{
|
||||||
|
MANGOS_ASSERT(false && "Unable to prepare SQL statement");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//save statement in internal registry
|
||||||
|
m_holder[nIndex] = pStmt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pStmt = m_holder[nIndex];
|
||||||
|
|
||||||
|
return pStmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlConnection::ExecuteStmt(int nIndex, const SqlStmtParameters& id )
|
||||||
|
{
|
||||||
|
if(nIndex == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//get prepared statement object
|
||||||
|
SqlPreparedStatement * pStmt = GetStmt(nIndex);
|
||||||
|
//bind parameters
|
||||||
|
pStmt->bind(id);
|
||||||
|
//execute statement
|
||||||
|
return pStmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
Database::~Database()
|
Database::~Database()
|
||||||
{
|
{
|
||||||
StopServer();
|
StopServer();
|
||||||
|
|
@ -274,7 +341,7 @@ bool Database::Execute(const char *sql)
|
||||||
if(pTrans)
|
if(pTrans)
|
||||||
{
|
{
|
||||||
//add SQL request to trans queue
|
//add SQL request to trans queue
|
||||||
pTrans->DelayExecute(sql);
|
pTrans->DelayExecute(new SqlPlainRequest(sql));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -283,7 +350,7 @@ bool Database::Execute(const char *sql)
|
||||||
return DirectExecute(sql);
|
return DirectExecute(sql);
|
||||||
|
|
||||||
// Simple sql statement
|
// Simple sql statement
|
||||||
m_threadBody->Delay(new SqlStatement(sql));
|
m_threadBody->Delay(new SqlPlainRequest(sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -477,6 +544,85 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::ExecuteStmt( const SqlStatementID& id, SqlStmtParameters * params )
|
||||||
|
{
|
||||||
|
if (!m_pAsyncConn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SqlTransaction * pTrans = m_TransStorage->get();
|
||||||
|
if(pTrans)
|
||||||
|
{
|
||||||
|
//add SQL request to trans queue
|
||||||
|
pTrans->DelayExecute(new SqlPreparedRequest(id.ID(), params));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if async execution is not available
|
||||||
|
if(!m_bAllowAsyncTransactions)
|
||||||
|
return DirectExecuteStmt(id, params);
|
||||||
|
|
||||||
|
// Simple sql statement
|
||||||
|
m_threadBody->Delay(new SqlPreparedRequest(id.ID(), params));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::DirectExecuteStmt( const SqlStatementID& id, SqlStmtParameters * params )
|
||||||
|
{
|
||||||
|
MANGOS_ASSERT(params);
|
||||||
|
std::auto_ptr<SqlStmtParameters> p(params);
|
||||||
|
//execute statement
|
||||||
|
SqlConnection::Lock _guard(getAsyncConnection());
|
||||||
|
return _guard->ExecuteStmt(id.ID(), *params);
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlStatement Database::CreateStatement(SqlStatementID& index, const char * fmt )
|
||||||
|
{
|
||||||
|
int nId = -1;
|
||||||
|
//check if statement ID is initialized
|
||||||
|
if(!index.initialized())
|
||||||
|
{
|
||||||
|
//convert to lower register
|
||||||
|
std::string szFmt(fmt);
|
||||||
|
//count input parameters
|
||||||
|
int nParams = std::count(szFmt.begin(), szFmt.end(), '?');
|
||||||
|
//find existing or add a new record in registry
|
||||||
|
LOCK_GUARD _guard(m_stmtGuard);
|
||||||
|
PreparedStmtRegistry::const_iterator iter = m_stmtRegistry.find(szFmt);
|
||||||
|
if(iter == m_stmtRegistry.end())
|
||||||
|
{
|
||||||
|
nId = ++m_iStmtIndex;
|
||||||
|
m_stmtRegistry[szFmt] = nId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nId = iter->second;
|
||||||
|
|
||||||
|
//save initialized statement index info
|
||||||
|
index.init(nId, nParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SqlStatement(index, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Database::GetStmtString(const int stmtId) const
|
||||||
|
{
|
||||||
|
LOCK_GUARD _guard(m_stmtGuard);
|
||||||
|
|
||||||
|
if(stmtId == -1 || stmtId > m_iStmtIndex)
|
||||||
|
return std::string();
|
||||||
|
|
||||||
|
PreparedStmtRegistry::const_iterator iter_last = m_stmtRegistry.end();
|
||||||
|
for(PreparedStmtRegistry::const_iterator iter = m_stmtRegistry.begin(); iter != iter_last; ++iter)
|
||||||
|
{
|
||||||
|
if(iter->second == stmtId)
|
||||||
|
return iter->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
//HELPER CLASSES AND FUNCTIONS
|
||||||
Database::TransHelper::~TransHelper()
|
Database::TransHelper::~TransHelper()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,14 @@
|
||||||
#include "Policies/ThreadingModel.h"
|
#include "Policies/ThreadingModel.h"
|
||||||
#include <ace/TSS_T.h>
|
#include <ace/TSS_T.h>
|
||||||
#include <ace/Atomic_Op.h>
|
#include <ace/Atomic_Op.h>
|
||||||
|
#include "SqlPreparedStatement.h"
|
||||||
|
|
||||||
class SqlTransaction;
|
class SqlTransaction;
|
||||||
class SqlResultQueue;
|
class SqlResultQueue;
|
||||||
class SqlQueryHolder;
|
class SqlQueryHolder;
|
||||||
|
class SqlStmtParameters;
|
||||||
|
class SqlParamBinder;
|
||||||
|
class Database;
|
||||||
|
|
||||||
#define MAX_QUERY_LEN 32*1024
|
#define MAX_QUERY_LEN 32*1024
|
||||||
|
|
||||||
|
|
@ -57,6 +61,9 @@ class MANGOS_DLL_SPEC SqlConnection
|
||||||
// can't rollback without transaction support
|
// can't rollback without transaction support
|
||||||
virtual bool RollbackTransaction() { return true; }
|
virtual bool RollbackTransaction() { return true; }
|
||||||
|
|
||||||
|
//methods to work with prepared statements
|
||||||
|
bool ExecuteStmt(int nIndex, const SqlStmtParameters& id);
|
||||||
|
|
||||||
//SqlConnection object lock
|
//SqlConnection object lock
|
||||||
class Lock
|
class Lock
|
||||||
{
|
{
|
||||||
|
|
@ -70,9 +77,27 @@ class MANGOS_DLL_SPEC SqlConnection
|
||||||
SqlConnection * const m_pConn;
|
SqlConnection * const m_pConn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//get DB object
|
||||||
|
Database& DB() { return m_db; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SqlConnection(Database& db) : m_db(db) {}
|
||||||
|
|
||||||
|
virtual SqlPreparedStatement * CreateStatement(const std::string& fmt);
|
||||||
|
//allocate prepared statement and return statement ID
|
||||||
|
SqlPreparedStatement * GetStmt(int nIndex);
|
||||||
|
|
||||||
|
Database& m_db;
|
||||||
|
|
||||||
|
//free prepared statements objects
|
||||||
|
void FreePreparedStatements();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef ACE_Recursive_Thread_Mutex LOCK_TYPE;
|
typedef ACE_Recursive_Thread_Mutex LOCK_TYPE;
|
||||||
LOCK_TYPE m_mutex;
|
LOCK_TYPE m_mutex;
|
||||||
|
|
||||||
|
typedef std::vector<SqlPreparedStatement * > StmtHolder;
|
||||||
|
StmtHolder m_holder;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MANGOS_DLL_SPEC Database
|
class MANGOS_DLL_SPEC Database
|
||||||
|
|
@ -165,6 +190,13 @@ class MANGOS_DLL_SPEC Database
|
||||||
//for sync transaction execution
|
//for sync transaction execution
|
||||||
bool CommitTransactionDirect();
|
bool CommitTransactionDirect();
|
||||||
|
|
||||||
|
//PREPARED STATEMENT API
|
||||||
|
|
||||||
|
//allocate index for prepared statement with SQL request 'fmt'
|
||||||
|
SqlStatement CreateStatement(SqlStatementID& index, const char * fmt);
|
||||||
|
//get prepared statement format string
|
||||||
|
std::string GetStmtString(const int stmtId) const;
|
||||||
|
|
||||||
operator bool () const { return m_pQueryConnections.size() && m_pAsyncConn != 0; }
|
operator bool () const { return m_pQueryConnections.size() && m_pAsyncConn != 0; }
|
||||||
|
|
||||||
//escape string generation
|
//escape string generation
|
||||||
|
|
@ -191,7 +223,7 @@ class MANGOS_DLL_SPEC Database
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Database() : m_pAsyncConn(NULL), m_pResultQueue(NULL), m_threadBody(NULL), m_delayThread(NULL),
|
Database() : m_pAsyncConn(NULL), m_pResultQueue(NULL), m_threadBody(NULL), m_delayThread(NULL),
|
||||||
m_logSQL(false), m_pingIntervallms(0), m_nQueryConnPoolSize(1), m_bAllowAsyncTransactions(false)
|
m_logSQL(false), m_pingIntervallms(0), m_nQueryConnPoolSize(1), m_bAllowAsyncTransactions(false), m_iStmtIndex(-1)
|
||||||
{
|
{
|
||||||
m_nQueryCounter = -1;
|
m_nQueryCounter = -1;
|
||||||
}
|
}
|
||||||
|
|
@ -235,6 +267,12 @@ class MANGOS_DLL_SPEC Database
|
||||||
//for now return one single connection for async requests
|
//for now return one single connection for async requests
|
||||||
SqlConnection * getAsyncConnection() const { return m_pAsyncConn; }
|
SqlConnection * getAsyncConnection() const { return m_pAsyncConn; }
|
||||||
|
|
||||||
|
friend class SqlStatement;
|
||||||
|
//PREPARED STATEMENT API
|
||||||
|
//query function for prepared statements
|
||||||
|
bool ExecuteStmt(const SqlStatementID& id, SqlStmtParameters * params);
|
||||||
|
bool DirectExecuteStmt(const SqlStatementID& id, SqlStmtParameters * params);
|
||||||
|
|
||||||
//connection helper counters
|
//connection helper counters
|
||||||
int m_nQueryConnPoolSize; //current size of query connection pool
|
int m_nQueryConnPoolSize; //current size of query connection pool
|
||||||
ACE_Atomic_Op<ACE_Thread_Mutex, long> m_nQueryCounter; //counter for connection selection
|
ACE_Atomic_Op<ACE_Thread_Mutex, long> m_nQueryCounter; //counter for connection selection
|
||||||
|
|
@ -252,6 +290,17 @@ class MANGOS_DLL_SPEC Database
|
||||||
|
|
||||||
bool m_bAllowAsyncTransactions; ///< flag which specifies if async transactions are enabled
|
bool m_bAllowAsyncTransactions; ///< flag which specifies if async transactions are enabled
|
||||||
|
|
||||||
|
//PREPARED STATEMENT REGISTRY
|
||||||
|
typedef ACE_Thread_Mutex LOCK_TYPE;
|
||||||
|
typedef ACE_Guard<LOCK_TYPE> LOCK_GUARD;
|
||||||
|
|
||||||
|
mutable LOCK_TYPE m_stmtGuard;
|
||||||
|
|
||||||
|
typedef UNORDERED_MAP<std::string, int> PreparedStmtRegistry;
|
||||||
|
PreparedStmtRegistry m_stmtRegistry; ///<
|
||||||
|
|
||||||
|
int m_iStmtIndex;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool m_logSQL;
|
bool m_logSQL;
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,12 @@ DatabaseMysql::~DatabaseMysql()
|
||||||
|
|
||||||
SqlConnection * DatabaseMysql::CreateConnection()
|
SqlConnection * DatabaseMysql::CreateConnection()
|
||||||
{
|
{
|
||||||
return new MySQLConnection();
|
return new MySQLConnection(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
MySQLConnection::~MySQLConnection()
|
MySQLConnection::~MySQLConnection()
|
||||||
{
|
{
|
||||||
|
FreePreparedStatements();
|
||||||
mysql_close(mMysql);
|
mysql_close(mMysql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,4 +310,194 @@ unsigned long MySQLConnection::escape_string(char *to, const char *from, unsigne
|
||||||
return(mysql_real_escape_string(mMysql, to, from, length));
|
return(mysql_real_escape_string(mMysql, to, from, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
SqlPreparedStatement * MySQLConnection::CreateStatement( const std::string& fmt )
|
||||||
|
{
|
||||||
|
return new MySqlPreparedStatement(fmt, *this, mMysql);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
MySqlPreparedStatement::MySqlPreparedStatement( const std::string& fmt, SqlConnection& conn, MYSQL * mysql ) : SqlPreparedStatement(fmt, conn),
|
||||||
|
m_pMySQLConn(mysql), m_stmt(NULL), m_pInputArgs(NULL), m_pResult(NULL), m_pResultMetadata(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MySqlPreparedStatement::~MySqlPreparedStatement()
|
||||||
|
{
|
||||||
|
RemoveBinds();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySqlPreparedStatement::prepare()
|
||||||
|
{
|
||||||
|
if(isPrepared())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//remove old binds
|
||||||
|
RemoveBinds();
|
||||||
|
|
||||||
|
//create statement object
|
||||||
|
m_stmt = mysql_stmt_init(m_pMySQLConn);
|
||||||
|
if (!m_stmt)
|
||||||
|
{
|
||||||
|
sLog.outError("SQL: mysql_stmt_init()() failed ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//prepare statement
|
||||||
|
if (mysql_stmt_prepare(m_stmt, m_szFmt.c_str(), m_szFmt.length()))
|
||||||
|
{
|
||||||
|
sLog.outError("SQL: mysql_stmt_prepare() failed for '%s'", m_szFmt.c_str());
|
||||||
|
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the parameter count from the statement */
|
||||||
|
m_nParams = mysql_stmt_param_count(m_stmt);
|
||||||
|
|
||||||
|
/* Fetch result set meta information */
|
||||||
|
m_pResultMetadata = mysql_stmt_result_metadata(m_stmt);
|
||||||
|
//if we do not have result metadata
|
||||||
|
if (!m_pResultMetadata && strnicmp(m_szFmt.c_str(), "select", 6) == 0)
|
||||||
|
{
|
||||||
|
sLog.outError("SQL: no meta information for '%s'", m_szFmt.c_str());
|
||||||
|
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//bind input buffers
|
||||||
|
if(m_nParams)
|
||||||
|
{
|
||||||
|
m_pInputArgs = new MYSQL_BIND[m_nParams];
|
||||||
|
memset(m_pInputArgs, 0, sizeof(MYSQL_BIND) * m_nParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if we have a statement which returns result sets
|
||||||
|
if(m_pResultMetadata)
|
||||||
|
{
|
||||||
|
//our statement is query
|
||||||
|
m_bIsQuery = true;
|
||||||
|
/* Get total columns in the query */
|
||||||
|
m_nColumns = mysql_num_fields(m_pResultMetadata);
|
||||||
|
|
||||||
|
//bind output buffers
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bPrepared = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySqlPreparedStatement::bind( const SqlStmtParameters& holder )
|
||||||
|
{
|
||||||
|
if(!isPrepared())
|
||||||
|
{
|
||||||
|
MANGOS_ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//finalize adding params
|
||||||
|
if(!m_pInputArgs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//verify if we bound all needed input parameters
|
||||||
|
if(m_nParams != holder.boundParams())
|
||||||
|
{
|
||||||
|
MANGOS_ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nIndex = 0;
|
||||||
|
SqlStmtParameters::ParameterContainer const& _args = holder.params();
|
||||||
|
|
||||||
|
SqlStmtParameters::ParameterContainer::const_iterator iter_last = _args.end();
|
||||||
|
for (SqlStmtParameters::ParameterContainer::const_iterator iter = _args.begin(); iter != iter_last; ++iter)
|
||||||
|
{
|
||||||
|
//bind parameter
|
||||||
|
addParam(nIndex++, (*iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
//bind input arguments
|
||||||
|
if(mysql_stmt_bind_param(m_stmt, m_pInputArgs))
|
||||||
|
{
|
||||||
|
sLog.outError("SQL ERROR: mysql_stmt_bind_param() failed\n");
|
||||||
|
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySqlPreparedStatement::addParam( int nIndex, const SqlStmtFieldData& data )
|
||||||
|
{
|
||||||
|
MANGOS_ASSERT(m_pInputArgs);
|
||||||
|
MANGOS_ASSERT(nIndex < m_nParams);
|
||||||
|
|
||||||
|
MYSQL_BIND& pData = m_pInputArgs[nIndex];
|
||||||
|
|
||||||
|
my_bool bUnsigned = 0;
|
||||||
|
enum_field_types dataType = ToMySQLType(data, bUnsigned);
|
||||||
|
|
||||||
|
//setup MYSQL_BIND structure
|
||||||
|
pData.buffer_type = dataType;
|
||||||
|
pData.is_unsigned = bUnsigned;
|
||||||
|
pData.buffer = data.buff();
|
||||||
|
pData.length = 0;
|
||||||
|
pData.buffer_length = data.type() == FIELD_STRING ? data.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySqlPreparedStatement::RemoveBinds()
|
||||||
|
{
|
||||||
|
if(!m_stmt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delete [] m_pInputArgs;
|
||||||
|
delete [] m_pResult;
|
||||||
|
|
||||||
|
mysql_free_result(m_pResultMetadata);
|
||||||
|
mysql_stmt_close(m_stmt);
|
||||||
|
|
||||||
|
m_stmt = NULL;
|
||||||
|
m_pResultMetadata = NULL;
|
||||||
|
m_pResult = NULL;
|
||||||
|
m_pInputArgs = NULL;
|
||||||
|
|
||||||
|
m_bPrepared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MySqlPreparedStatement::execute()
|
||||||
|
{
|
||||||
|
if(!isPrepared())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(mysql_stmt_execute(m_stmt))
|
||||||
|
{
|
||||||
|
sLog.outError("SQL: cannot execute '%s'", m_szFmt.c_str());
|
||||||
|
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_field_types MySqlPreparedStatement::ToMySQLType( const SqlStmtFieldData &data, my_bool &bUnsigned )
|
||||||
|
{
|
||||||
|
bUnsigned = 0;
|
||||||
|
enum_field_types dataType = MYSQL_TYPE_NULL;
|
||||||
|
|
||||||
|
switch (data.type())
|
||||||
|
{
|
||||||
|
case FIELD_NONE: dataType = MYSQL_TYPE_NULL; break;
|
||||||
|
case FIELD_BOOL: dataType = MYSQL_TYPE_BIT; bUnsigned = 1; break;
|
||||||
|
case FIELD_I8: dataType = MYSQL_TYPE_TINY; break;
|
||||||
|
case FIELD_UI8: dataType = MYSQL_TYPE_TINY; bUnsigned = 1; break;
|
||||||
|
case FIELD_I16: dataType = MYSQL_TYPE_SHORT; break;
|
||||||
|
case FIELD_UI16: dataType = MYSQL_TYPE_SHORT; bUnsigned = 1; break;
|
||||||
|
case FIELD_I32: dataType = MYSQL_TYPE_LONG; break;
|
||||||
|
case FIELD_UI32: dataType = MYSQL_TYPE_LONG; bUnsigned = 1; break;
|
||||||
|
case FIELD_I64: dataType = MYSQL_TYPE_LONGLONG; break;
|
||||||
|
case FIELD_UI64: dataType = MYSQL_TYPE_LONGLONG; bUnsigned = 1; break;
|
||||||
|
case FIELD_FLOAT: dataType = MYSQL_TYPE_FLOAT; break;
|
||||||
|
case FIELD_DOUBLE: dataType = MYSQL_TYPE_DOUBLE; break;
|
||||||
|
case FIELD_STRING: dataType = MYSQL_TYPE_STRING; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataType;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
#ifndef _DATABASEMYSQL_H
|
#ifndef _DATABASEMYSQL_H
|
||||||
#define _DATABASEMYSQL_H
|
#define _DATABASEMYSQL_H
|
||||||
|
|
||||||
#include "Common.h"
|
//#include "Common.h"
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "Policies/Singleton.h"
|
#include "Policies/Singleton.h"
|
||||||
#include "ace/Thread_Mutex.h"
|
#include "ace/Thread_Mutex.h"
|
||||||
|
|
@ -34,10 +34,42 @@
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//MySQL prepared statement class
|
||||||
|
class MANGOS_DLL_SPEC MySqlPreparedStatement : public SqlPreparedStatement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MySqlPreparedStatement(const std::string& fmt, SqlConnection& conn, MYSQL * mysql);
|
||||||
|
~MySqlPreparedStatement();
|
||||||
|
|
||||||
|
//prepare statement
|
||||||
|
virtual bool prepare();
|
||||||
|
|
||||||
|
//bind input parameters
|
||||||
|
virtual void bind(const SqlStmtParameters& holder);
|
||||||
|
|
||||||
|
//execute DML statement
|
||||||
|
virtual bool execute();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//bind parameters
|
||||||
|
void addParam(int nIndex, const SqlStmtFieldData& data);
|
||||||
|
|
||||||
|
static enum_field_types ToMySQLType( const SqlStmtFieldData &data, my_bool &bUnsigned );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void RemoveBinds();
|
||||||
|
|
||||||
|
MYSQL * m_pMySQLConn;
|
||||||
|
MYSQL_STMT * m_stmt;
|
||||||
|
MYSQL_BIND * m_pInputArgs;
|
||||||
|
MYSQL_BIND * m_pResult;
|
||||||
|
MYSQL_RES *m_pResultMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
class MANGOS_DLL_SPEC MySQLConnection : public SqlConnection
|
class MANGOS_DLL_SPEC MySQLConnection : public SqlConnection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MySQLConnection() : mMysql(NULL) {}
|
MySQLConnection(Database& db) : SqlConnection(db), mMysql(NULL) {}
|
||||||
~MySQLConnection();
|
~MySQLConnection();
|
||||||
|
|
||||||
bool Initialize(const char *infoString);
|
bool Initialize(const char *infoString);
|
||||||
|
|
@ -52,6 +84,9 @@ class MANGOS_DLL_SPEC MySQLConnection : public SqlConnection
|
||||||
bool CommitTransaction();
|
bool CommitTransaction();
|
||||||
bool RollbackTransaction();
|
bool RollbackTransaction();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SqlPreparedStatement * CreateStatement(const std::string& fmt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _TransactionCmd(const char *sql);
|
bool _TransactionCmd(const char *sql);
|
||||||
bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount);
|
bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount);
|
||||||
|
|
|
||||||
|
|
@ -25,26 +25,26 @@
|
||||||
|
|
||||||
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
|
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
|
||||||
|
|
||||||
void SqlStatement::Execute(SqlConnection *conn)
|
bool SqlPlainRequest::Execute(SqlConnection *conn)
|
||||||
{
|
{
|
||||||
/// just do it
|
/// just do it
|
||||||
LOCK_DB_CONN(conn);
|
LOCK_DB_CONN(conn);
|
||||||
conn->Execute(m_sql);
|
return conn->Execute(m_sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
SqlTransaction::~SqlTransaction()
|
SqlTransaction::~SqlTransaction()
|
||||||
{
|
{
|
||||||
while(!m_queue.empty())
|
while(!m_queue.empty())
|
||||||
{
|
{
|
||||||
delete [] (const_cast<char*>(m_queue.back()));
|
delete m_queue.back();
|
||||||
m_queue.pop_back();
|
m_queue.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SqlTransaction::Execute(SqlConnection *conn)
|
bool SqlTransaction::Execute(SqlConnection *conn)
|
||||||
{
|
{
|
||||||
if(m_queue.empty())
|
if(m_queue.empty())
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
LOCK_DB_CONN(conn);
|
LOCK_DB_CONN(conn);
|
||||||
|
|
||||||
|
|
@ -53,30 +53,47 @@ void SqlTransaction::Execute(SqlConnection *conn)
|
||||||
const int nItems = m_queue.size();
|
const int nItems = m_queue.size();
|
||||||
for (int i = 0; i < nItems; ++i)
|
for (int i = 0; i < nItems; ++i)
|
||||||
{
|
{
|
||||||
const char *sql = m_queue[i];
|
SqlOperation * pStmt = m_queue[i];
|
||||||
|
|
||||||
if(!conn->Execute(sql))
|
if(!pStmt->Execute(conn))
|
||||||
{
|
{
|
||||||
conn->RollbackTransaction();
|
conn->RollbackTransaction();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->CommitTransaction();
|
return conn->CommitTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlPreparedRequest::SqlPreparedRequest(int nIndex, SqlStmtParameters * arg ) : m_nIndex(nIndex), m_param(arg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlPreparedRequest::~SqlPreparedRequest()
|
||||||
|
{
|
||||||
|
delete m_param;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlPreparedRequest::Execute( SqlConnection *conn )
|
||||||
|
{
|
||||||
|
LOCK_DB_CONN(conn);
|
||||||
|
return conn->ExecuteStmt(m_nIndex, *m_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ---- ASYNC QUERIES ----
|
/// ---- ASYNC QUERIES ----
|
||||||
|
|
||||||
void SqlQuery::Execute(SqlConnection *conn)
|
bool SqlQuery::Execute(SqlConnection *conn)
|
||||||
{
|
{
|
||||||
if(!m_callback || !m_queue)
|
if(!m_callback || !m_queue)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
LOCK_DB_CONN(conn);
|
LOCK_DB_CONN(conn);
|
||||||
/// execute the query and store the result in the callback
|
/// execute the query and store the result in the callback
|
||||||
m_callback->SetResult(conn->Query(m_sql));
|
m_callback->SetResult(conn->Query(m_sql));
|
||||||
/// add the callback to the sql result queue of the thread it originated from
|
/// add the callback to the sql result queue of the thread it originated from
|
||||||
m_queue->add(m_callback);
|
m_queue->add(m_callback);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SqlResultQueue::Update()
|
void SqlResultQueue::Update()
|
||||||
|
|
@ -190,10 +207,10 @@ void SqlQueryHolder::SetSize(size_t size)
|
||||||
m_queries.resize(size);
|
m_queries.resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SqlQueryHolderEx::Execute(SqlConnection *conn)
|
bool SqlQueryHolderEx::Execute(SqlConnection *conn)
|
||||||
{
|
{
|
||||||
if(!m_holder || !m_callback || !m_queue)
|
if(!m_holder || !m_callback || !m_queue)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
LOCK_DB_CONN(conn);
|
LOCK_DB_CONN(conn);
|
||||||
/// we can do this, we are friends
|
/// we can do this, we are friends
|
||||||
|
|
@ -207,4 +224,6 @@ void SqlQueryHolderEx::Execute(SqlConnection *conn)
|
||||||
|
|
||||||
/// sync with the caller thread
|
/// sync with the caller thread
|
||||||
m_queue->add(m_callback);
|
m_queue->add(m_callback);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,43 +31,53 @@
|
||||||
class Database;
|
class Database;
|
||||||
class SqlConnection;
|
class SqlConnection;
|
||||||
class SqlDelayThread;
|
class SqlDelayThread;
|
||||||
|
class SqlStmtParameters;
|
||||||
|
|
||||||
class SqlOperation
|
class SqlOperation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void OnRemove() { delete this; }
|
virtual void OnRemove() { delete this; }
|
||||||
virtual void Execute(SqlConnection *conn) = 0;
|
virtual bool Execute(SqlConnection *conn) = 0;
|
||||||
virtual ~SqlOperation() {}
|
virtual ~SqlOperation() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
|
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
|
||||||
|
|
||||||
class SqlStatement : public SqlOperation
|
class SqlPlainRequest : public SqlOperation
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const char *m_sql;
|
const char *m_sql;
|
||||||
public:
|
public:
|
||||||
SqlStatement(const char *sql) : m_sql(mangos_strdup(sql)){}
|
SqlPlainRequest(const char *sql) : m_sql(mangos_strdup(sql)){}
|
||||||
~SqlStatement() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
~SqlPlainRequest() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
||||||
void Execute(SqlConnection *conn);
|
bool Execute(SqlConnection *conn);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SqlTransaction : public SqlOperation
|
class SqlTransaction : public SqlOperation
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::vector<const char *> m_queue;
|
std::vector<SqlOperation * > m_queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SqlTransaction() {}
|
SqlTransaction() {}
|
||||||
~SqlTransaction();
|
~SqlTransaction();
|
||||||
|
|
||||||
void DelayExecute(const char *sql)
|
void DelayExecute(SqlOperation * sql) { m_queue.push_back(sql); }
|
||||||
{
|
|
||||||
char* _sql = mangos_strdup(sql);
|
|
||||||
m_queue.push_back(_sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Execute(SqlConnection *conn);
|
bool Execute(SqlConnection *conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SqlPreparedRequest : public SqlOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SqlPreparedRequest(int nIndex, SqlStmtParameters * arg);
|
||||||
|
~SqlPreparedRequest();
|
||||||
|
|
||||||
|
bool Execute(SqlConnection *conn);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int m_nIndex;
|
||||||
|
SqlStmtParameters * m_param;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// ---- ASYNC QUERIES ----
|
/// ---- ASYNC QUERIES ----
|
||||||
|
|
@ -95,7 +105,7 @@ class SqlQuery : public SqlOperation
|
||||||
SqlQuery(const char *sql, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue)
|
SqlQuery(const char *sql, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue)
|
||||||
: m_sql(mangos_strdup(sql)), m_callback(callback), m_queue(queue) {}
|
: m_sql(mangos_strdup(sql)), m_callback(callback), m_queue(queue) {}
|
||||||
~SqlQuery() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
~SqlQuery() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
||||||
void Execute(SqlConnection *conn);
|
bool Execute(SqlConnection *conn);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SqlQueryHolder
|
class SqlQueryHolder
|
||||||
|
|
@ -124,6 +134,6 @@ class SqlQueryHolderEx : public SqlOperation
|
||||||
public:
|
public:
|
||||||
SqlQueryHolderEx(SqlQueryHolder *holder, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue)
|
SqlQueryHolderEx(SqlQueryHolder *holder, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue)
|
||||||
: m_holder(holder), m_callback(callback), m_queue(queue) {}
|
: m_holder(holder), m_callback(callback), m_queue(queue) {}
|
||||||
void Execute(SqlConnection *conn);
|
bool Execute(SqlConnection *conn);
|
||||||
};
|
};
|
||||||
#endif //__SQLOPERATIONS_H
|
#endif //__SQLOPERATIONS_H
|
||||||
|
|
|
||||||
160
src/shared/Database/SqlPreparedStatement.cpp
Normal file
160
src/shared/Database/SqlPreparedStatement.cpp
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DatabaseEnv.h"
|
||||||
|
|
||||||
|
SqlStmtParameters::SqlStmtParameters( int nParams )
|
||||||
|
{
|
||||||
|
//reserve memory if needed
|
||||||
|
if(nParams > 0)
|
||||||
|
m_params.reserve(nParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlStmtParameters::reset( const SqlStatement& stmt )
|
||||||
|
{
|
||||||
|
m_params.clear();
|
||||||
|
//reserve memory if needed
|
||||||
|
if(stmt.arguments() > 0)
|
||||||
|
m_params.reserve(stmt.arguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
SqlStatement& SqlStatement::operator=( const SqlStatement& index )
|
||||||
|
{
|
||||||
|
if(this != &index)
|
||||||
|
{
|
||||||
|
m_index = index.m_index;
|
||||||
|
m_pDB = index.m_pDB;
|
||||||
|
|
||||||
|
if(m_pParams)
|
||||||
|
{
|
||||||
|
delete m_pParams;
|
||||||
|
m_pParams = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index.m_pParams)
|
||||||
|
m_pParams = new SqlStmtParameters(*(index.m_pParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlStatement::Execute()
|
||||||
|
{
|
||||||
|
SqlStmtParameters * args = detach();
|
||||||
|
//verify amount of bound parameters
|
||||||
|
if(args->boundParams() != arguments())
|
||||||
|
{
|
||||||
|
sLog.outError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
|
||||||
|
sLog.outError("SQL ERROR: statement: %s", m_pDB->GetStmtString(ID()).c_str());
|
||||||
|
MANGOS_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_pDB->ExecuteStmt(m_index, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlStatement::DirectExecute()
|
||||||
|
{
|
||||||
|
SqlStmtParameters * args = detach();
|
||||||
|
//verify amount of bound parameters
|
||||||
|
if(args->boundParams() != arguments())
|
||||||
|
{
|
||||||
|
sLog.outError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
|
||||||
|
sLog.outError("SQL ERROR: statement: %s", m_pDB->GetStmtString(ID()).c_str());
|
||||||
|
MANGOS_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_pDB->DirectExecuteStmt(m_index, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
SqlPlainPreparedStatement::SqlPlainPreparedStatement( const std::string& fmt, SqlConnection& conn ) : SqlPreparedStatement(fmt, conn)
|
||||||
|
{
|
||||||
|
m_bPrepared = true;
|
||||||
|
m_nParams = std::count(m_szFmt.begin(), m_szFmt.end(), '?');
|
||||||
|
m_bIsQuery = strnicmp(m_szFmt.c_str(), "select", 6) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlPlainPreparedStatement::bind( const SqlStmtParameters& holder )
|
||||||
|
{
|
||||||
|
//verify if we bound all needed input parameters
|
||||||
|
if(m_nParams != holder.boundParams())
|
||||||
|
{
|
||||||
|
MANGOS_ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset resulting plain SQL request
|
||||||
|
m_szPlainRequest = m_szFmt;
|
||||||
|
size_t nLastPos = 0;
|
||||||
|
|
||||||
|
SqlStmtParameters::ParameterContainer const& _args = holder.params();
|
||||||
|
|
||||||
|
SqlStmtParameters::ParameterContainer::const_iterator iter_last = _args.end();
|
||||||
|
for (SqlStmtParameters::ParameterContainer::const_iterator iter = _args.begin(); iter != iter_last; ++iter)
|
||||||
|
{
|
||||||
|
//bind parameter
|
||||||
|
const SqlStmtFieldData& data = (*iter);
|
||||||
|
|
||||||
|
std::ostringstream fmt;
|
||||||
|
DataToString(data, fmt);
|
||||||
|
|
||||||
|
nLastPos = m_szPlainRequest.find('?', nLastPos);
|
||||||
|
if(nLastPos != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string tmp = fmt.str();
|
||||||
|
m_szPlainRequest.replace(nLastPos, 1, tmp);
|
||||||
|
nLastPos += tmp.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SqlPlainPreparedStatement::execute()
|
||||||
|
{
|
||||||
|
if(m_szPlainRequest.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_pConn.Execute(m_szPlainRequest.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlPlainPreparedStatement::DataToString( const SqlStmtFieldData& data, std::ostringstream& fmt )
|
||||||
|
{
|
||||||
|
switch (data.type())
|
||||||
|
{
|
||||||
|
case FIELD_BOOL: fmt << "'" << uint32(data.toBool()) << "'"; break;
|
||||||
|
case FIELD_UI8: fmt << "'" << uint32(data.toUint8()) << "'"; break;
|
||||||
|
case FIELD_UI16: fmt << "'" << uint32(data.toUint16()) << "'"; break;
|
||||||
|
case FIELD_UI32: fmt << "'" << data.toUint32() << "'"; break;
|
||||||
|
case FIELD_UI64: fmt << "'" << data.toUint64() << "'"; break;
|
||||||
|
case FIELD_I8: fmt << "'" << int32(data.toInt8()) << "'"; break;
|
||||||
|
case FIELD_I16: fmt << "'" << int32(data.toInt16()) << "'"; break;
|
||||||
|
case FIELD_I32: fmt << "'" << data.toInt32() << "'"; break;
|
||||||
|
case FIELD_I64: fmt << "'" << data.toInt64() << "'"; break;
|
||||||
|
case FIELD_FLOAT: fmt << "'" << data.toFloat() << "'"; break;
|
||||||
|
case FIELD_DOUBLE: fmt << "'" << data.toDouble() << "'"; break;
|
||||||
|
case FIELD_STRING:
|
||||||
|
{
|
||||||
|
std::string tmp = data.toStr();
|
||||||
|
m_pConn.DB().escape_string(tmp);
|
||||||
|
fmt << "'" << tmp << "'";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
352
src/shared/Database/SqlPreparedStatement.h
Normal file
352
src/shared/Database/SqlPreparedStatement.h
Normal file
|
|
@ -0,0 +1,352 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SQLPREPAREDSTATEMENTS_H
|
||||||
|
#define SQLPREPAREDSTATEMENTS_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include <ace/TSS_T.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
class Database;
|
||||||
|
class SqlConnection;
|
||||||
|
class QueryResult;
|
||||||
|
|
||||||
|
union SqlStmtField
|
||||||
|
{
|
||||||
|
bool boolean;
|
||||||
|
uint8 ui8;
|
||||||
|
int8 i8;
|
||||||
|
uint16 ui16;
|
||||||
|
int16 i16;
|
||||||
|
uint32 ui32;
|
||||||
|
int32 i32;
|
||||||
|
uint64 ui64;
|
||||||
|
int64 i64;
|
||||||
|
float f;
|
||||||
|
double d;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SqlStmtFieldType
|
||||||
|
{
|
||||||
|
FIELD_BOOL,
|
||||||
|
FIELD_UI8,
|
||||||
|
FIELD_UI16,
|
||||||
|
FIELD_UI32,
|
||||||
|
FIELD_UI64,
|
||||||
|
FIELD_I8,
|
||||||
|
FIELD_I16,
|
||||||
|
FIELD_I32,
|
||||||
|
FIELD_I64,
|
||||||
|
FIELD_FLOAT,
|
||||||
|
FIELD_DOUBLE,
|
||||||
|
FIELD_STRING,
|
||||||
|
FIELD_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
//templates might be the best choice here
|
||||||
|
//but I didn't have time to play with them
|
||||||
|
class MANGOS_DLL_SPEC SqlStmtFieldData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SqlStmtFieldData() : m_type(FIELD_NONE) { m_binaryData.ui64 = 0; }
|
||||||
|
~SqlStmtFieldData() {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
SqlStmtFieldData(T param) { set(param); }
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
void set(T1 param1);
|
||||||
|
|
||||||
|
//getters
|
||||||
|
bool toBool() const { MANGOS_ASSERT(m_type == FIELD_BOOL); return m_binaryData.boolean; }
|
||||||
|
uint8 toUint8() const { MANGOS_ASSERT(m_type == FIELD_UI8); return m_binaryData.ui8; }
|
||||||
|
int8 toInt8() const { MANGOS_ASSERT(m_type == FIELD_I8); return m_binaryData.i8; }
|
||||||
|
uint16 toUint16() const { MANGOS_ASSERT(m_type == FIELD_UI16); return m_binaryData.ui16; }
|
||||||
|
int16 toInt16() const { MANGOS_ASSERT(m_type == FIELD_I16); return m_binaryData.i16; }
|
||||||
|
uint32 toUint32() const { MANGOS_ASSERT(m_type == FIELD_UI32); return m_binaryData.ui32; }
|
||||||
|
int32 toInt32() const { MANGOS_ASSERT(m_type == FIELD_I32); return m_binaryData.i32; }
|
||||||
|
uint64 toUint64() const { MANGOS_ASSERT(m_type == FIELD_UI64); return m_binaryData.ui64; }
|
||||||
|
int64 toInt64() const { MANGOS_ASSERT(m_type == FIELD_I64); return m_binaryData.i64; }
|
||||||
|
float toFloat() const { MANGOS_ASSERT(m_type == FIELD_FLOAT); return m_binaryData.f; }
|
||||||
|
double toDouble() const { MANGOS_ASSERT(m_type == FIELD_DOUBLE); return m_binaryData.d; }
|
||||||
|
const char * toStr() const { MANGOS_ASSERT(m_type == FIELD_STRING); return m_szStringData.c_str(); }
|
||||||
|
|
||||||
|
//get type of data
|
||||||
|
SqlStmtFieldType type() const { return m_type; }
|
||||||
|
//get underlying buffer type
|
||||||
|
void * buff() const { return m_type == FIELD_STRING ? (void * )m_szStringData.c_str() : (void *)&m_binaryData; }
|
||||||
|
|
||||||
|
//get size of data
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case FIELD_NONE: return 0;
|
||||||
|
case FIELD_BOOL: return sizeof(bool);
|
||||||
|
case FIELD_UI8: return sizeof(uint8);
|
||||||
|
case FIELD_UI16: return sizeof(uint16);
|
||||||
|
case FIELD_UI32: return sizeof(uint32);
|
||||||
|
case FIELD_UI64: return sizeof(uint64);
|
||||||
|
case FIELD_I8: return sizeof(int8);
|
||||||
|
case FIELD_I16: return sizeof(int16);
|
||||||
|
case FIELD_I32: return sizeof(int32);
|
||||||
|
case FIELD_I64: return sizeof(int64);
|
||||||
|
case FIELD_FLOAT: return sizeof(float);
|
||||||
|
case FIELD_DOUBLE: return sizeof(double);
|
||||||
|
case FIELD_STRING: return m_szStringData.length();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("unrecognized type of SqlStmtFieldType obtained");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SqlStmtFieldType m_type;
|
||||||
|
SqlStmtField m_binaryData;
|
||||||
|
std::string m_szStringData;
|
||||||
|
};
|
||||||
|
|
||||||
|
//template specialization
|
||||||
|
template<> inline void SqlStmtFieldData::set(bool val) { m_type = FIELD_BOOL; m_binaryData.boolean = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(uint8 val) { m_type = FIELD_UI8; m_binaryData.ui8 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(int8 val) { m_type = FIELD_I8; m_binaryData.i8 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(uint16 val) { m_type = FIELD_UI16; m_binaryData.ui16 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(int16 val) { m_type = FIELD_I16; m_binaryData.i16 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(uint32 val) { m_type = FIELD_UI32; m_binaryData.ui32 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(int32 val) { m_type = FIELD_I32; m_binaryData.i32 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(uint64 val) { m_type = FIELD_UI64; m_binaryData.ui64 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(int64 val) { m_type = FIELD_I64; m_binaryData.i64 = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(float val) { m_type = FIELD_FLOAT; m_binaryData.f = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(double val) { m_type = FIELD_DOUBLE; m_binaryData.d = val; }
|
||||||
|
template<> inline void SqlStmtFieldData::set(const char * val) { m_type = FIELD_STRING; m_szStringData = val; }
|
||||||
|
|
||||||
|
class SqlStatement;
|
||||||
|
//prepared statement executor
|
||||||
|
class MANGOS_DLL_SPEC SqlStmtParameters
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::vector<SqlStmtFieldData> ParameterContainer;
|
||||||
|
|
||||||
|
//reserve memory to contain all input parameters of stmt
|
||||||
|
explicit SqlStmtParameters(int nParams);
|
||||||
|
|
||||||
|
~SqlStmtParameters() {}
|
||||||
|
|
||||||
|
//get amount of bound parameters
|
||||||
|
int boundParams() const { return int(m_params.size()); }
|
||||||
|
//add parameter
|
||||||
|
void addParam(const SqlStmtFieldData& data) { m_params.push_back(data); }
|
||||||
|
//empty SQL statement parameters. In case nParams > 1 - reserve memory for parameters
|
||||||
|
//should help to reuse the same object with batched SQL requests
|
||||||
|
void reset(const SqlStatement& stmt);
|
||||||
|
//swaps contents of intenral param container
|
||||||
|
void swap(SqlStmtParameters& obj);
|
||||||
|
//get bound parameters
|
||||||
|
const ParameterContainer& params() const { return m_params; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SqlStmtParameters& operator=(const SqlStmtParameters& obj);
|
||||||
|
|
||||||
|
//statement parameter holder
|
||||||
|
ParameterContainer m_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
//statement ID encapsulation logic
|
||||||
|
class SqlStatementID
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SqlStatementID() : m_bInitialized(false) {}
|
||||||
|
|
||||||
|
int ID() const { return m_nIndex; }
|
||||||
|
int arguments() const { return m_nArguments; }
|
||||||
|
bool initialized() const { return m_bInitialized; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Database;
|
||||||
|
void init(int nID, int nArgs) { m_nIndex = nID; m_nArguments = nArgs; m_bInitialized = true; }
|
||||||
|
|
||||||
|
int m_nIndex;
|
||||||
|
int m_nArguments;
|
||||||
|
bool m_bInitialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
//statement index
|
||||||
|
class MANGOS_DLL_SPEC SqlStatement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~SqlStatement() { delete m_pParams; }
|
||||||
|
|
||||||
|
SqlStatement(const SqlStatement& index) : m_index(index.m_index), m_pDB(index.m_pDB), m_pParams(NULL)
|
||||||
|
{
|
||||||
|
if(index.m_pParams)
|
||||||
|
m_pParams = new SqlStmtParameters(*(index.m_pParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlStatement& operator=(const SqlStatement& index);
|
||||||
|
|
||||||
|
int ID() const { return m_index.ID(); }
|
||||||
|
int arguments() const { return m_index.arguments(); }
|
||||||
|
|
||||||
|
bool Execute();
|
||||||
|
bool DirectExecute();
|
||||||
|
|
||||||
|
//templates to simplify 1-4 parameter bindings
|
||||||
|
template<typename ParamType1>
|
||||||
|
bool PExecute(ParamType1 param1)
|
||||||
|
{
|
||||||
|
arg(param1);
|
||||||
|
return Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ParamType1, typename ParamType2>
|
||||||
|
bool PExecute(ParamType1 param1, ParamType2 param2)
|
||||||
|
{
|
||||||
|
arg(param1);
|
||||||
|
arg(param2);
|
||||||
|
return Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ParamType1, typename ParamType2, typename ParamType3>
|
||||||
|
bool PExecute(ParamType1 param1, ParamType2 param2, ParamType3 param3)
|
||||||
|
{
|
||||||
|
arg(param1);
|
||||||
|
arg(param2);
|
||||||
|
arg(param3);
|
||||||
|
return Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ParamType1, typename ParamType2, typename ParamType3, typename ParamType4>
|
||||||
|
bool PExecute(ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4)
|
||||||
|
{
|
||||||
|
arg(param1);
|
||||||
|
arg(param2);
|
||||||
|
arg(param3);
|
||||||
|
arg(param4);
|
||||||
|
return Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
//bind parameters with specified type
|
||||||
|
void addBool(bool var) { arg(var); }
|
||||||
|
void addUInt8(uint8 var) { arg(var); }
|
||||||
|
void addInt8(int8 var) { arg(var); }
|
||||||
|
void addUInt16(uint16 var) { arg(var); }
|
||||||
|
void addInt16(int16 var) { arg(var); }
|
||||||
|
void addUInt32(uint32 var) { arg(var); }
|
||||||
|
void addInt32(int32 var) { arg(var); }
|
||||||
|
void addUInt64(uint64 var) { arg(var); }
|
||||||
|
void addInt64(int64 var) { arg(var); }
|
||||||
|
void addFloat(float var) { arg(var); }
|
||||||
|
void addDouble(double var) { arg(var); }
|
||||||
|
void addString(const char * var) { arg(var); }
|
||||||
|
void addString(const std::string& var) { arg(var.c_str()); }
|
||||||
|
void addString(std::ostringstream& ss) { arg(ss.str().c_str()); ss.str(std::string()); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//don't allow anyone except Database class to create static SqlStatement objects
|
||||||
|
friend class Database;
|
||||||
|
SqlStatement(const SqlStatementID& index, Database& db) : m_index(index), m_pDB(&db), m_pParams(NULL) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
SqlStmtParameters * get()
|
||||||
|
{
|
||||||
|
if(!m_pParams)
|
||||||
|
m_pParams = new SqlStmtParameters(arguments());
|
||||||
|
|
||||||
|
return m_pParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlStmtParameters * detach()
|
||||||
|
{
|
||||||
|
SqlStmtParameters * p = m_pParams ? m_pParams : new SqlStmtParameters(0);
|
||||||
|
m_pParams = NULL;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function
|
||||||
|
//use appropriate add* functions to bind specific data type
|
||||||
|
template<typename ParamType>
|
||||||
|
void arg(ParamType val)
|
||||||
|
{
|
||||||
|
SqlStmtParameters * p = get();
|
||||||
|
p->addParam(SqlStmtFieldData(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlStatementID m_index;
|
||||||
|
Database * m_pDB;
|
||||||
|
SqlStmtParameters * m_pParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
//base prepared statement class
|
||||||
|
class MANGOS_DLL_SPEC SqlPreparedStatement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~SqlPreparedStatement() {}
|
||||||
|
|
||||||
|
bool isPrepared() const { return m_bPrepared; }
|
||||||
|
bool isQuery() const { return m_bIsQuery; }
|
||||||
|
|
||||||
|
uint32 params() const { return m_nParams; }
|
||||||
|
uint32 columns() const { return isQuery() ? m_nColumns : 0; }
|
||||||
|
|
||||||
|
//initialize internal structures of prepared statement
|
||||||
|
//upon success m_bPrepared should be true
|
||||||
|
virtual bool prepare() = 0;
|
||||||
|
//bind parameters for prepared statement from parameter placeholder
|
||||||
|
virtual void bind(const SqlStmtParameters& holder) = 0;
|
||||||
|
|
||||||
|
//execute statement w/o result set
|
||||||
|
virtual bool execute() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SqlPreparedStatement(const std::string& fmt, SqlConnection& conn) : m_szFmt(fmt), m_nParams(0), m_nColumns(0), m_bPrepared(false), m_bIsQuery(false), m_pConn(conn) {}
|
||||||
|
|
||||||
|
uint32 m_nParams;
|
||||||
|
uint32 m_nColumns;
|
||||||
|
bool m_bIsQuery;
|
||||||
|
bool m_bPrepared;
|
||||||
|
std::string m_szFmt;
|
||||||
|
SqlConnection& m_pConn;
|
||||||
|
};
|
||||||
|
|
||||||
|
//prepared statements via plain SQL string requests
|
||||||
|
class MANGOS_DLL_SPEC SqlPlainPreparedStatement : public SqlPreparedStatement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SqlPlainPreparedStatement(const std::string& fmt, SqlConnection& conn);
|
||||||
|
~SqlPlainPreparedStatement() {}
|
||||||
|
|
||||||
|
//this statement is always prepared
|
||||||
|
virtual bool prepare() { return true; }
|
||||||
|
|
||||||
|
//we should replace all '?' symbols with substrings with proper format
|
||||||
|
virtual void bind(const SqlStmtParameters& holder);
|
||||||
|
|
||||||
|
virtual bool execute();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void DataToString(const SqlStmtFieldData& data, std::ostringstream& fmt);
|
||||||
|
|
||||||
|
std::string m_szPlainRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#ifndef __REVISION_NR_H__
|
#ifndef __REVISION_NR_H__
|
||||||
#define __REVISION_NR_H__
|
#define __REVISION_NR_H__
|
||||||
#define REVISION_NR "11283"
|
#define REVISION_NR "11284"
|
||||||
#endif // __REVISION_NR_H__
|
#endif // __REVISION_NR_H__
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
<ProjectConfiguration Include="Debug_NoPCH|Win32">
|
<ProjectConfiguration Include="Debug_NoPCH|Win32">
|
||||||
<Configuration>Debug_NoPCH</Configuration>
|
<Configuration>Debug_NoPCH</Configuration>
|
||||||
|
|
@ -441,6 +442,7 @@
|
||||||
<ClCompile Include="..\..\src\shared\Database\QueryResultMysql.cpp" />
|
<ClCompile Include="..\..\src\shared\Database\QueryResultMysql.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\Database\SqlDelayThread.cpp" />
|
<ClCompile Include="..\..\src\shared\Database\SqlDelayThread.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\Database\SqlOperations.cpp" />
|
<ClCompile Include="..\..\src\shared\Database\SqlOperations.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\shared\Database\SqlPreparedStatement.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\Database\SQLStorage.cpp" />
|
<ClCompile Include="..\..\src\shared\Database\SQLStorage.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\Log.cpp" />
|
<ClCompile Include="..\..\src\shared\Log.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\ProgressBar.cpp" />
|
<ClCompile Include="..\..\src\shared\ProgressBar.cpp" />
|
||||||
|
|
@ -456,6 +458,7 @@
|
||||||
<ClInclude Include="..\..\src\shared\Auth\SARC4.h" />
|
<ClInclude Include="..\..\src\shared\Auth\SARC4.h" />
|
||||||
<ClInclude Include="..\..\src\shared\Auth\Sha1.h" />
|
<ClInclude Include="..\..\src\shared\Auth\Sha1.h" />
|
||||||
<ClInclude Include="..\..\src\shared\ByteBuffer.h" />
|
<ClInclude Include="..\..\src\shared\ByteBuffer.h" />
|
||||||
|
<ClInclude Include="..\..\src\shared\Database\SqlPreparedStatement.h" />
|
||||||
<ClInclude Include="..\..\src\shared\WorldPacket.h" />
|
<ClInclude Include="..\..\src\shared\WorldPacket.h" />
|
||||||
<ClInclude Include="..\..\src\shared\Common.h" />
|
<ClInclude Include="..\..\src\shared\Common.h" />
|
||||||
<ClInclude Include="..\..\src\shared\Config\Config.h" />
|
<ClInclude Include="..\..\src\shared\Config\Config.h" />
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,9 @@
|
||||||
<ClCompile Include="..\..\src\shared\Common.cpp" />
|
<ClCompile Include="..\..\src\shared\Common.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\ServiceWin32.cpp" />
|
<ClCompile Include="..\..\src\shared\ServiceWin32.cpp" />
|
||||||
<ClCompile Include="..\..\src\shared\Threading.cpp" />
|
<ClCompile Include="..\..\src\shared\Threading.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\shared\Database\SqlPreparedStatement.cpp">
|
||||||
|
<Filter>Database</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\src\shared\Database\Database.h">
|
<ClInclude Include="..\..\src\shared\Database\Database.h">
|
||||||
|
|
@ -167,6 +170,9 @@
|
||||||
<ClInclude Include="..\..\src\shared\revision_sql.h" />
|
<ClInclude Include="..\..\src\shared\revision_sql.h" />
|
||||||
<ClInclude Include="..\..\src\shared\ServiceWin32.h" />
|
<ClInclude Include="..\..\src\shared\ServiceWin32.h" />
|
||||||
<ClInclude Include="..\..\src\shared\Threading.h" />
|
<ClInclude Include="..\..\src\shared\Threading.h" />
|
||||||
|
<ClInclude Include="..\..\src\shared\Database\SqlPreparedStatement.h">
|
||||||
|
<Filter>Database</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<CustomBuild Include="..\..\src\shared\revision.h" />
|
<CustomBuild Include="..\..\src\shared\revision.h" />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="Windows-1252"?>
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
<VisualStudioProject
|
<VisualStudioProject
|
||||||
ProjectType="Visual C++"
|
ProjectType="Visual C++"
|
||||||
Version="8.00"
|
Version="8,00"
|
||||||
Name="mangosd"
|
Name="mangosd"
|
||||||
ProjectGUID="{A3A04E47-43A2-4C08-90B3-029CEF558594}"
|
ProjectGUID="{A3A04E47-43A2-4C08-90B3-029CEF558594}"
|
||||||
RootNamespace="mangosd"
|
RootNamespace="mangosd"
|
||||||
|
|
|
||||||
|
|
@ -260,85 +260,6 @@
|
||||||
Name="VCPostBuildEventTool"
|
Name="VCPostBuildEventTool"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration
|
|
||||||
Name="Debug_NoPCH|Win32"
|
|
||||||
OutputDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
|
||||||
IntermediateDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
|
||||||
ConfigurationType="4"
|
|
||||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
|
||||||
UseOfMFC="0"
|
|
||||||
ATLMinimizesCRunTimeLibraryUsage="false"
|
|
||||||
CharacterSet="2"
|
|
||||||
>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreBuildEventTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCustomBuildTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCXMLDataGeneratorTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebServiceProxyGeneratorTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
AdditionalOptions="/MP"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories="..\..\dep\include;..\..\src\framework;..\..\src\shared;..\..\dep\ACE_wrappers"
|
|
||||||
PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE"
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
RuntimeLibrary="3"
|
|
||||||
RuntimeTypeInfo="true"
|
|
||||||
UsePrecompiledHeader="0"
|
|
||||||
PrecompiledHeaderFile=".\shared__$(PlatformName)_$(ConfigurationName)\shared.pch"
|
|
||||||
AssemblerListingLocation=".\shared__$(PlatformName)_$(ConfigurationName)\"
|
|
||||||
ObjectFile=".\shared__$(PlatformName)_$(ConfigurationName)\"
|
|
||||||
ProgramDataBaseFileName=".\shared__$(PlatformName)_$(ConfigurationName)\"
|
|
||||||
WarningLevel="3"
|
|
||||||
SuppressStartupBanner="true"
|
|
||||||
Detect64BitPortabilityProblems="true"
|
|
||||||
DebugInformationFormat="3"
|
|
||||||
CompileAs="0"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCManagedResourceCompilerTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCResourceCompilerTool"
|
|
||||||
PreprocessorDefinitions="_DEBUG"
|
|
||||||
Culture="1033"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreLinkEventTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCLibrarianTool"
|
|
||||||
AdditionalDependencies="user32.lib"
|
|
||||||
OutputFile=".\shared__$(PlatformName)_$(ConfigurationName)\shared.lib"
|
|
||||||
AdditionalLibraryDirectories="dep\lib\$(PlatformName)_debug"
|
|
||||||
SuppressStartupBanner="true"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCALinkTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCXDCMakeTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCBscMakeTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCFxCopTool"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPostBuildEventTool"
|
|
||||||
/>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Debug|x64"
|
Name="Debug|x64"
|
||||||
OutputDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
OutputDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
||||||
|
|
@ -420,6 +341,85 @@
|
||||||
Name="VCPostBuildEventTool"
|
Name="VCPostBuildEventTool"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug_NoPCH|Win32"
|
||||||
|
OutputDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
||||||
|
IntermediateDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
||||||
|
ConfigurationType="4"
|
||||||
|
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||||
|
UseOfMFC="0"
|
||||||
|
ATLMinimizesCRunTimeLibraryUsage="false"
|
||||||
|
CharacterSet="2"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
AdditionalOptions="/MP"
|
||||||
|
Optimization="0"
|
||||||
|
AdditionalIncludeDirectories="..\..\dep\include;..\..\src\framework;..\..\src\shared;..\..\dep\ACE_wrappers"
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="3"
|
||||||
|
RuntimeTypeInfo="true"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
PrecompiledHeaderFile=".\shared__$(PlatformName)_$(ConfigurationName)\shared.pch"
|
||||||
|
AssemblerListingLocation=".\shared__$(PlatformName)_$(ConfigurationName)\"
|
||||||
|
ObjectFile=".\shared__$(PlatformName)_$(ConfigurationName)\"
|
||||||
|
ProgramDataBaseFileName=".\shared__$(PlatformName)_$(ConfigurationName)\"
|
||||||
|
WarningLevel="3"
|
||||||
|
SuppressStartupBanner="true"
|
||||||
|
Detect64BitPortabilityProblems="true"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
CompileAs="0"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
PreprocessorDefinitions="_DEBUG"
|
||||||
|
Culture="1033"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLibrarianTool"
|
||||||
|
AdditionalDependencies="user32.lib"
|
||||||
|
OutputFile=".\shared__$(PlatformName)_$(ConfigurationName)\shared.lib"
|
||||||
|
AdditionalLibraryDirectories="dep\lib\$(PlatformName)_debug"
|
||||||
|
SuppressStartupBanner="true"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Debug_NoPCH|x64"
|
Name="Debug_NoPCH|x64"
|
||||||
OutputDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
OutputDirectory=".\shared__$(PlatformName)_$(ConfigurationName)"
|
||||||
|
|
@ -572,6 +572,14 @@
|
||||||
RelativePath="..\..\src\shared\Database\SqlOperations.h"
|
RelativePath="..\..\src\shared\Database\SqlOperations.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\shared\Database\SqlPreparedStatement.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\shared\Database\SqlPreparedStatement.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\shared\Database\SQLStorage.cpp"
|
RelativePath="..\..\src\shared\Database\SQLStorage.cpp"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="Windows-1252"?>
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
<VisualStudioProject
|
<VisualStudioProject
|
||||||
ProjectType="Visual C++"
|
ProjectType="Visual C++"
|
||||||
Version="9.00"
|
Version="9,00"
|
||||||
Name="shared"
|
Name="shared"
|
||||||
ProjectGUID="{90297C34-F231-4DF4-848E-A74BCC0E40ED}"
|
ProjectGUID="{90297C34-F231-4DF4-848E-A74BCC0E40ED}"
|
||||||
RootNamespace="shared"
|
RootNamespace="shared"
|
||||||
|
|
@ -575,6 +575,14 @@
|
||||||
RelativePath="..\..\src\shared\Database\SqlOperations.h"
|
RelativePath="..\..\src\shared\Database\SqlOperations.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\shared\Database\SqlPreparedStatement.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\src\shared\Database\SqlPreparedStatement.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\src\shared\Database\SQLStorage.cpp"
|
RelativePath="..\..\src\shared\Database\SQLStorage.cpp"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue