DB Layer:

- Make SQL Transactions actual objects used in code. (Thanks to Derex for the idea)
* Uncommitted transactions will be automatically rolled back and cleaned up using ACE_Refcounted_Auto_Ptr, so no need to call Rollback() in the code.
* Prevents recursive transactions and makes developers aware of transactions going on.
* Gets rid of unneccesary overhead iterating over a concurrent map.
- Some cleanups in affected code, including better usage of transaction control in AH / mail related code to prevent data loss.

*** Experimental, use at own risk, recommended to backup your DBs. ***

--HG--
branch : trunk
This commit is contained in:
Machiavelli
2010-08-21 03:19:25 +02:00
parent a7498d2f56
commit 994186f267
49 changed files with 861 additions and 774 deletions
@@ -88,7 +88,7 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
}
//does not clear ram
void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction, SQLTransaction& trans)
{
Item *pItem = GetAItem(auction->item_guidlow);
if (!pItem)
@@ -146,7 +146,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
// set owner to bidder (to prevent delete item with sender char deleting)
// owner in `data` will set at mail receive and item extracting
CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
trans->PAppend("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
if (bidder)
{
@@ -157,11 +157,11 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
MailDraft(msgAuctionWonSubject.str(), msgAuctionWonBody.str())
.AddItem(pItem)
.SendMailTo(MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
.SendMailTo(trans, MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
}
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction)
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction, SQLTransaction& trans)
{
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
Player *owner = sObjectMgr.GetPlayer(owner_guid);
@@ -186,12 +186,12 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction)
sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str());
MailDraft(msgAuctionSalePendingSubject.str(), msgAuctionSalePendingBody.str())
.SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED);
.SendMailTo(trans, MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED);
}
}
//call this method to send mail to auction owner, when auction is successful, it does not clear ram
void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction)
void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction, SQLTransaction& trans)
{
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
Player *owner = sObjectMgr.GetPlayer(owner_guid);
@@ -224,12 +224,12 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction)
}
MailDraft(msgAuctionSuccessfulSubject.str(), auctionSuccessfulBody.str())
.AddMoney(profit)
.SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED, sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY));
.SendMailTo(trans, MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED, sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY));
}
}
//does not clear ram
void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction)
void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction, SQLTransaction& trans)
{
//return an item in auction to its owner by mail
Item *pItem = GetAItem(auction->item_guidlow);
@@ -250,10 +250,58 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction)
MailDraft(subject.str(), "") // TODO: fix body
.AddItem(pItem)
.SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED);
.SendMailTo(trans, MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED, 0);
}
}
//this function sends mail to old bidder
void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans)
{
uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder,0, HIGHGUID_PLAYER);
Player *oldBidder = sObjectMgr.GetPlayer(oldBidder_guid);
uint32 oldBidder_accId = 0;
if (!oldBidder)
oldBidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(oldBidder_guid);
// old bidder exist
if (oldBidder || oldBidder_accId)
{
std::ostringstream msgAuctionOutbiddedSubject;
msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED << ":0:0";
if (oldBidder && newBidder)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
MailDraft(msgAuctionOutbiddedSubject.str(), "") // TODO: fix body
.AddMoney(auction->bid)
.SendMailTo(trans, MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
}
//this function sends mail, when auction is cancelled to old bidder
void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans)
{
uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
Player *bidder = sObjectMgr.GetPlayer(bidder_guid);
uint32 bidder_accId = 0;
if (!bidder)
bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid);
// bidder exist
if (bidder || bidder_accId)
{
std::ostringstream msgAuctionCancelledSubject;
msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0";
MailDraft(msgAuctionCancelledSubject.str(), "") // TODO: fix body
.AddMoney(auction->bid)
.SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
}
void AuctionHouseMgr::LoadAuctionItems()
{
// data needs to be at first place for Item::LoadFromDB
@@ -342,6 +390,10 @@ void AuctionHouseMgr::LoadAuctions()
barGoLink bar(AuctionCount);
//- TODO: Get rid of horrible design so we don't have to use transaction here to statisfy
//- function parameters.
SQLTransaction trans = CharacterDatabase.BeginTransaction();
AuctionEntry *aItem;
do
@@ -366,7 +418,7 @@ void AuctionHouseMgr::LoadAuctions()
CreatureData const* auctioneerData = sObjectMgr.GetCreatureData(aItem->auctioneer);
if (!auctioneerData)
{
aItem->DeleteFromDB();
aItem->DeleteFromDB(trans);
sLog.outError("Auction %u has not a existing auctioneer (GUID : %u)", aItem->Id, aItem->auctioneer);
delete aItem;
continue;
@@ -375,7 +427,7 @@ void AuctionHouseMgr::LoadAuctions()
CreatureInfo const* auctioneerInfo = sObjectMgr.GetCreatureTemplate(auctioneerData->id);
if (!auctioneerInfo)
{
aItem->DeleteFromDB();
aItem->DeleteFromDB(trans);
sLog.outError("Auction %u has not a existing auctioneer (GUID : %u Entry: %u)", aItem->Id, aItem->auctioneer,auctioneerData->id);
delete aItem;
continue;
@@ -384,7 +436,7 @@ void AuctionHouseMgr::LoadAuctions()
aItem->auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(auctioneerInfo->faction_A);
if (!aItem->auctionHouseEntry)
{
aItem->DeleteFromDB();
aItem->DeleteFromDB(trans);
sLog.outError("Auction %u has auctioneer (GUID : %u Entry: %u) with wrong faction %u",
aItem->Id, aItem->auctioneer,auctioneerData->id,auctioneerInfo->faction_A);
delete aItem;
@@ -395,7 +447,7 @@ void AuctionHouseMgr::LoadAuctions()
// and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems)
if (!GetAItem(aItem->item_guidlow))
{
aItem->DeleteFromDB();
aItem->DeleteFromDB(trans);
sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow);
delete aItem;
continue;
@@ -405,6 +457,8 @@ void AuctionHouseMgr::LoadAuctions()
} while (result->NextRow());
CharacterDatabase.CommitTransaction(trans);
sLog.outString();
sLog.outString(">> Loaded %u auctions", AuctionCount);
}
@@ -506,35 +560,20 @@ void AuctionHouseObject::Update()
if (!result)
return;
if (result->GetRowCount() == 0)
return;
vector<uint32> expiredAuctions;
do
{
uint32 tmpdata = result->Fetch()->GetUInt32();
expiredAuctions.push_back(tmpdata);
}
while (result->NextRow());
while (!expiredAuctions.empty())
{
vector<uint32>::iterator iter = expiredAuctions.begin();
// from auctionhousehandler.cpp, creates auction pointer & player pointer
AuctionEntry* auction = GetAuction(*iter);
// Erase the auction from the vector.
expiredAuctions.erase(iter);
AuctionEntry* auction = GetAuction(result->Fetch()->GetUInt32());
if (!auction)
continue;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
///- Either cancel the auction if there was no bidder
if (auction->bidder == 0)
{
sAuctionMgr.SendAuctionExpiredMail(auction);
sAuctionMgr.SendAuctionExpiredMail(auction, trans);
sScriptMgr.OnAuctionExpire(this, auction);
}
///- Or perform the transaction
@@ -543,19 +582,21 @@ void AuctionHouseObject::Update()
//we should send an "item sold" message if the seller is online
//we send the item to the winner
//we send the money to the seller
sAuctionMgr.SendAuctionSuccessfulMail(auction);
sAuctionMgr.SendAuctionWonMail(auction);
sAuctionMgr.SendAuctionSuccessfulMail(auction, trans);
sAuctionMgr.SendAuctionWonMail(auction, trans);
sScriptMgr.OnAuctionSuccessful(this, auction);
}
///- In any case clear the auction
CharacterDatabase.BeginTransaction();
auction->DeleteFromDB();
uint32 item_template = auction->item_template;
sAuctionMgr.RemoveAItem(auction->item_guidlow);
///- In any case clear the auction
auction->DeleteFromDB(trans);
CharacterDatabase.CommitTransaction(trans);
RemoveAuction(auction, item_template);
CharacterDatabase.CommitTransaction();
sAuctionMgr.RemoveAItem(auction->item_guidlow);
}
while (result->NextRow());
}
void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
@@ -746,16 +787,14 @@ uint32 AuctionEntry::GetAuctionOutBid() const
return outbid;
}
void AuctionEntry::DeleteFromDB() const
void AuctionEntry::DeleteFromDB(SQLTransaction& trans) const
{
//No SQL injection (Id is integer)
CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",Id);
trans->PAppend("DELETE FROM auctionhouse WHERE id = '%u'",Id);
}
void AuctionEntry::SaveToDB() const
void AuctionEntry::SaveToDB(SQLTransaction& trans) const
{
//No SQL injection (no strings)
CharacterDatabase.PExecute("INSERT INTO auctionhouse (id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit) "
trans->PAppend("INSERT INTO auctionhouse (id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit) "
"VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u')",
Id, auctioneer, item_guidlow, item_template, owner, buyout, (uint64)expire_time, bidder, bid, startbid, deposit);
}