qBittorrent
BitTorrent::DBResumeDataStorage Class Referencefinal

#include <dbresumedatastorage.h>

Inheritance diagram for BitTorrent::DBResumeDataStorage:
Collaboration diagram for BitTorrent::DBResumeDataStorage:

Classes

class  Worker
 

Public Member Functions

 DBResumeDataStorage (const QString &dbPath, QObject *parent=nullptr)
 
 ~DBResumeDataStorage () override
 
QVector< TorrentIDregisteredTorrents () const override
 
std::optional< LoadTorrentParamsload (const TorrentID &id) const override
 
void store (const TorrentID &id, const LoadTorrentParams &resumeData) const override
 
void remove (const TorrentID &id) const override
 
void storeQueue (const QVector< TorrentID > &queue) const override
 

Private Member Functions

int currentDBVersion () const
 
void createDB () const
 
void updateDBFromVersion1 () const
 

Private Attributes

QThread * m_ioThread = nullptr
 
Workerm_asyncWorker = nullptr
 

Detailed Description

Definition at line 37 of file dbresumedatastorage.h.

Constructor & Destructor Documentation

◆ DBResumeDataStorage()

BitTorrent::DBResumeDataStorage::DBResumeDataStorage ( const QString &  dbPath,
QObject *  parent = nullptr 
)
explicit

Definition at line 191 of file dbresumedatastorage.cpp.

192  : ResumeDataStorage {parent}
193  , m_ioThread {new QThread(this)}
194 {
195  const bool needCreateDB = !QFile::exists(dbPath);
196 
197  auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), DB_CONNECTION_NAME);
198  db.setDatabaseName(dbPath);
199  if (!db.open())
200  throw RuntimeError(db.lastError().text());
201 
202  if (needCreateDB)
203  {
204  createDB();
205  }
206  else
207  {
208  const int dbVersion = currentDBVersion();
209  if (dbVersion == 1)
211  }
212 
213  m_asyncWorker = new Worker(dbPath, QLatin1String("ResumeDataStorageWorker"));
214  m_asyncWorker->moveToThread(m_ioThread);
215  connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
216  m_ioThread->start();
217 
218  RuntimeError *errPtr = nullptr;
219  QMetaObject::invokeMethod(m_asyncWorker, [this, &errPtr]()
220  {
221  try
222  {
224  }
225  catch (const RuntimeError &err)
226  {
227  errPtr = new RuntimeError(err);
228  }
229  }, Qt::BlockingQueuedConnection);
230 
231  if (errPtr)
232  throw *errPtr;
233 }

References createDB(), currentDBVersion(), anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME, m_asyncWorker, m_ioThread, BitTorrent::DBResumeDataStorage::Worker::openDatabase(), and updateDBFromVersion1().

Here is the call graph for this function:

◆ ~DBResumeDataStorage()

BitTorrent::DBResumeDataStorage::~DBResumeDataStorage ( )
override

Definition at line 235 of file dbresumedatastorage.cpp.

236 {
237  QMetaObject::invokeMethod(m_asyncWorker, &Worker::closeDatabase);
238  QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
239 
240  m_ioThread->quit();
241  m_ioThread->wait();
242 }

References anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME.

Member Function Documentation

◆ createDB()

void BitTorrent::DBResumeDataStorage::createDB ( ) const
private

Definition at line 390 of file dbresumedatastorage.cpp.

391 {
392  auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
393 
394  if (!db.transaction())
395  throw RuntimeError(db.lastError().text());
396 
397  QSqlQuery query {db};
398 
399  try
400  {
401  const QStringList tableMetaItems = {
402  makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
403  makeColumnDefinition(DB_COLUMN_NAME, "TEXT NOT NULL UNIQUE"),
405  };
406  const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
407  if (!query.exec(createTableMetaQuery))
408  throw RuntimeError(query.lastError().text());
409 
410  const QString insertMetaVersionQuery = makeInsertStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
411  if (!query.prepare(insertMetaVersionQuery))
412  throw RuntimeError(query.lastError().text());
413 
414  query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1(META_VERSION));
415  query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
416 
417  if (!query.exec())
418  throw RuntimeError(query.lastError().text());
419 
420  const QStringList tableTorrentsItems = {
421  makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
422  makeColumnDefinition(DB_COLUMN_TORRENT_ID, "BLOB NOT NULL UNIQUE"),
423  makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, "INTEGER NOT NULL DEFAULT -1"),
429  makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
432  makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, "INTEGER NOT NULL"),
434  makeColumnDefinition(DB_COLUMN_STOPPED, "INTEGER NOT NULL"),
435  makeColumnDefinition(DB_COLUMN_RESUMEDATA, "BLOB NOT NULL"),
437  };
438  const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
439  if (!query.exec(createTableTorrentsQuery))
440  throw RuntimeError(query.lastError().text());
441 
442  if (!db.commit())
443  throw RuntimeError(db.lastError().text());
444  }
445  catch (const RuntimeError &)
446  {
447  db.rollback();
448  throw;
449  }
450 }
QString makeColumnDefinition(const Column &column, const char *definition)
QString makeInsertStatement(const QString &tableName, const QVector< Column > &columns)
QString makeCreateTableStatement(const QString &tableName, const QStringList &items)

References anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_CATEGORY, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_CONTENT_LAYOUT, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_HAS_SEED_STATUS, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_ID, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_METADATA, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_OPERATING_MODE, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_QUEUE_POSITION, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_RATIO_LIMIT, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_RESUMEDATA, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_SEEDING_TIME_LIMIT, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_STOPPED, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TAGS, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TARGET_SAVE_PATH, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TORRENT_ID, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_VALUE, anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_META, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_TORRENTS, anonymous_namespace{dbresumedatastorage.cpp}::DB_VERSION, anonymous_namespace{dbresumedatastorage.cpp}::makeColumnDefinition(), anonymous_namespace{dbresumedatastorage.cpp}::makeCreateTableStatement(), anonymous_namespace{dbresumedatastorage.cpp}::makeInsertStatement(), anonymous_namespace{dbresumedatastorage.cpp}::META_VERSION, and anonymous_namespace{dbresumedatastorage.cpp}::Column::placeholder.

Referenced by DBResumeDataStorage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ currentDBVersion()

int BitTorrent::DBResumeDataStorage::currentDBVersion ( ) const
private

Definition at line 363 of file dbresumedatastorage.cpp.

364 {
365  const auto selectDBVersionStatement = QString::fromLatin1("SELECT %1 FROM %2 WHERE %3 = %4;")
367 
368  auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
369  QSqlQuery query {db};
370 
371  if (!query.prepare(selectDBVersionStatement))
372  throw RuntimeError(query.lastError().text());
373 
374  query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1(META_VERSION));
375 
376  if (!query.exec())
377  throw RuntimeError(query.lastError().text());
378 
379  if (!query.next())
380  throw RuntimeError(tr("Database is corrupted."));
381 
382  bool ok;
383  const int dbVersion = query.value(0).toInt(&ok);
384  if (!ok)
385  throw RuntimeError(tr("Database is corrupted."));
386 
387  return dbVersion;
388 }

References anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_VALUE, anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_META, anonymous_namespace{dbresumedatastorage.cpp}::META_VERSION, anonymous_namespace{dbresumedatastorage.cpp}::Column::name, anonymous_namespace{dbresumedatastorage.cpp}::Column::placeholder, and anonymous_namespace{dbresumedatastorage.cpp}::quoted().

Referenced by DBResumeDataStorage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load()

std::optional< BitTorrent::LoadTorrentParams > BitTorrent::DBResumeDataStorage::load ( const TorrentID id) const
overridevirtual

Implements BitTorrent::ResumeDataStorage.

Definition at line 263 of file dbresumedatastorage.cpp.

264 {
265  const QString selectTorrentStatement =
266  QString(QLatin1String("SELECT * FROM %1 WHERE %2 = %3;"))
268 
269  auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
270  QSqlQuery query {db};
271  try
272  {
273  if (!query.prepare(selectTorrentStatement))
274  throw RuntimeError(query.lastError().text());
275 
276  query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
277  if (!query.exec())
278  throw RuntimeError(query.lastError().text());
279 
280  if (!query.next())
281  throw RuntimeError(tr("Not found."));
282  }
283  catch (const RuntimeError &err)
284  {
285  LogMsg(tr("Couldn't load resume data of torrent '%1'. Error: %2")
286  .arg(id.toString(), err.message()), Log::CRITICAL);
287  return std::nullopt;
288  }
289 
290  LoadTorrentParams resumeData;
291  resumeData.restored = true;
292  resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
293  resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
294  const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
295  if (!tagsData.isEmpty())
296  {
297  const QStringList tagList = tagsData.split(QLatin1Char(','));
298  resumeData.tags.insert(tagList.cbegin(), tagList.cend());
299  }
300  resumeData.hasSeedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
301  resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
302  resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
303  resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
304  resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
305  query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
306  resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
307  query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
308  resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
309 
310  resumeData.savePath = Profile::instance()->fromPortablePath(
311  Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
312  resumeData.useAutoTMM = resumeData.savePath.isEmpty();
313  if (!resumeData.useAutoTMM)
314  {
315  resumeData.downloadPath = Profile::instance()->fromPortablePath(
316  Utils::Fs::toUniformPath(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
317  }
318 
319  const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
320  const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray();
321  const QByteArray allData = ((bencodedMetadata.isEmpty() || bencodedResumeData.isEmpty())
322  ? bencodedResumeData
323  : (bencodedResumeData.chopped(1) + bencodedMetadata.mid(1)));
324 
325  lt::error_code ec;
326  const lt::bdecode_node root = lt::bdecode(allData, ec);
327 
328  resumeData.downloadPath = Profile::instance()->fromPortablePath(
329  Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
330 
331  lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
332 
333  p = lt::read_resume_data(root, ec);
334  p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
335 
336  return resumeData;
337 }
QString message() const noexcept
Definition: exceptions.cpp:36
QString fromPortablePath(const QString &portablePath) const
Definition: profile.cpp:126
static const Profile * instance()
Definition: profile.cpp:67
void LogMsg(const QString &message, const Log::MsgType &type)
Definition: logger.cpp:125
@ CRITICAL
Definition: logger.h:48
QString toUniformPath(const QString &path)
Definition: fs.cpp:69
QString toString(const lt::socket_type_t socketType)
Definition: session.cpp:183

References BitTorrent::LoadTorrentParams::category, BitTorrent::LoadTorrentParams::contentLayout, Log::CRITICAL, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_CATEGORY, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_CONTENT_LAYOUT, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_DOWNLOAD_PATH, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_HAS_SEED_STATUS, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_METADATA, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_OPERATING_MODE, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_RATIO_LIMIT, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_RESUMEDATA, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_SEEDING_TIME_LIMIT, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_STOPPED, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TAGS, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TARGET_SAVE_PATH, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TORRENT_ID, anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_TORRENTS, BitTorrent::LoadTorrentParams::downloadPath, BitTorrent::LoadTorrentParams::firstLastPiecePriority, anonymous_namespace{dbresumedatastorage.cpp}::fromLTString(), Profile::fromPortablePath(), BitTorrent::LoadTorrentParams::hasSeedStatus, Profile::instance(), LogMsg(), BitTorrent::LoadTorrentParams::ltAddTorrentParams, Exception::message(), anonymous_namespace{dbresumedatastorage.cpp}::Column::name, BitTorrent::LoadTorrentParams::name, BitTorrent::LoadTorrentParams::operatingMode, anonymous_namespace{dbresumedatastorage.cpp}::Column::placeholder, anonymous_namespace{dbresumedatastorage.cpp}::quoted(), BitTorrent::LoadTorrentParams::ratioLimit, BitTorrent::LoadTorrentParams::restored, BitTorrent::LoadTorrentParams::savePath, BitTorrent::LoadTorrentParams::seedingTimeLimit, BitTorrent::LoadTorrentParams::stopped, BitTorrent::LoadTorrentParams::tags, anonymous_namespace{session.cpp}::toString(), Utils::Fs::toUniformPath(), and BitTorrent::LoadTorrentParams::useAutoTMM.

Here is the call graph for this function:

◆ registeredTorrents()

QVector< BitTorrent::TorrentID > BitTorrent::DBResumeDataStorage::registeredTorrents ( ) const
overridevirtual

Implements BitTorrent::ResumeDataStorage.

Definition at line 244 of file dbresumedatastorage.cpp.

245 {
246  const auto selectTorrentIDStatement = QString::fromLatin1("SELECT %1 FROM %2 ORDER BY %3;")
248 
249  auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
250  QSqlQuery query {db};
251 
252  if (!query.exec(selectTorrentIDStatement))
253  throw RuntimeError(query.lastError().text());
254 
255  QVector<TorrentID> registeredTorrents;
256  registeredTorrents.reserve(query.size());
257  while (query.next())
258  registeredTorrents.append(BitTorrent::TorrentID::fromString(query.value(0).toString()));
259 
260  return registeredTorrents;
261 }
QVector< TorrentID > registeredTorrents() const override
static TorrentID fromString(const QString &hashString)
Definition: infohash.cpp:76

References anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_QUEUE_POSITION, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_TORRENT_ID, anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_TORRENTS, BitTorrent::TorrentID::fromString(), anonymous_namespace{dbresumedatastorage.cpp}::Column::name, and anonymous_namespace{dbresumedatastorage.cpp}::quoted().

Here is the call graph for this function:

◆ remove()

void BitTorrent::DBResumeDataStorage::remove ( const TorrentID id) const
overridevirtual

Implements BitTorrent::ResumeDataStorage.

Definition at line 347 of file dbresumedatastorage.cpp.

348 {
349  QMetaObject::invokeMethod(m_asyncWorker, [this, id]()
350  {
351  m_asyncWorker->remove(id);
352  });
353 }
void remove(const TorrentID &id) const

◆ store()

void BitTorrent::DBResumeDataStorage::store ( const TorrentID id,
const LoadTorrentParams resumeData 
) const
overridevirtual

Implements BitTorrent::ResumeDataStorage.

Definition at line 339 of file dbresumedatastorage.cpp.

340 {
341  QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
342  {
343  m_asyncWorker->store(id, resumeData);
344  });
345 }
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const

◆ storeQueue()

void BitTorrent::DBResumeDataStorage::storeQueue ( const QVector< TorrentID > &  queue) const
overridevirtual

Implements BitTorrent::ResumeDataStorage.

Definition at line 355 of file dbresumedatastorage.cpp.

356 {
357  QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
358  {
359  m_asyncWorker->storeQueue(queue);
360  });
361 }
void storeQueue(const QVector< TorrentID > &queue) const

◆ updateDBFromVersion1()

void BitTorrent::DBResumeDataStorage::updateDBFromVersion1 ( ) const
private

Definition at line 452 of file dbresumedatastorage.cpp.

453 {
454  auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
455 
456  if (!db.transaction())
457  throw RuntimeError(db.lastError().text());
458 
459  QSqlQuery query {db};
460 
461  try
462  {
463  const auto alterTableTorrentsQuery = QString::fromLatin1("ALTER TABLE %1 ADD %2")
465  if (!query.exec(alterTableTorrentsQuery))
466  throw RuntimeError(query.lastError().text());
467 
468  const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
469  if (!query.prepare(updateMetaVersionQuery))
470  throw RuntimeError(query.lastError().text());
471 
472  query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1(META_VERSION));
473  query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
474 
475  if (!query.exec())
476  throw RuntimeError(query.lastError().text());
477 
478  if (!db.commit())
479  throw RuntimeError(db.lastError().text());
480  }
481  catch (const RuntimeError &)
482  {
483  db.rollback();
484  throw;
485  }
486 }
QString makeUpdateStatement(const QString &tableName, const QVector< Column > &columns)

References anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_DOWNLOAD_PATH, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_COLUMN_VALUE, anonymous_namespace{dbresumedatastorage.cpp}::DB_CONNECTION_NAME, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_META, anonymous_namespace{dbresumedatastorage.cpp}::DB_TABLE_TORRENTS, anonymous_namespace{dbresumedatastorage.cpp}::DB_VERSION, anonymous_namespace{dbresumedatastorage.cpp}::makeColumnDefinition(), anonymous_namespace{dbresumedatastorage.cpp}::makeUpdateStatement(), anonymous_namespace{dbresumedatastorage.cpp}::META_VERSION, anonymous_namespace{dbresumedatastorage.cpp}::Column::placeholder, and anonymous_namespace{dbresumedatastorage.cpp}::quoted().

Referenced by DBResumeDataStorage().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_asyncWorker

Worker* BitTorrent::DBResumeDataStorage::m_asyncWorker = nullptr
private

Definition at line 60 of file dbresumedatastorage.h.

Referenced by DBResumeDataStorage().

◆ m_ioThread

QThread* BitTorrent::DBResumeDataStorage::m_ioThread = nullptr
private

Definition at line 57 of file dbresumedatastorage.h.

Referenced by DBResumeDataStorage().


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