TrinityCore
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
UpdateFetcher Class Reference

#include <UpdateFetcher.h>

Classes

struct  AppliedFileEntry
 
struct  DirectoryEntry
 
struct  PathCompare
 

Public Member Functions

 UpdateFetcher (Path const &updateDirectory, std::function< void(std::string const &)> const &apply, std::function< void(Path const &path)> const &applyFile, std::function< QueryResult(std::string const &)> const &retrieve)
 
UpdateResult Update (bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const
 

Private Types

enum  UpdateMode { MODE_APPLY, MODE_REHASH }
 
enum  State { RELEASED, ARCHIVED }
 
typedef boost::filesystem::path Path
 
typedef std::pair< Path, StateLocaleFileEntry
 
typedef std::set
< LocaleFileEntry, PathCompare
LocaleFileStorage
 
typedef std::unordered_map
< std::string, std::string > 
HashToFileNameStorage
 
typedef std::unordered_map
< std::string,
AppliedFileEntry
AppliedFileStorage
 
typedef std::vector
< UpdateFetcher::DirectoryEntry
DirectoryStorage
 

Private Member Functions

LocaleFileStorage GetFileList () const
 
void FillFileListRecursively (Path const &path, LocaleFileStorage &storage, State const state, uint32 const depth) const
 
DirectoryStorage ReceiveIncludedDirectories () const
 
AppliedFileStorage ReceiveAppliedFiles () const
 
std::string ReadSQLUpdate (Path const &file) const
 
std::string CalculateHash (std::string const &query) const
 
uint32 Apply (Path const &path) const
 
void UpdateEntry (AppliedFileEntry const &entry, uint32 const speed=0) const
 
void RenameEntry (std::string const &from, std::string const &to) const
 
void CleanUp (AppliedFileStorage const &storage) const
 
void UpdateState (std::string const &name, State const state) const
 

Private Attributes

Path const _sourceDirectory
 
std::function< void(std::string
const &)> const 
_apply
 
std::function< void(Path const
&path)> const 
_applyFile
 
std::function< QueryResult(std::string
const &)> const 
_retrieve
 

Member Typedef Documentation

typedef std::unordered_map<std::string, AppliedFileEntry> UpdateFetcher::AppliedFileStorage
private
typedef std::unordered_map<std::string, std::string> UpdateFetcher::HashToFileNameStorage
private
typedef std::pair<Path, State> UpdateFetcher::LocaleFileEntry
private
typedef boost::filesystem::path UpdateFetcher::Path
private

Member Enumeration Documentation

enum UpdateFetcher::State
private
Enumerator
RELEASED 
ARCHIVED 
49  {
50  RELEASED,
51  ARCHIVED
52  };
Definition: UpdateFetcher.h:51
Definition: UpdateFetcher.h:50
Enumerator
MODE_APPLY 
MODE_REHASH 
43  {
44  MODE_APPLY,
46  };
Definition: UpdateFetcher.h:45
Definition: UpdateFetcher.h:44

Constructor & Destructor Documentation

UpdateFetcher::UpdateFetcher ( Path const updateDirectory,
std::function< void(std::string const &)> const apply,
std::function< void(Path const &path)> const applyFile,
std::function< QueryResult(std::string const &)> const retrieve 
)
35  :
36  _sourceDirectory(sourceDirectory), _apply(apply), _applyFile(applyFile),
37  _retrieve(retrieve)
38 {
39 }
std::function< void(std::string const &)> const _apply
Definition: UpdateFetcher.h:127
Path const _sourceDirectory
Definition: UpdateFetcher.h:125
std::function< void(Path const &path)> const _applyFile
Definition: UpdateFetcher.h:128
void apply(T *val)
Definition: ByteConverter.h:41
std::function< QueryResult(std::string const &)> const _retrieve
Definition: UpdateFetcher.h:129

Member Function Documentation

uint32 UpdateFetcher::Apply ( Path const path) const
private
347 {
348  using Time = std::chrono::high_resolution_clock;
349 
350  // Benchmark query speed
351  auto const begin = Time::now();
352 
353  // Update database
354  _applyFile(path);
355 
356  // Return time the query took to apply
357  return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count());
358 }
std::function< void(Path const &path)> const _applyFile
Definition: UpdateFetcher.h:128
uint32_t uint32
Definition: g3dmath.h:168

+ Here is the caller graph for this function:

std::string UpdateFetcher::CalculateHash ( std::string const query) const
private
338 {
339  // Calculate a Sha1 hash based on query content.
340  unsigned char digest[SHA_DIGEST_LENGTH];
341  SHA1((unsigned char*)query.c_str(), query.length(), (unsigned char*)&digest);
342 
343  return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
344 }
char * query(struct soap *soap)
Definition: httpget.cpp:244
std::string ByteArrayToHexStr(uint8 const *bytes, uint32 arrayLen, bool reverse)
Definition: Util.cpp:509

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void UpdateFetcher::CleanUp ( AppliedFileStorage const storage) const
private
389 {
390  if (storage.empty())
391  return;
392 
393  std::stringstream update;
394  size_t remaining = storage.size();
395 
396  update << "DELETE FROM `updates` WHERE `name` IN(";
397 
398  for (auto const& entry : storage)
399  {
400  update << "\"" << entry.first << "\"";
401  if ((--remaining) > 0)
402  update << ", ";
403  }
404 
405  update << ")";
406 
407  // Update database
408  _apply(update.str());
409 }
std::function< void(std::string const &)> const _apply
Definition: UpdateFetcher.h:127

+ Here is the caller graph for this function:

void UpdateFetcher::FillFileListRecursively ( Path const path,
LocaleFileStorage storage,
State const  state,
uint32 const  depth 
) const
private
52 {
53  static uint32 const MAX_DEPTH = 10;
54  static directory_iterator const end;
55 
56  for (directory_iterator itr(path); itr != end; ++itr)
57  {
58  if (is_directory(itr->path()))
59  {
60  if (depth < MAX_DEPTH)
61  FillFileListRecursively(itr->path(), storage, state, depth + 1);
62  }
63  else if (itr->path().extension() == ".sql")
64  {
65  TC_LOG_TRACE("sql.updates", "Added locale file \"%s\".", itr->path().filename().generic_string().c_str());
66 
67  LocaleFileEntry const entry = { itr->path(), state };
68 
69  // Check for doubled filenames
70  // Since elements are only compared through their filenames this is ok
71  if (storage.find(entry) != storage.end())
72  {
73  TC_LOG_FATAL("sql.updates", "Duplicated filename occurred \"%s\", since updates are ordered " \
74  "through its filename every name needs to be unique!", itr->path().generic_string().c_str());
75 
76  throw UpdateException("Updating failed, see the log for details.");
77  }
78 
79  storage.insert(entry);
80  }
81  }
82 }
Definition: DBUpdater.h:26
void FillFileListRecursively(Path const &path, LocaleFileStorage &storage, State const state, uint32 const depth) const
Definition: UpdateFetcher.cpp:51
#define TC_LOG_TRACE(filterType__,...)
Definition: Log.h:195
uint32_t uint32
Definition: Define.h:150
std::pair< Path, State > LocaleFileEntry
Definition: UpdateFetcher.h:92
#define TC_LOG_FATAL(filterType__,...)
Definition: Log.h:210

+ Here is the caller graph for this function:

UpdateFetcher::LocaleFileStorage UpdateFetcher::GetFileList ( ) const
private
42 {
43  LocaleFileStorage files;
45  for (auto const& entry : directories)
46  FillFileListRecursively(entry.path, files, entry.state, 1);
47 
48  return files;
49 }
void FillFileListRecursively(Path const &path, LocaleFileStorage &storage, State const state, uint32 const depth) const
Definition: UpdateFetcher.cpp:51
std::set< LocaleFileEntry, PathCompare > LocaleFileStorage
Definition: UpdateFetcher.h:102
DirectoryStorage ReceiveIncludedDirectories() const
Definition: UpdateFetcher.cpp:84
std::vector< UpdateFetcher::DirectoryEntry > DirectoryStorage
Definition: UpdateFetcher.h:105

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

std::string UpdateFetcher::ReadSQLUpdate ( Path const file) const
private
141 {
142  std::ifstream in(file.c_str());
143  if (!in.is_open())
144  {
145  TC_LOG_FATAL("sql.updates", "Failed to open the sql update \"%s\" for reading! "
146  "Stopping the server to keep the database integrity, "
147  "try to identify and solve the issue or disabled the database updater.",
148  file.generic_string().c_str());
149 
150  throw UpdateException("Opening the sql update failed!");
151  }
152 
153  auto update = [&in] {
154  std::ostringstream ss;
155  ss << in.rdbuf();
156  return ss.str();
157  }();
158 
159  in.close();
160  return update;
161 }
Definition: DBUpdater.h:26
#define TC_LOG_FATAL(filterType__,...)
Definition: Log.h:210

+ Here is the caller graph for this function:

UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles ( ) const
private
119 {
120  AppliedFileStorage map;
121 
122  QueryResult result = _retrieve("SELECT `name`, `hash`, `state`, UNIX_TIMESTAMP(`timestamp`) FROM `updates` ORDER BY `name` ASC");
123  if (!result)
124  return map;
125 
126  do
127  {
128  Field* fields = result->Fetch();
129 
130  AppliedFileEntry const entry = { fields[0].GetString(), fields[1].GetString(),
131  AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64() };
132 
133  map.insert(std::make_pair(entry.name, entry));
134  }
135  while (result->NextRow());
136 
137  return map;
138 }
uint64 GetUInt64() const
Definition: Field.h:184
Class used to access individual fields of database query result.
Definition: Field.h:56
static State StateConvert(std::string const &state)
Definition: UpdateFetcher.h:67
std::unordered_map< std::string, AppliedFileEntry > AppliedFileStorage
Definition: UpdateFetcher.h:104
std::shared_ptr< ResultSet > QueryResult
Definition: QueryResult.h:61
std::function< QueryResult(std::string const &)> const _retrieve
Definition: UpdateFetcher.h:129
std::string GetString() const
Definition: Field.h:276

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories ( ) const
private
85 {
86  DirectoryStorage directories;
87 
88  QueryResult const result = _retrieve("SELECT `path`, `state` FROM `updates_include`");
89  if (!result)
90  return directories;
91 
92  do
93  {
94  Field* fields = result->Fetch();
95 
96  std::string path = fields[0].GetString();
97  if (path.substr(0, 1) == "$")
98  path = _sourceDirectory.generic_string() + path.substr(1);
99 
100  Path const p(path);
101 
102  if (!is_directory(p))
103  {
104  TC_LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" isn't existing, skipped!", p.generic_string().c_str());
105  continue;
106  }
107 
108  DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(fields[1].GetString()) };
109  directories.push_back(entry);
110 
111  TC_LOG_TRACE("sql.updates", "Added applied file \"%s\" from remote.", p.filename().generic_string().c_str());
112 
113  } while (result->NextRow());
114 
115  return directories;
116 }
Path const _sourceDirectory
Definition: UpdateFetcher.h:125
Class used to access individual fields of database query result.
Definition: Field.h:56
static State StateConvert(std::string const &state)
Definition: UpdateFetcher.h:67
boost::filesystem::path Path
Definition: UpdateFetcher.h:30
#define TC_LOG_TRACE(filterType__,...)
Definition: Log.h:195
std::vector< UpdateFetcher::DirectoryEntry > DirectoryStorage
Definition: UpdateFetcher.h:105
std::shared_ptr< ResultSet > QueryResult
Definition: QueryResult.h:61
std::function< QueryResult(std::string const &)> const _retrieve
Definition: UpdateFetcher.h:129
#define TC_LOG_WARN(filterType__,...)
Definition: Log.h:204
std::string GetString() const
Definition: Field.h:276

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void UpdateFetcher::RenameEntry ( std::string const from,
std::string const to 
) const
private
370 {
371  // Delete target if it exists
372  {
373  std::string const update = "DELETE FROM `updates` WHERE `name`=\"" + to + "\"";
374 
375  // Update database
376  _apply(update);
377  }
378 
379  // Rename
380  {
381  std::string const update = "UPDATE `updates` SET `name`=\"" + to + "\" WHERE `name`=\"" + from + "\"";
382 
383  // Update database
384  _apply(update);
385  }
386 }
std::function< void(std::string const &)> const _apply
Definition: UpdateFetcher.h:127

+ Here is the caller graph for this function:

UpdateResult UpdateFetcher::Update ( bool const  redundancyChecks,
bool const  allowRehash,
bool const  archivedRedundancy,
int32 const  cleanDeadReferencesMaxCount 
) const
167 {
168  LocaleFileStorage const available = GetFileList();
170 
171  size_t countRecentUpdates = 0;
172  size_t countArchivedUpdates = 0;
173 
174  // Count updates
175  for (auto const& entry : applied)
176  if (entry.second.state == RELEASED)
177  ++countRecentUpdates;
178  else
179  ++countArchivedUpdates;
180 
181  // Fill hash to name cache
182  HashToFileNameStorage hashToName;
183  for (auto entry : applied)
184  hashToName.insert(std::make_pair(entry.second.hash, entry.first));
185 
186  size_t importedUpdates = 0;
187 
188  for (auto const& availableQuery : available)
189  {
190  TC_LOG_DEBUG("sql.updates", "Checking update \"%s\"...", availableQuery.first.filename().generic_string().c_str());
191 
192  AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string());
193  if (iter != applied.end())
194  {
195  // If redundancy is disabled skip it since the update is already applied.
196  if (!redundancyChecks)
197  {
198  TC_LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks.");
199  applied.erase(iter);
200  continue;
201  }
202 
203  // If the update is in an archived directory and is marked as archived in our database skip redundancy checks (archived updates never change).
204  if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED))
205  {
206  TC_LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks.");
207  applied.erase(iter);
208  continue;
209  }
210  }
211 
212  // Calculate hash
213  std::string const hash =
214  CalculateHash(ReadSQLUpdate(availableQuery.first));
215 
216  UpdateMode mode = MODE_APPLY;
217 
218  // Update is not in our applied list
219  if (iter == applied.end())
220  {
221  // Catch renames (different filename but same hash)
222  HashToFileNameStorage::const_iterator const hashIter = hashToName.find(hash);
223  if (hashIter != hashToName.end())
224  {
225  // Check if the original file was removed if not we've got a problem.
226  LocaleFileStorage::const_iterator localeIter;
227  // Push localeIter forward
228  for (localeIter = available.begin(); (localeIter != available.end()) &&
229  (localeIter->first.filename().string() != hashIter->second); ++localeIter);
230 
231  // Conflict!
232  if (localeIter != available.end())
233  {
234  TC_LOG_WARN("sql.updates", ">> Seems like update \"%s\" \'%s\' was renamed, but the old file is still there! " \
235  "Trade it as a new file! (Probably its an unmodified copy of file \"%s\")",
236  availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(),
237  localeIter->first.filename().string().c_str());
238  }
239  // Its save to trade the file as renamed here
240  else
241  {
242  TC_LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.",
243  hashIter->second.c_str(), availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
244 
245  RenameEntry(hashIter->second, availableQuery.first.filename().string());
246  applied.erase(hashIter->second);
247  continue;
248  }
249  }
250  // Apply the update if it was never seen before.
251  else
252  {
253  TC_LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...",
254  availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
255  }
256  }
257  // Rehash the update entry if it is contained in our database but with an empty hash.
258  else if (allowRehash && iter->second.hash.empty())
259  {
260  mode = MODE_REHASH;
261 
262  TC_LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", availableQuery.first.filename().string().c_str(),
263  hash.substr(0, 7).c_str());
264  }
265  else
266  {
267  // If the hash of the files differs from the one stored in our database reapply the update (because it was changed).
268  if (iter->second.hash != hash)
269  {
270  TC_LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", availableQuery.first.filename().string().c_str(),
271  iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str());
272  }
273  else
274  {
275  // If the file wasn't changed and just moved update its state if necessary.
276  if (iter->second.state != availableQuery.second)
277  {
278  TC_LOG_DEBUG("sql.updates", ">> Updating state of \"%s\" to \'%s\'...",
279  availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str());
280 
281  UpdateState(availableQuery.first.filename().string(), availableQuery.second);
282  }
283 
284  TC_LOG_DEBUG("sql.updates", ">> Update is already applied and is matching hash \'%s\'.", hash.substr(0, 7).c_str());
285 
286  applied.erase(iter);
287  continue;
288  }
289  }
290 
291  uint32 speed = 0;
292  AppliedFileEntry const file = { availableQuery.first.filename().string(), hash, availableQuery.second, 0 };
293 
294  switch (mode)
295  {
296  case MODE_APPLY:
297  speed = Apply(availableQuery.first);
298  /*no break*/
299  case MODE_REHASH:
300  UpdateEntry(file, speed);
301  break;
302  }
303 
304  if (iter != applied.end())
305  applied.erase(iter);
306 
307  if (mode == MODE_APPLY)
308  ++importedUpdates;
309  }
310 
311  // Cleanup up orphaned entries if enabled
312  if (!applied.empty())
313  {
314  bool const doCleanup = (cleanDeadReferencesMaxCount < 0) || (applied.size() <= static_cast<size_t>(cleanDeadReferencesMaxCount));
315 
316  for (auto const& entry : applied)
317  {
318  TC_LOG_WARN("sql.updates", ">> File \'%s\' was applied to the database but is missing in" \
319  " your update directory now!", entry.first.c_str());
320 
321  if (doCleanup)
322  TC_LOG_INFO("sql.updates", "Deleting orphaned entry \'%s\'...", entry.first.c_str());
323  }
324 
325  if (doCleanup)
326  CleanUp(applied);
327  else
328  {
329  TC_LOG_ERROR("sql.updates", "Cleanup is disabled! There are " SZFMTD " dirty files that were applied to your database " \
330  "but are now missing in your source directory!", applied.size());
331  }
332  }
333 
334  return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
335 }
std::string ReadSQLUpdate(Path const &file) const
Definition: UpdateFetcher.cpp:140
std::string CalculateHash(std::string const &query) const
Definition: UpdateFetcher.cpp:337
#define hash
Definition: private_namespace.h:186
#define SZFMTD
Definition: Define.h:143
static State StateConvert(std::string const &state)
Definition: UpdateFetcher.h:67
Definition: UpdateFetcher.h:51
LocaleFileStorage GetFileList() const
Definition: UpdateFetcher.cpp:41
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:198
Definition: UpdateFetcher.h:50
uint32 Apply(Path const &path) const
Definition: UpdateFetcher.cpp:346
std::set< LocaleFileEntry, PathCompare > LocaleFileStorage
Definition: UpdateFetcher.h:102
Definition: DBUpdater.h:44
std::unordered_map< std::string, std::string > HashToFileNameStorage
Definition: UpdateFetcher.h:103
Definition: UpdateFetcher.h:45
void UpdateEntry(AppliedFileEntry const &entry, uint32 const speed=0) const
Definition: UpdateFetcher.cpp:360
uint32_t uint32
Definition: Define.h:150
std::unordered_map< std::string, AppliedFileEntry > AppliedFileStorage
Definition: UpdateFetcher.h:104
Definition: UpdateFetcher.h:44
#define TC_LOG_WARN(filterType__,...)
Definition: Log.h:204
AppliedFileStorage ReceiveAppliedFiles() const
Definition: UpdateFetcher.cpp:118
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:201
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:207
void CleanUp(AppliedFileStorage const &storage) const
Definition: UpdateFetcher.cpp:388
void UpdateState(std::string const &name, State const state) const
Definition: UpdateFetcher.cpp:411
UpdateMode
Definition: UpdateFetcher.h:42
void RenameEntry(std::string const &from, std::string const &to) const
Definition: UpdateFetcher.cpp:369

+ Here is the call graph for this function:

void UpdateFetcher::UpdateEntry ( AppliedFileEntry const entry,
uint32 const  speed = 0 
) const
private
361 {
362  std::string const update = "REPLACE INTO `updates` (`name`, `hash`, `state`, `speed`) VALUES (\"" +
363  entry.name + "\", \"" + entry.hash + "\", \'" + entry.GetStateAsString() + "\', " + std::to_string(speed) + ")";
364 
365  // Update database
366  _apply(update);
367 }
std::function< void(std::string const &)> const _apply
Definition: UpdateFetcher.h:127

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void UpdateFetcher::UpdateState ( std::string const name,
State const  state 
) const
private
412 {
413  std::string const update = "UPDATE `updates` SET `state`=\'" + AppliedFileEntry::StateConvert(state) + "\' WHERE `name`=\"" + name + "\"";
414 
415  // Update database
416  _apply(update);
417 }
std::function< void(std::string const &)> const _apply
Definition: UpdateFetcher.h:127
static State StateConvert(std::string const &state)
Definition: UpdateFetcher.h:67

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Member Data Documentation

std::function<void(std::string const&)> const UpdateFetcher::_apply
private
std::function<void(Path const& path)> const UpdateFetcher::_applyFile
private
std::function<QueryResult(std::string const&)> const UpdateFetcher::_retrieve
private
Path const UpdateFetcher::_sourceDirectory
private

The documentation for this class was generated from the following files: