From 40ef9cbf2f8f1fbdcf80c91e88ed9a9377f9904e Mon Sep 17 00:00:00 2001 From: Ambal Date: Fri, 25 Mar 2011 21:52:59 +0200 Subject: [PATCH] [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 --- src/framework/Utilities/Callback.h | 25 + src/game/AchievementMgr.cpp | 92 +-- src/game/Item.cpp | 106 +++- src/game/MapPersistentStateMgr.cpp | 28 +- src/game/Pet.cpp | 167 +++-- src/game/Player.cpp | 619 +++++++++++++------ src/game/Player.h | 12 +- src/game/ReputationMgr.cpp | 10 +- src/game/SpellHandler.cpp | 6 +- src/game/WorldSession.cpp | 25 +- src/shared/Database/Database.cpp | 150 ++++- src/shared/Database/Database.h | 51 +- src/shared/Database/DatabaseMysql.cpp | 193 +++++- src/shared/Database/DatabaseMysql.h | 39 +- src/shared/Database/SqlOperations.cpp | 45 +- src/shared/Database/SqlOperations.h | 38 +- src/shared/Database/SqlPreparedStatement.cpp | 160 +++++ src/shared/Database/SqlPreparedStatement.h | 352 +++++++++++ src/shared/revision_nr.h | 2 +- win/VC100/shared.vcxproj | 7 +- win/VC100/shared.vcxproj.filters | 6 + win/VC80/mangosd.vcproj | 2 +- win/VC80/shared.vcproj | 166 ++--- win/VC90/shared.vcproj | 10 +- 24 files changed, 1823 insertions(+), 488 deletions(-) create mode 100644 src/shared/Database/SqlPreparedStatement.cpp create mode 100644 src/shared/Database/SqlPreparedStatement.h diff --git a/src/framework/Utilities/Callback.h b/src/framework/Utilities/Callback.h index fb6349c4b..64bbd46ca 100644 --- a/src/framework/Utilities/Callback.h +++ b/src/framework/Utilities/Callback.h @@ -19,6 +19,31 @@ #ifndef 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 ------------ namespace MaNGOS diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index cd924ad57..2996e5b95 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -478,77 +478,45 @@ void AchievementMgr::DeleteFromDB(ObjectGuid guid) void AchievementMgr::SaveToDB() { + static SqlStatementID delComplAchievements ; + static SqlStatementID insComplAchievements ; + static SqlStatementID delProgress ; + static SqlStatementID insProgress ; + if(!m_completedAchievements.empty()) { - bool need_execute = false; - std::ostringstream ssdel; - std::ostringstream ssins; + //delete existing achievements in the loop for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter) { if(!iter->second.changed) 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 << "("<GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")"; - /// mark as saved in db iter->second.changed = false; - } - if(need_execute) - ssdel << ")"; + SqlStatement stmt = CharacterDatabase.CreateStatement(delComplAchievements, "DELETE FROM character_achievement WHERE guid = ? AND achievement = ?"); + stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first); - if(need_execute) - { - CharacterDatabase.Execute( ssdel.str().c_str() ); - CharacterDatabase.Execute( ssins.str().c_str() ); + stmt = CharacterDatabase.CreateStatement(insComplAchievements, "INSERT INTO character_achievement (guid, achievement, date) VALUES (?, ?, ?)"); + stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first, uint64(iter->second.date)); } } if(!m_criteriaProgress.empty()) { - /// prepare deleting and insert - bool need_execute_del = false; - bool need_execute_ins = false; - std::ostringstream ssdel; - std::ostringstream ssins; + //insert achievements for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) { if(!iter->second.changed) continue; - // deleted data (including 0 progress state) - { - /// 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 << ", "; + /// mark as updated in db + iter->second.changed = false; - // new/changed record data - ssdel << iter->first; - } + // new/changed record data + 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; if (!needSave) { @@ -558,33 +526,9 @@ void AchievementMgr::SaveToDB() if (needSave) { - /// first new/changed record prefix - if(!need_execute_ins) - { - 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 << ")"; + stmt = CharacterDatabase.CreateStatement(insProgress, "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES (?, ?, ?, ?)"); + stmt.PExecute(GetPlayer()->GetGUIDLow(), iter->first, iter->second.counter, uint64(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() ); } } } diff --git a/src/game/Item.cpp b/src/game/Item.cpp index 8bbd5a697..1b0906734 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -295,40 +295,58 @@ void Item::SaveToDB() { case ITEM_NEW: { - std::string text = m_text; - CharacterDatabase.escape_string(text); - CharacterDatabase.PExecute( "DELETE FROM item_instance WHERE guid = '%u'", guid ); + static SqlStatementID delItem ; + static SqlStatementID insItem ; + + SqlStatement stmt = CharacterDatabase.CreateStatement(delItem, "DELETE FROM item_instance WHERE guid = ?"); + stmt.PExecute(guid); + 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 ) 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; case ITEM_CHANGED: { - std::string text = m_text; - CharacterDatabase.escape_string(text); + static SqlStatementID updInstance ; + static SqlStatementID updGifts ; + + SqlStatement stmt = CharacterDatabase.CreateStatement(updInstance, "UPDATE item_instance SET data = ?, owner_guid = ?, text = ? WHERE guid = ?"); + std::ostringstream ss; - ss << "UPDATE item_instance SET data = '"; for(uint16 i = 0; i < m_valuesCount; ++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)) - 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; 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)) - 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()) - 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; return; @@ -338,17 +356,28 @@ void Item::SaveToDB() } 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(Player* owner = GetOwner()) { + static SqlStatementID saveGold ; + static SqlStatementID saveLoot ; + // save money as 0 itemid data if (loot.gold) - CharacterDatabase.PExecute("INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) " - "VALUES (%u, %u, 0, %u, 0, 0)", - GetGUIDLow(), owner->GetGUIDLow(), loot.gold); + { + SqlStatement stmt = CharacterDatabase.CreateStatement(saveGold, "INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) VALUES (?, ?, 0, ?, 0, 0)"); + 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) for (size_t i = 0; i < loot.GetMaxSlotInLootFor(owner); ++i) @@ -363,9 +392,14 @@ void Item::SaveToDB() if (!qitem && item->is_blocked) continue; - CharacterDatabase.PExecute("INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) " - "VALUES (%u, %u, %u, %u, %u, %i)", - GetGUIDLow(), owner->GetGUIDLow(), item->itemid, item->count, item->randomSuffix, item->randomPropertyId); + stmt.addUInt32(GetGUIDLow()); + stmt.addUInt32(owner->GetGUIDLow()); + 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); need_save = true; + static SqlStatementID delGifts ; + // 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 { + static SqlStatementID updItem ; + + SqlStatement stmt = CharacterDatabase.CreateStatement(updItem, "UPDATE item_instance SET data = ?, owner_guid = ? WHERE guid = ?"); + std::ostringstream ss; - ss << "UPDATE item_instance SET data = '"; for(uint16 i = 0; i < m_valuesCount; ++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; @@ -504,12 +546,18 @@ void Item::LoadLootFromDB(Field *fields) 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() { - 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 diff --git a/src/game/MapPersistentStateMgr.cpp b/src/game/MapPersistentStateMgr.cpp index 9622ce688..814e0f4be 100644 --- a/src/game/MapPersistentStateMgr.cpp +++ b/src/game/MapPersistentStateMgr.cpp @@ -77,9 +77,19 @@ void MapPersistentState::SaveCreatureRespawnTime(uint32 loguid, time_t t) return; 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()) - 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(); } @@ -92,9 +102,19 @@ void MapPersistentState::SaveGORespawnTime(uint32 loguid, time_t t) return; 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()) - 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(); } diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 7b020676e..c7b119e83 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -377,52 +377,65 @@ void Pet::SavePetToDB(PetSaveMode mode) _SaveAuras(); uint32 ownerLow = GetOwnerGuid().GetCounter(); - std::string name = m_name; - CharacterDatabase.escape_string(name); // 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) 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 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); - // 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) << ", '"; + { + static SqlStatementID del ; + + 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) { ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << " "; }; + savePet.addString(ss); - ss << "', " - << time(NULL) << ", " - << uint32(m_resetTalentsCost) << ", " - << uint64(m_resetTalentsTime) << ", " - << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " - << uint32(getPetType()) << ")"; + savePet.addUInt64(uint64(time(NULL))); + savePet.addUInt32(uint32(m_resetTalentsCost)); + savePet.addUInt64(uint64(m_resetTalentsTime)); + savePet.addUInt32(GetUInt32Value(UNIT_CREATED_BY_SPELL)); + savePet.addUInt32(uint32(getPetType())); - CharacterDatabase.Execute( ss.str().c_str() ); + savePet.Execute(); CharacterDatabase.CommitTransaction(); } // delete @@ -438,11 +451,26 @@ void Pet::DeleteFromDB(uint32 guidlow, bool separate_transaction) if(separate_transaction) CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow); + static SqlStatementID delPet ; + static SqlStatementID delDeclName ; + static SqlStatementID delAuras ; + static SqlStatementID delSpells ; + 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) CharacterDatabase.CommitTransaction(); @@ -1126,7 +1154,11 @@ void Pet::_LoadSpellCooldowns() 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); @@ -1137,7 +1169,8 @@ void Pet::_SaveSpellCooldowns() m_CreatureSpellCooldowns.erase(itr++); 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; } } @@ -1163,6 +1196,9 @@ void Pet::_LoadSpells() 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) { ++next; @@ -1174,15 +1210,26 @@ void Pet::_SaveSpells() switch(itr->second.state) { case PETSPELL_REMOVED: - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first); - m_spells.erase(itr); + { + SqlStatement stmt = CharacterDatabase.CreateStatement(delSpell, "DELETE FROM pet_spell WHERE guid = ? and spell = ?"); + stmt.PExecute(m_charmInfo->GetPetNumber(), itr->first); + m_spells.erase(itr); + } continue; 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; 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; case PETSPELL_UNCHANGED: continue; @@ -1286,7 +1333,11 @@ void Pet::_LoadAuras(uint32 timediff) 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(); @@ -1340,14 +1391,28 @@ void Pet::_SaveAuras() if (!effIndexMask) continue; + + 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) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - 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 " - "('%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%u')", - 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], - maxduration[EFFECT_INDEX_0], maxduration[EFFECT_INDEX_1], maxduration[EFFECT_INDEX_2], - remaintime[EFFECT_INDEX_0], remaintime[EFFECT_INDEX_1], remaintime[EFFECT_INDEX_2], - effIndexMask); + stmt.addUInt32(m_charmInfo->GetPetNumber()); + stmt.addUInt64(holder->GetCasterGuid().GetRawValue()); + stmt.addUInt32(holder->GetCastItemGuid().GetCounter()); + 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(); } } } diff --git a/src/game/Player.cpp b/src/game/Player.cpp index e4f7dbf31..1055a91f5 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -267,10 +267,8 @@ uint32 PlayerTaxi::GetCurrentTaxiPath() const std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi) { - ss << "'"; for(int i = 0; i < TaxiMaskSize; ++i) ss << taxi.m_taximask[i] << " "; - ss << "'"; return ss; } @@ -3714,14 +3712,15 @@ void Player::_LoadSpellCooldowns(QueryResult *result) 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 infTime = curTime + infinityCooldownDelayCheck; - bool first_round = true; - std::ostringstream ss; - // remove outdated and save active for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();) { @@ -3729,24 +3728,13 @@ void Player::_SaveSpellCooldowns() m_spellCooldowns.erase(itr++); else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload { - if (first_round) - { - 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) << ")"; + 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)); ++itr; } else ++itr; - } - // if something changed execute - if (!first_round) - CharacterDatabase.Execute( ss.str().c_str() ); } 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)) - 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); RemoveItemDurations(pItem); @@ -17091,144 +17084,156 @@ void Player::SaveToDB() CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow()); + static SqlStatementID delChar ; + static SqlStatementID insChar ; + + SqlStatement stmt = CharacterDatabase.CreateStatement(delChar, "DELETE FROM characters WHERE guid = ?"); + stmt.PExecute(GetGUIDLow()); - std::string sql_name = m_name; - CharacterDatabase.escape_string(sql_name); - - std::ostringstream ss; - ss << "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags," + SqlStatement uberInsert = CharacterDatabase.CreateStatement(insChar, "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, " "taximask, online, cinematic, " "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, " "death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, " "todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, " - "power4, power5, power6, power7, specCount, activeSpec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars) VALUES (" - << GetGUIDLow() << ", " - << GetSession()->GetAccountId() << ", '" - << sql_name << "', " - << (uint32)getRace() << ", " - << (uint32)getClass() << ", " - << (uint32)getGender() << ", " - << getLevel() << ", " - << GetUInt32Value(PLAYER_XP) << ", " - << GetMoney() << ", " - << GetUInt32Value(PLAYER_BYTES) << ", " - << GetUInt32Value(PLAYER_BYTES_2) << ", " - << GetUInt32Value(PLAYER_FLAGS) << ", "; + "power4, power5, power6, power7, specCount, activeSpec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, " + "?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); + + uberInsert.addUInt32(GetGUIDLow()); + uberInsert.addUInt32(GetSession()->GetAccountId()); + 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()) { - ss << GetMapId() << ", " - << (uint32)GetDungeonDifficulty() << ", " - << finiteAlways(GetPositionX()) << ", " - << finiteAlways(GetPositionY()) << ", " - << finiteAlways(GetPositionZ()) << ", " - << finiteAlways(GetOrientation()) << ", "; + uberInsert.addUInt32(GetMapId()); + uberInsert.addUInt32(uint32(GetDungeonDifficulty())); + uberInsert.addFloat(finiteAlways(GetPositionX())); + uberInsert.addFloat(finiteAlways(GetPositionY())); + uberInsert.addFloat(finiteAlways(GetPositionZ())); + uberInsert.addFloat(finiteAlways(GetOrientation())); } else { - ss << GetTeleportDest().mapid << ", " - << (uint32)GetDungeonDifficulty() << ", " - << finiteAlways(GetTeleportDest().coord_x) << ", " - << finiteAlways(GetTeleportDest().coord_y) << ", " - << finiteAlways(GetTeleportDest().coord_z) << ", " - << finiteAlways(GetTeleportDest().orientation) << ", "; + uberInsert.addUInt32(GetTeleportDest().mapid); + uberInsert.addUInt32(uint32(GetDungeonDifficulty())); + uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_x)); + uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_y)); + uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_z)); + 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] << ", "; - ss << m_Played_time[PLAYED_TIME_LEVEL] << ", "; + uberInsert.addUInt32(m_Played_time[PLAYED_TIME_TOTAL]); + uberInsert.addUInt32(m_Played_time[PLAYED_TIME_LEVEL]); - ss << finiteAlways(m_rest_bonus) << ", "; - ss << (uint64)time(NULL) << ", "; - ss << (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0) << ", "; + uberInsert.addFloat(finiteAlways(m_rest_bonus)); + uberInsert.addUInt64(uint64(time(NULL))); + uberInsert.addUInt32(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0); //save, far from tavern/city //save, but in tavern/city - ss << m_resetTalentsCost << ", "; - ss << (uint64)m_resetTalentsTime << ", "; + uberInsert.addUInt32(m_resetTalentsCost); + uberInsert.addUInt64(uint64(m_resetTalentsTime)); - ss << finiteAlways(m_movementInfo.GetTransportPos()->x) << ", "; - ss << finiteAlways(m_movementInfo.GetTransportPos()->y) << ", "; - ss << finiteAlways(m_movementInfo.GetTransportPos()->z) << ", "; - ss << finiteAlways(m_movementInfo.GetTransportPos()->o) << ", "; + uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->x)); + uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->y)); + uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->z)); + uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->o)); if (m_transport) - ss << m_transport->GetGUIDLow(); + uberInsert.addUInt32(m_transport->GetGUIDLow()); else - ss << "0"; - ss << ", "; + uberInsert.addUInt32(0); - 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) - 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) - ss << "," << GetPower(Powers(i)); + uberInsert.addUInt32(GetPower(Powers(i))); - ss << ", "; - ss << uint32(m_specsCount) << ", "; - ss << uint32(m_activeSpec) << ", '"; - for(uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i ) + uberInsert.addUInt32(uint32(m_specsCount)); + uberInsert.addUInt32(uint32(m_activeSpec)); + + for(uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i ) //string { ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << " "; } + uberInsert.addString(ss); - ss << "', '"; - for(uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i ) + for(uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i ) //string { ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << " "; } + uberInsert.addString(ss); - ss << "',"; - ss << GetUInt32Value(PLAYER_AMMO_ID) << ", '"; - for(uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i ) + uberInsert.addUInt32(GetUInt32Value(PLAYER_AMMO_ID)); + + for(uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i ) //string { ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << " "; } - ss << "',"; - ss << uint32(GetByteValue(PLAYER_FIELD_BYTES, 2)); - ss << ")"; + uberInsert.addString(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 _SaveMail(); @@ -17272,11 +17277,18 @@ void Player::SaveInventoryAndGoldToDB() 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() { + static SqlStatementID insertAction ; + static SqlStatementID updateAction ; + static SqlStatementID deleteAction ; + for(int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) { for(ActionButtonList::iterator itr = m_actionButtons[i].begin(); itr != m_actionButtons[i].end(); ) @@ -17284,20 +17296,40 @@ void Player::_SaveActions() switch (itr->second.uState) { 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() ); - itr->second.uState = ACTIONBUTTON_UNCHANGED; - ++itr; + { + 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; + } break; 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 ); - itr->second.uState = ACTIONBUTTON_UNCHANGED; - ++itr; + { + 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; + } break; case ACTIONBUTTON_DELETED: - CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' AND button = '%u' AND spec = '%u'", GetGUIDLow(), (uint32)itr->first, i); - m_actionButtons[i].erase(itr++); + { + 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++); + } break; default: ++itr; @@ -17309,13 +17341,21 @@ void Player::_SaveActions() 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(); if (auraHolders.empty()) 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) { SpellAuraHolder *holder = itr->second; @@ -17350,19 +17390,29 @@ void Player::_SaveAuras() if (!effIndexMask) 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 " - "('%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%u')", - GetGUIDLow(), holder->GetCasterGuid().GetRawValue(), holder->GetCastItemGuid().GetCounter(), holder->GetId(), holder->GetStackAmount(), holder->GetAuraCharges(), - damage[EFFECT_INDEX_0], damage[EFFECT_INDEX_1], damage[EFFECT_INDEX_2], - maxduration[EFFECT_INDEX_0], maxduration[EFFECT_INDEX_1], maxduration[EFFECT_INDEX_2], - remaintime[EFFECT_INDEX_0], remaintime[EFFECT_INDEX_1], remaintime[EFFECT_INDEX_2], - effIndexMask); + stmt.addUInt32(GetGUIDLow()); + stmt.addUInt64(holder->GetCasterGuid().GetRawValue()); + stmt.addUInt32(holder->GetCastItemGuid().GetCounter()); + stmt.addUInt32(holder->GetId()); + stmt.addUInt32(holder->GetStackAmount()); + stmt.addUInt8(holder->GetAuraCharges()); + 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() { + static SqlStatementID insertGlyph ; + static SqlStatementID updateGlyph ; + static SqlStatementID deleteGlyph ; for (uint8 spec = 0; spec < m_specsCount; ++spec) { @@ -17371,13 +17421,22 @@ void Player::_SaveGlyphs() switch(m_glyphs[spec][slot].uState) { 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; 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; 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; case GLYPH_UNCHANGED: break; @@ -17395,8 +17454,16 @@ void Player::_SaveInventory() { Item *item = m_items[i]; 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); } @@ -17436,6 +17503,10 @@ void Player::_SaveInventory() return; } + static SqlStatementID insertInventory ; + static SqlStatementID updateInventory ; + static SqlStatementID deleteInventory ; + for(size_t i = 0; i < m_itemUpdateQueue.size(); ++i) { Item *item = m_itemUpdateQueue[i]; @@ -17447,13 +17518,32 @@ void Player::_SaveInventory() switch(item->GetState()) { 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; 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; 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; case ITEM_UNCHANGED: break; @@ -17466,17 +17556,35 @@ void Player::_SaveInventory() 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) { Mail *m = (*itr); 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'", - m->HasItems() ? 1 : 0, (uint64)m->expire_time, (uint64)m->deliver_time, m->money, m->COD, m->checked, m->messageID); + SqlStatement stmt = CharacterDatabase.CreateStatement(updateMail, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?"); + 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()) { + stmt = CharacterDatabase.CreateStatement(deleteMailItems, "DELETE FROM mail_items WHERE item_guid = ?"); + for(std::vector::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->state = MAIL_STATE_UNCHANGED; @@ -17484,11 +17592,17 @@ void Player::_SaveMail() else if (m->state == MAIL_STATE_DELETED) { 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) - 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); - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", m->messageID); + SqlStatement stmt = CharacterDatabase.CreateStatement(deleteMain, "DELETE FROM mail WHERE id = ?"); + 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() { + static SqlStatementID insertQuestStatus ; + + static SqlStatementID updateQuestStatus ; + // we don't need transactions here. for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i ) { switch (i->second.uState) { 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')", - 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]); + { + SqlStatement stmt = CharacterDatabase.CreateStatement(insertQuestStatus, "INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) " + "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; 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; case QUEST_UNCHANGED: break; @@ -17538,11 +17683,17 @@ void Player::_SaveDailyQuestStatus() return; // 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) if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx)) - CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest) VALUES ('%u', '%u')", - GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx)); + stmtIns.PExecute(GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx)); m_DailyQuestChanged = false; } @@ -17553,13 +17704,18 @@ void Player::_SaveWeeklyQuestStatus() return; // 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) { uint32 quest_id = *iter; - - CharacterDatabase.PExecute("INSERT INTO character_queststatus_weekly (guid,quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id); + stmtIns.PExecute(GetGUIDLow(), quest_id); } m_WeeklyQuestChanged = false; @@ -17571,13 +17727,18 @@ void Player::_SaveMonthlyQuestStatus() return; // 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) { uint32 quest_id = *iter; - - CharacterDatabase.PExecute("INSERT INTO character_queststatus_monthly (guid, quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id); + stmtIns.PExecute(GetGUIDLow(), quest_id); } m_MonthlyQuestChanged = false; @@ -17585,6 +17746,10 @@ void Player::_SaveMonthlyQuestStatus() void Player::_SaveSkills() { + static SqlStatementID delSkills ; + static SqlStatementID insSkills ; + static SqlStatementID updSkills ; + // we don't need transactions here. for( SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ) { @@ -17596,7 +17761,8 @@ void Player::_SaveSkills() 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++); continue; } @@ -17608,12 +17774,16 @@ void Player::_SaveSkills() switch (itr->second.uState) { 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; 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; case SKILL_UNCHANGED: case SKILL_DELETED: @@ -17628,6 +17798,12 @@ void Player::_SaveSkills() 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();) { uint32 talentCosts = GetTalentSpellCost(itr->first); @@ -17635,11 +17811,11 @@ void Player::_SaveSpells() if (!talentCosts) { 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 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) @@ -17655,16 +17831,22 @@ void Player::_SaveSpells() 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();) { 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 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) 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)) return; - CharacterDatabase.PExecute("DELETE FROM character_stats WHERE guid = '%u'", GetGUIDLow()); - std::ostringstream ss; - ss << "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5, maxpower6, maxpower7, " + static SqlStatementID delStats ; + static SqlStatementID insertStats ; + + 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, " - "blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) VALUES (" - << GetGUIDLow() << ", " - << GetMaxHealth() << ", "; + "blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) " + "VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + stmt.addUInt32(GetGUIDLow()); + stmt.addUInt32(GetMaxHealth()); 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) - ss << GetStat(Stats(i)) << ", "; + stmt.addFloat(GetStat(Stats(i))); // armor + school resistances for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) - ss << GetResistance(SpellSchools(i)) << ","; - ss << GetFloatValue(PLAYER_BLOCK_PERCENTAGE) << ", " - << GetFloatValue(PLAYER_DODGE_PERCENTAGE) << ", " - << GetFloatValue(PLAYER_PARRY_PERCENTAGE) << ", " - << GetFloatValue(PLAYER_CRIT_PERCENTAGE) << ", " - << GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE) << ", " - << GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1) << ", " - << GetUInt32Value(UNIT_FIELD_ATTACK_POWER) << ", " - << GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER) << ", " - << GetBaseSpellPowerBonus() << ")"; - CharacterDatabase.Execute( ss.str().c_str() ); + stmt.addUInt32(GetResistance(SpellSchools(i))); + stmt.addFloat(GetFloatValue(PLAYER_BLOCK_PERCENTAGE)); + stmt.addFloat(GetFloatValue(PLAYER_DODGE_PERCENTAGE)); + stmt.addFloat(GetFloatValue(PLAYER_PARRY_PERCENTAGE)); + stmt.addFloat(GetFloatValue(PLAYER_CRIT_PERCENTAGE)); + stmt.addFloat(GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE)); + stmt.addFloat(GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1)); + stmt.addUInt32(GetUInt32Value(UNIT_FIELD_ATTACK_POWER)); + stmt.addUInt32(GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER)); + stmt.addUInt32(GetBaseSpellPowerBonus()); + + stmt.Execute(); } void Player::outDebugStatsValues() const @@ -22218,6 +22407,10 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset) void Player::_SaveEquipmentSets() { + static SqlStatementID updSets ; + static SqlStatementID insSets ; + static SqlStatementID delSets ; + for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();) { uint32 index = itr->first; @@ -22229,36 +22422,51 @@ void Player::_SaveEquipmentSets() break; // nothing do case EQUIPMENT_SET_CHANGED: { - // prevent SQL injection - std::string db_IconName = eqset.IconName; - std::string db_Name = eqset.Name; - CharacterDatabase.escape_string(db_IconName); - CharacterDatabase.escape_string(db_Name); - 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'", - 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); + SqlStatement stmt = CharacterDatabase.CreateStatement(updSets, "UPDATE character_equipmentsets SET name=?, iconname=?, item0=?, item1=?, item2=?, item3=?, item4=?, " + "item5=?, item6=?, item7=?, item8=?, item9=?, item10=?, item11=?, item12=?, item13=?, item14=?, " + "item15=?, item16=?, item17=?, item18=? WHERE guid=? AND setguid=? AND setindex=?"); + + stmt.addString(eqset.IconName); + stmt.addString(eqset.Name); + + 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; ++itr; break; } case EQUIPMENT_SET_NEW: { - // prevent SQL injection - std::string db_IconName = eqset.IconName; - std::string db_Name = eqset.Name; - CharacterDatabase.escape_string(db_IconName); - CharacterDatabase.escape_string(db_Name); - 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')", - 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]); + SqlStatement stmt = CharacterDatabase.CreateStatement(insSets, "INSERT INTO character_equipmentsets VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + stmt.addUInt32(GetGUIDLow()); + stmt.addUInt64(eqset.Guid); + stmt.addUInt32(index); + stmt.addString(eqset.IconName); + stmt.addString(eqset.Name); + + for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) + stmt.addUInt32(eqset.Items[i]); + + stmt.Execute(); + eqset.state = EQUIPMENT_SET_UNCHANGED; ++itr; break; } 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++); break; + } } } } @@ -22269,13 +22477,30 @@ void Player::_SaveBGData() if (!m_bgData.m_needSave) 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) { + stmt = CharacterDatabase.CreateStatement(insBGData, "INSERT INTO character_battleground_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); /* 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')", - GetGUIDLow(), m_bgData.bgInstanceID, uint32(m_bgData.bgTeam), m_bgData.joinPos.coord_x, m_bgData.joinPos.coord_y, m_bgData.joinPos.coord_z, - m_bgData.joinPos.orientation, m_bgData.joinPos.mapid, m_bgData.taxiPath[0], m_bgData.taxiPath[1], m_bgData.mountSpell); + stmt.addUInt32(GetGUIDLow()); + stmt.addUInt32(m_bgData.bgInstanceID); + 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; diff --git a/src/game/Player.h b/src/game/Player.h index 0ca7ba2e3..b217ff414 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1744,14 +1744,8 @@ class MANGOS_DLL_SPEC Player : public Unit bool isRessurectRequested() const { return !m_resurrectGuid.IsEmpty(); } void ResurectUsingRequestData(); - int getCinematic() - { - return m_cinematic; - } - void setCinematic(int cine) - { - m_cinematic = cine; - } + uint32 getCinematic() { return m_cinematic; } + void setCinematic(uint32 cine) { m_cinematic = cine; } static bool IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg = true); ActionButton* addActionButton(uint8 spec, uint8 button, uint32 action, uint8 type); @@ -2567,7 +2561,7 @@ class MANGOS_DLL_SPEC Player : public Unit typedef std::list JoinedChannelsList; JoinedChannelsList m_channels; - int m_cinematic; + uint32 m_cinematic; TradeData* m_trade; diff --git a/src/game/ReputationMgr.cpp b/src/game/ReputationMgr.cpp index 15ef0fc21..63345cf41 100644 --- a/src/game/ReputationMgr.cpp +++ b/src/game/ReputationMgr.cpp @@ -503,12 +503,18 @@ void ReputationMgr::LoadFromDB(QueryResult *result) 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) { if (itr->second.needSave) { - CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", 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); + stmtDel.PExecute(m_player->GetGUIDLow(), itr->second.ID); + stmtIns.PExecute(m_player->GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags); itr->second.needSave = false; } } diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index f6238c24a..eefab9900 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -262,7 +262,11 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); 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 pUser->SendLoot(pItem->GetObjectGuid(),LOOT_CORPSE); diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index f7063d554..2db93f4b4 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -749,15 +749,32 @@ void WorldSession::SendTutorialsData() void WorldSession::SaveTutorialsData() { + static SqlStatementID updTutorial ; + static SqlStatementID insTutorial ; + switch(m_tutorialState) { 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; + 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; case TUTORIALDATA_UNCHANGED: break; diff --git a/src/shared/Database/Database.cpp b/src/shared/Database/Database.cpp index 4e951e17a..a6d27e523 100644 --- a/src/shared/Database/Database.cpp +++ b/src/shared/Database/Database.cpp @@ -23,10 +23,77 @@ #include #include #include +#include #define MIN_CONNECTION_POOL_SIZE 1 #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() { StopServer(); @@ -274,7 +341,7 @@ bool Database::Execute(const char *sql) if(pTrans) { //add SQL request to trans queue - pTrans->DelayExecute(sql); + pTrans->DelayExecute(new SqlPlainRequest(sql)); } else { @@ -283,7 +350,7 @@ bool Database::Execute(const char *sql) return DirectExecute(sql); // Simple sql statement - m_threadBody->Delay(new SqlStatement(sql)); + m_threadBody->Delay(new SqlPlainRequest(sql)); } return true; @@ -477,6 +544,85 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ 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 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() { reset(); diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h index 1b51329e1..2ff0740f4 100644 --- a/src/shared/Database/Database.h +++ b/src/shared/Database/Database.h @@ -26,10 +26,14 @@ #include "Policies/ThreadingModel.h" #include #include +#include "SqlPreparedStatement.h" class SqlTransaction; class SqlResultQueue; class SqlQueryHolder; +class SqlStmtParameters; +class SqlParamBinder; +class Database; #define MAX_QUERY_LEN 32*1024 @@ -57,6 +61,9 @@ class MANGOS_DLL_SPEC SqlConnection // can't rollback without transaction support virtual bool RollbackTransaction() { return true; } + //methods to work with prepared statements + bool ExecuteStmt(int nIndex, const SqlStmtParameters& id); + //SqlConnection object lock class Lock { @@ -70,9 +77,27 @@ class MANGOS_DLL_SPEC SqlConnection 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: typedef ACE_Recursive_Thread_Mutex LOCK_TYPE; LOCK_TYPE m_mutex; + + typedef std::vector StmtHolder; + StmtHolder m_holder; }; class MANGOS_DLL_SPEC Database @@ -165,6 +190,13 @@ class MANGOS_DLL_SPEC Database //for sync transaction execution 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; } //escape string generation @@ -191,7 +223,7 @@ class MANGOS_DLL_SPEC Database protected: 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; } @@ -235,6 +267,12 @@ class MANGOS_DLL_SPEC Database //for now return one single connection for async requests 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 int m_nQueryConnPoolSize; //current size of query connection pool ACE_Atomic_Op 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 + //PREPARED STATEMENT REGISTRY + typedef ACE_Thread_Mutex LOCK_TYPE; + typedef ACE_Guard LOCK_GUARD; + + mutable LOCK_TYPE m_stmtGuard; + + typedef UNORDERED_MAP PreparedStmtRegistry; + PreparedStmtRegistry m_stmtRegistry; ///< + + int m_iStmtIndex; + private: bool m_logSQL; diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp index d5988e12a..6b9fc3173 100644 --- a/src/shared/Database/DatabaseMysql.cpp +++ b/src/shared/Database/DatabaseMysql.cpp @@ -65,11 +65,12 @@ DatabaseMysql::~DatabaseMysql() SqlConnection * DatabaseMysql::CreateConnection() { - return new MySQLConnection(); + return new MySQLConnection(*this); } MySQLConnection::~MySQLConnection() { + FreePreparedStatements(); 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)); } +////////////////////////////////////////////////////////////////////////// +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 diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h index 3cfeffa49..b83257497 100644 --- a/src/shared/Database/DatabaseMysql.h +++ b/src/shared/Database/DatabaseMysql.h @@ -21,7 +21,7 @@ #ifndef _DATABASEMYSQL_H #define _DATABASEMYSQL_H -#include "Common.h" +//#include "Common.h" #include "Database.h" #include "Policies/Singleton.h" #include "ace/Thread_Mutex.h" @@ -34,10 +34,42 @@ #include #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 { public: - MySQLConnection() : mMysql(NULL) {} + MySQLConnection(Database& db) : SqlConnection(db), mMysql(NULL) {} ~MySQLConnection(); bool Initialize(const char *infoString); @@ -52,6 +84,9 @@ class MANGOS_DLL_SPEC MySQLConnection : public SqlConnection bool CommitTransaction(); bool RollbackTransaction(); + protected: + SqlPreparedStatement * CreateStatement(const std::string& fmt); + private: bool _TransactionCmd(const char *sql); bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount); diff --git a/src/shared/Database/SqlOperations.cpp b/src/shared/Database/SqlOperations.cpp index 013cd6f48..951db8c32 100644 --- a/src/shared/Database/SqlOperations.cpp +++ b/src/shared/Database/SqlOperations.cpp @@ -25,26 +25,26 @@ /// ---- ASYNC STATEMENTS / TRANSACTIONS ---- -void SqlStatement::Execute(SqlConnection *conn) +bool SqlPlainRequest::Execute(SqlConnection *conn) { /// just do it LOCK_DB_CONN(conn); - conn->Execute(m_sql); + return conn->Execute(m_sql); } SqlTransaction::~SqlTransaction() { while(!m_queue.empty()) { - delete [] (const_cast(m_queue.back())); + delete m_queue.back(); m_queue.pop_back(); } } -void SqlTransaction::Execute(SqlConnection *conn) +bool SqlTransaction::Execute(SqlConnection *conn) { if(m_queue.empty()) - return; + return true; LOCK_DB_CONN(conn); @@ -53,30 +53,47 @@ void SqlTransaction::Execute(SqlConnection *conn) const int nItems = m_queue.size(); 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(); - 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 ---- -void SqlQuery::Execute(SqlConnection *conn) +bool SqlQuery::Execute(SqlConnection *conn) { if(!m_callback || !m_queue) - return; + return false; LOCK_DB_CONN(conn); /// execute the query and store the result in the callback m_callback->SetResult(conn->Query(m_sql)); /// add the callback to the sql result queue of the thread it originated from m_queue->add(m_callback); + + return true; } void SqlResultQueue::Update() @@ -190,10 +207,10 @@ void SqlQueryHolder::SetSize(size_t size) m_queries.resize(size); } -void SqlQueryHolderEx::Execute(SqlConnection *conn) +bool SqlQueryHolderEx::Execute(SqlConnection *conn) { if(!m_holder || !m_callback || !m_queue) - return; + return false; LOCK_DB_CONN(conn); /// we can do this, we are friends @@ -207,4 +224,6 @@ void SqlQueryHolderEx::Execute(SqlConnection *conn) /// sync with the caller thread m_queue->add(m_callback); + + return true; } diff --git a/src/shared/Database/SqlOperations.h b/src/shared/Database/SqlOperations.h index ac53e5df5..baff78b81 100644 --- a/src/shared/Database/SqlOperations.h +++ b/src/shared/Database/SqlOperations.h @@ -31,43 +31,53 @@ class Database; class SqlConnection; class SqlDelayThread; +class SqlStmtParameters; class SqlOperation { public: virtual void OnRemove() { delete this; } - virtual void Execute(SqlConnection *conn) = 0; + virtual bool Execute(SqlConnection *conn) = 0; virtual ~SqlOperation() {} }; /// ---- ASYNC STATEMENTS / TRANSACTIONS ---- -class SqlStatement : public SqlOperation +class SqlPlainRequest : public SqlOperation { private: const char *m_sql; public: - SqlStatement(const char *sql) : m_sql(mangos_strdup(sql)){} - ~SqlStatement() { char* tofree = const_cast(m_sql); delete [] tofree; } - void Execute(SqlConnection *conn); + SqlPlainRequest(const char *sql) : m_sql(mangos_strdup(sql)){} + ~SqlPlainRequest() { char* tofree = const_cast(m_sql); delete [] tofree; } + bool Execute(SqlConnection *conn); }; class SqlTransaction : public SqlOperation { private: - std::vector m_queue; + std::vector m_queue; public: SqlTransaction() {} ~SqlTransaction(); - void DelayExecute(const char *sql) - { - char* _sql = mangos_strdup(sql); - m_queue.push_back(_sql); - } + void DelayExecute(SqlOperation * 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 ---- @@ -95,7 +105,7 @@ class SqlQuery : public SqlOperation SqlQuery(const char *sql, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue) : m_sql(mangos_strdup(sql)), m_callback(callback), m_queue(queue) {} ~SqlQuery() { char* tofree = const_cast(m_sql); delete [] tofree; } - void Execute(SqlConnection *conn); + bool Execute(SqlConnection *conn); }; class SqlQueryHolder @@ -124,6 +134,6 @@ class SqlQueryHolderEx : public SqlOperation public: SqlQueryHolderEx(SqlQueryHolder *holder, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue) : m_holder(holder), m_callback(callback), m_queue(queue) {} - void Execute(SqlConnection *conn); + bool Execute(SqlConnection *conn); }; #endif //__SQLOPERATIONS_H diff --git a/src/shared/Database/SqlPreparedStatement.cpp b/src/shared/Database/SqlPreparedStatement.cpp new file mode 100644 index 000000000..3d70a0c80 --- /dev/null +++ b/src/shared/Database/SqlPreparedStatement.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2005-2011 MaNGOS + * + * 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; + } +} \ No newline at end of file diff --git a/src/shared/Database/SqlPreparedStatement.h b/src/shared/Database/SqlPreparedStatement.h new file mode 100644 index 000000000..c6ba6c4ac --- /dev/null +++ b/src/shared/Database/SqlPreparedStatement.h @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2005-2011 MaNGOS + * + * 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 +#include +#include + +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 + SqlStmtFieldData(T param) { set(param); } + + template + 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 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 + bool PExecute(ParamType1 param1) + { + arg(param1); + return Execute(); + } + + template + bool PExecute(ParamType1 param1, ParamType2 param2) + { + arg(param1); + arg(param2); + return Execute(); + } + + template + bool PExecute(ParamType1 param1, ParamType2 param2, ParamType3 param3) + { + arg(param1); + arg(param2); + arg(param3); + return Execute(); + } + + template + 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 + 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 + + diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 1daf4e79e..c3b39a3bc 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11283" + #define REVISION_NR "11284" #endif // __REVISION_NR_H__ diff --git a/win/VC100/shared.vcxproj b/win/VC100/shared.vcxproj index 7fbffb554..272108bfe 100644 --- a/win/VC100/shared.vcxproj +++ b/win/VC100/shared.vcxproj @@ -1,4 +1,5 @@ - + + Debug_NoPCH @@ -441,6 +442,7 @@ + @@ -456,6 +458,7 @@ + @@ -501,4 +504,4 @@ - + \ No newline at end of file diff --git a/win/VC100/shared.vcxproj.filters b/win/VC100/shared.vcxproj.filters index 85f2946a6..27c042c23 100644 --- a/win/VC100/shared.vcxproj.filters +++ b/win/VC100/shared.vcxproj.filters @@ -75,6 +75,9 @@ + + Database + @@ -167,6 +170,9 @@ + + Database + diff --git a/win/VC80/mangosd.vcproj b/win/VC80/mangosd.vcproj index 90dffb2d4..8eaa70cd9 100644 --- a/win/VC80/mangosd.vcproj +++ b/win/VC80/mangosd.vcproj @@ -1,7 +1,7 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/win/VC90/shared.vcproj b/win/VC90/shared.vcproj index ae7aec594..4fbbaa456 100644 --- a/win/VC90/shared.vcproj +++ b/win/VC90/shared.vcproj @@ -1,7 +1,7 @@ + + + +