Akonadi

dbupdater.cpp
1 /*
2  SPDX-FileCopyrightText: 2007-2012 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "dbupdater.h"
8 #include "akonadischema.h"
9 #include "akonadiserver_debug.h"
10 #include "datastore.h"
11 #include "dbconfig.h"
12 #include "dbinitializer_p.h"
13 #include "dbintrospector.h"
14 #include "dbtype.h"
15 #include "entities.h"
16 #include "querybuilder.h"
17 #include "selectquerybuilder.h"
18 
19 #include <private/dbus_p.h>
20 
21 #include <QCoreApplication>
22 #include <QDBusConnection>
23 #include <QDBusError>
24 #include <QMetaMethod>
25 #include <QSqlError>
26 #include <QSqlQuery>
27 #include <QThread>
28 
29 #include <QDomDocument>
30 #include <QElapsedTimer>
31 #include <QFile>
32 #include <QSqlResult>
33 
34 using namespace Akonadi;
35 using namespace Akonadi::Server;
36 
37 DbUpdater::DbUpdater(const QSqlDatabase &database, const QString &filename)
38  : m_database(database)
39  , m_filename(filename)
40 {
41 }
42 
44 {
46 
47  // TODO error handling
48  auto currentVersion = SchemaVersion::retrieveAll().at(0);
49 
50  UpdateSet::Map updates;
51 
52  if (!parseUpdateSets(currentVersion.version(), updates)) {
53  return false;
54  }
55 
56  if (updates.isEmpty()) {
57  return true;
58  }
59 
60  // indicate clients this might take a while
61  // we can ignore unregistration in error cases, that'll kill the server anyway
62  if (!QDBusConnection::sessionBus().registerService(DBus::serviceName(DBus::UpgradeIndicator))) {
63  qCCritical(AKONADISERVER_LOG) << "Unable to connect to dbus service: " << QDBusConnection::sessionBus().lastError().message();
64  }
65 
66  // QMap is sorted, so we should be replaying the changes in correct order
67  for (QMap<int, UpdateSet>::ConstIterator it = updates.constBegin(); it != updates.constEnd(); ++it) {
68  Q_ASSERT(it.key() > currentVersion.version());
69  qCDebug(AKONADISERVER_LOG) << "DbUpdater: update to version:" << it.key() << " mandatory:" << it.value().abortOnFailure;
70 
71  bool success = false;
72  bool hasTransaction = false;
73  if (it.value().complex) { // complex update
74  const QString methodName = QStringLiteral("complexUpdate_%1()").arg(it.value().version);
75  const int index = metaObject()->indexOfMethod(methodName.toLatin1().constData());
76  if (index == -1) {
77  success = false;
78  qCCritical(AKONADISERVER_LOG) << "Update to version" << it.value().version << "marked as complex, but no implementation is available";
79  } else {
80  const QMetaMethod method = metaObject()->method(index);
81  method.invoke(this, Q_RETURN_ARG(bool, success));
82  if (!success) {
83  qCCritical(AKONADISERVER_LOG) << "Update failed";
84  }
85  }
86  } else { // regular update
87  success = m_database.transaction();
88  if (success) {
89  hasTransaction = true;
90  const QStringList statements = it.value().statements;
91  for (const QString &statement : statements) {
92  QSqlQuery query(m_database);
93  success = query.exec(statement);
94  if (!success) {
95  qCCritical(AKONADISERVER_LOG) << "DBUpdater: query error:" << query.lastError().text() << m_database.lastError().text();
96  qCCritical(AKONADISERVER_LOG) << "Query was: " << statement;
97  qCCritical(AKONADISERVER_LOG) << "Target version was: " << it.key();
98  qCCritical(AKONADISERVER_LOG) << "Mandatory: " << it.value().abortOnFailure;
99  }
100  }
101  }
102  }
103 
104  if (success) {
105  currentVersion.setVersion(it.key());
106  success = currentVersion.update();
107  }
108 
109  if (!success || (hasTransaction && !m_database.commit())) {
110  qCCritical(AKONADISERVER_LOG) << "Failed to commit transaction for database update";
111  if (hasTransaction) {
112  m_database.rollback();
113  }
114  if (it.value().abortOnFailure) {
115  return false;
116  }
117  }
118  }
119 
120  QDBusConnection::sessionBus().unregisterService(DBus::serviceName(DBus::UpgradeIndicator));
121  return true;
122 }
123 
124 bool DbUpdater::parseUpdateSets(int currentVersion, UpdateSet::Map &updates) const
125 {
126  QFile file(m_filename);
127  if (!file.open(QIODevice::ReadOnly)) {
128  qCCritical(AKONADISERVER_LOG) << "Unable to open update description file" << m_filename;
129  return false;
130  }
131 
132  QDomDocument document;
133 
134  QString errorMsg;
135  int line;
136  int column;
137  if (!document.setContent(&file, &errorMsg, &line, &column)) {
138  qCCritical(AKONADISERVER_LOG) << "Unable to parse update description file" << m_filename << ":" << errorMsg << "at line" << line << "column" << column;
139  return false;
140  }
141 
142  const QDomElement documentElement = document.documentElement();
143  if (documentElement.tagName() != QLatin1String("updates")) {
144  qCCritical(AKONADISERVER_LOG) << "Invalid update description file format";
145  return false;
146  }
147 
148  // iterate over the xml document and extract update information into an UpdateSet
149  QDomElement updateElement = documentElement.firstChildElement();
150  while (!updateElement.isNull()) {
151  if (updateElement.tagName() == QLatin1String("update")) {
152  const int version = updateElement.attribute(QStringLiteral("version"), QStringLiteral("-1")).toInt();
153  if (version <= 0) {
154  qCCritical(AKONADISERVER_LOG) << "Invalid version attribute in database update description";
155  return false;
156  }
157 
158  if (updates.contains(version)) {
159  qCCritical(AKONADISERVER_LOG) << "Duplicate version attribute in database update description";
160  return false;
161  }
162 
163  if (version <= currentVersion) {
164  qCDebug(AKONADISERVER_LOG) << "skipping update" << version;
165  } else {
166  UpdateSet updateSet;
167  updateSet.version = version;
168  updateSet.abortOnFailure = (updateElement.attribute(QStringLiteral("abortOnFailure")) == QLatin1String("true"));
169 
170  QDomElement childElement = updateElement.firstChildElement();
171  while (!childElement.isNull()) {
172  if (childElement.tagName() == QLatin1String("raw-sql")) {
173  if (updateApplicable(childElement.attribute(QStringLiteral("backends")))) {
174  updateSet.statements << buildRawSqlStatement(childElement);
175  }
176  } else if (childElement.tagName() == QLatin1String("complex-update")) {
177  if (updateApplicable(childElement.attribute(QStringLiteral("backends")))) {
178  updateSet.complex = true;
179  }
180  }
181  // TODO: check for generic tags here in the future
182 
183  childElement = childElement.nextSiblingElement();
184  }
185 
186  if (!updateSet.statements.isEmpty() || updateSet.complex) {
187  updates.insert(version, updateSet);
188  }
189  }
190  }
191  updateElement = updateElement.nextSiblingElement();
192  }
193 
194  return true;
195 }
196 
197 bool DbUpdater::updateApplicable(const QString &backends) const
198 {
199  const QStringList matchingBackends = backends.split(QLatin1Char(','));
200 
201  QString currentBackend;
202  switch (DbType::type(m_database)) {
203  case DbType::MySQL:
204  currentBackend = QStringLiteral("mysql");
205  break;
206  case DbType::PostgreSQL:
207  currentBackend = QStringLiteral("psql");
208  break;
209  case DbType::Sqlite:
210  currentBackend = QStringLiteral("sqlite");
211  break;
212  case DbType::Unknown:
213  return false;
214  }
215 
216  return matchingBackends.contains(currentBackend);
217 }
218 
219 QString DbUpdater::buildRawSqlStatement(const QDomElement &element) const
220 {
221  return element.text().trimmed();
222 }
223 
224 bool DbUpdater::complexUpdate_25()
225 {
226  qCDebug(AKONADISERVER_LOG) << "Starting database update to version 25";
227 
228  DbType::Type dbType = DbType::type(DataStore::self()->database());
229 
230  QElapsedTimer ttotal;
231  ttotal.start();
232 
233  // Recover from possibly failed or interrupted update
234  {
235  // We don't care if this fails, it just means that there was no failed update
236  QSqlQuery query(DataStore::self()->database());
237  query.exec(QStringLiteral("ALTER TABLE PartTable_old RENAME TO PartTable"));
238  }
239 
240  {
241  QSqlQuery query(DataStore::self()->database());
242  query.exec(QStringLiteral("DROP TABLE IF EXISTS PartTable_new"));
243  }
244 
245  {
246  // Make sure the table is empty, otherwise we get duplicate key error
247  QSqlQuery query(DataStore::self()->database());
248  if (dbType == DbType::Sqlite) {
249  query.exec(QStringLiteral("DELETE FROM PartTypeTable"));
250  } else { // MySQL, PostgreSQL
251  query.exec(QStringLiteral("TRUNCATE TABLE PartTypeTable"));
252  }
253  }
254 
255  {
256  // It appears that more users than expected have the invalid "GID" part in their
257  // PartTable, which breaks the migration below (see BKO#331867), so we apply this
258  // wanna-be fix to remove the invalid part before we start the actual migration.
259  QueryBuilder qb(QStringLiteral("PartTable"), QueryBuilder::Delete);
260  qb.addValueCondition(QStringLiteral("PartTable.name"), Query::Equals, QLatin1String("GID"));
261  qb.exec();
262  }
263 
264  qCDebug(AKONADISERVER_LOG) << "Creating a PartTable_new";
265  {
266  TableDescription description;
267  description.name = QStringLiteral("PartTable_new");
268 
269  ColumnDescription idColumn;
270  idColumn.name = QStringLiteral("id");
271  idColumn.type = QStringLiteral("qint64");
272  idColumn.isAutoIncrement = true;
273  idColumn.isPrimaryKey = true;
274  description.columns << idColumn;
275 
276  ColumnDescription pimItemIdColumn;
277  pimItemIdColumn.name = QStringLiteral("pimItemId");
278  pimItemIdColumn.type = QStringLiteral("qint64");
279  pimItemIdColumn.allowNull = false;
280  description.columns << pimItemIdColumn;
281 
282  ColumnDescription partTypeIdColumn;
283  partTypeIdColumn.name = QStringLiteral("partTypeId");
284  partTypeIdColumn.type = QStringLiteral("qint64");
285  partTypeIdColumn.allowNull = false;
286  description.columns << partTypeIdColumn;
287 
288  ColumnDescription dataColumn;
289  dataColumn.name = QStringLiteral("data");
290  dataColumn.type = QStringLiteral("QByteArray");
291  description.columns << dataColumn;
292 
293  ColumnDescription dataSizeColumn;
294  dataSizeColumn.name = QStringLiteral("datasize");
295  dataSizeColumn.type = QStringLiteral("qint64");
296  dataSizeColumn.allowNull = false;
297  description.columns << dataSizeColumn;
298 
299  ColumnDescription versionColumn;
300  versionColumn.name = QStringLiteral("version");
301  versionColumn.type = QStringLiteral("int");
302  versionColumn.defaultValue = QStringLiteral("0");
303  description.columns << versionColumn;
304 
305  ColumnDescription externalColumn;
306  externalColumn.name = QStringLiteral("external");
307  externalColumn.type = QStringLiteral("bool");
308  externalColumn.defaultValue = QStringLiteral("false");
309  description.columns << externalColumn;
310 
312  const QString queryString = initializer->buildCreateTableStatement(description);
313 
314  QSqlQuery query(DataStore::self()->database());
315  if (!query.exec(queryString)) {
316  qCCritical(AKONADISERVER_LOG) << query.lastError().text();
317  return false;
318  }
319  }
320 
321  qCDebug(AKONADISERVER_LOG) << "Migrating part types";
322  {
323  // Get list of all part names
324  QueryBuilder qb(QStringLiteral("PartTable"), QueryBuilder::Select);
325  qb.setDistinct(true);
326  qb.addColumn(QStringLiteral("PartTable.name"));
327 
328  if (!qb.exec()) {
329  qCCritical(AKONADISERVER_LOG) << qb.query().lastError().text();
330  return false;
331  }
332 
333  // Process them one by one
334  QSqlQuery query = qb.query();
335  while (query.next()) {
336  // Split the part name to namespace and name and insert it to PartTypeTable
337  const QString partName = query.value(0).toString();
338  const QString ns = partName.left(3);
339  const QString name = partName.mid(4);
340 
341  {
342  QueryBuilder qb(QStringLiteral("PartTypeTable"), QueryBuilder::Insert);
343  qb.setColumnValue(QStringLiteral("ns"), ns);
344  qb.setColumnValue(QStringLiteral("name"), name);
345  if (!qb.exec()) {
346  qCCritical(AKONADISERVER_LOG) << qb.query().lastError().text();
347  return false;
348  }
349  }
350  qCDebug(AKONADISERVER_LOG) << "\t Moved part type" << partName << "to PartTypeTable";
351  }
352  query.finish();
353  }
354 
355  qCDebug(AKONADISERVER_LOG) << "Migrating data from PartTable to PartTable_new";
356  {
357  QSqlQuery query(DataStore::self()->database());
358  QString queryString;
359  if (dbType == DbType::PostgreSQL) {
360  queryString = QStringLiteral(
361  "INSERT INTO PartTable_new (id, pimItemId, partTypeId, data, datasize, version, external) "
362  "SELECT PartTable.id, PartTable.pimItemId, PartTypeTable.id, PartTable.data, "
363  " PartTable.datasize, PartTable.version, PartTable.external "
364  "FROM PartTable "
365  "LEFT JOIN PartTypeTable ON "
366  " PartTable.name = CONCAT(PartTypeTable.ns, ':', PartTypeTable.name)");
367  } else if (dbType == DbType::MySQL) {
368  queryString = QStringLiteral(
369  "INSERT INTO PartTable_new (id, pimItemId, partTypeId, data, datasize, version, external) "
370  "SELECT PartTable.id, PartTable.pimItemId, PartTypeTable.id, PartTable.data, "
371  "PartTable.datasize, PartTable.version, PartTable.external "
372  "FROM PartTable "
373  "LEFT JOIN PartTypeTable ON PartTable.name = CONCAT(PartTypeTable.ns, ':', PartTypeTable.name)");
374  } else if (dbType == DbType::Sqlite) {
375  queryString = QStringLiteral(
376  "INSERT INTO PartTable_new (id, pimItemId, partTypeId, data, datasize, version, external) "
377  "SELECT PartTable.id, PartTable.pimItemId, PartTypeTable.id, PartTable.data, "
378  "PartTable.datasize, PartTable.version, PartTable.external "
379  "FROM PartTable "
380  "LEFT JOIN PartTypeTable ON PartTable.name = PartTypeTable.ns || ':' || PartTypeTable.name");
381  }
382 
383  if (!query.exec(queryString)) {
384  qCCritical(AKONADISERVER_LOG) << query.lastError().text();
385  return false;
386  }
387  }
388 
389  qCDebug(AKONADISERVER_LOG) << "Swapping PartTable_new for PartTable";
390  {
391  // Does an atomic swap
392 
393  QSqlQuery query(DataStore::self()->database());
394 
395  if (dbType == DbType::PostgreSQL || dbType == DbType::Sqlite) {
396  if (dbType == DbType::PostgreSQL) {
397  DataStore::self()->beginTransaction(QStringLiteral("DBUPDATER (r25)"));
398  }
399 
400  if (!query.exec(QStringLiteral("ALTER TABLE PartTable RENAME TO PartTable_old"))) {
401  qCCritical(AKONADISERVER_LOG) << query.lastError().text();
403  return false;
404  }
405 
406  // If this fails in SQLite (i.e. without transaction), we can still recover on next start)
407  if (!query.exec(QStringLiteral("ALTER TABLE PartTable_new RENAME TO PartTable"))) {
408  qCCritical(AKONADISERVER_LOG) << query.lastError().text();
409  if (DataStore::self()->inTransaction()) {
411  }
412  return false;
413  }
414 
415  if (dbType == DbType::PostgreSQL) {
417  }
418  } else { // MySQL cannot do rename in transaction, but supports atomic renames
419  if (!query.exec(QStringLiteral("RENAME TABLE PartTable TO PartTable_old,"
420  " PartTable_new TO PartTable"))) {
421  qCCritical(AKONADISERVER_LOG) << query.lastError().text();
422  return false;
423  }
424  }
425  }
426 
427  qCDebug(AKONADISERVER_LOG) << "Removing PartTable_old";
428  {
429  QSqlQuery query(DataStore::self()->database());
430  if (!query.exec(QStringLiteral("DROP TABLE PartTable_old;"))) {
431  // It does not matter when this fails, we are successfully migrated
432  qCDebug(AKONADISERVER_LOG) << query.lastError().text();
433  qCDebug(AKONADISERVER_LOG) << "Not a fatal problem, continuing...";
434  }
435  }
436 
437  // Fine tuning for PostgreSQL
438  qCDebug(AKONADISERVER_LOG) << "Final tuning of new PartTable";
439  {
440  QSqlQuery query(DataStore::self()->database());
441  if (dbType == DbType::PostgreSQL) {
442  query.exec(QStringLiteral("ALTER TABLE PartTable RENAME CONSTRAINT parttable_new_pkey TO parttable_pkey"));
443  query.exec(QStringLiteral("ALTER SEQUENCE parttable_new_id_seq RENAME TO parttable_id_seq"));
444  query.exec(QStringLiteral("SELECT setval('parttable_id_seq', MAX(id) + 1) FROM PartTable"));
445  } else if (dbType == DbType::MySQL) {
446  // 0 will automatically reset AUTO_INCREMENT to SELECT MAX(id) + 1 FROM PartTable
447  query.exec(QStringLiteral("ALTER TABLE PartTable AUTO_INCREMENT = 0"));
448  }
449  }
450 
451  qCDebug(AKONADISERVER_LOG) << "Update done in" << ttotal.elapsed() << "ms";
452 
453  // Foreign keys and constraints will be reconstructed automatically once
454  // all updates are done
455 
456  return true;
457 }
458 
459 bool DbUpdater::complexUpdate_36()
460 {
461  qCDebug(AKONADISERVER_LOG, "Starting database update to version 36");
462  Q_ASSERT(DbType::type(DataStore::self()->database()) == DbType::Sqlite);
463 
464  QSqlQuery query(DataStore::self()->database());
465  if (!query.exec(QStringLiteral("PRAGMA foreign_key_checks=OFF"))) {
466  qCCritical(AKONADISERVER_LOG, "Failed to disable foreign key checks!");
467  return false;
468  }
469 
470  const auto hasForeignKeys = [](const TableDescription &desc) {
471  return std::any_of(desc.columns.cbegin(), desc.columns.cend(), [](const ColumnDescription &col) {
472  return !col.refTable.isEmpty() && !col.refColumn.isEmpty();
473  });
474  };
475 
476  const auto recreateTableWithForeignKeys = [](const TableDescription &table) -> QPair<bool, QSqlQuery> {
477  qCDebug(AKONADISERVER_LOG) << "Updating foreign keys in table" << table.name;
478 
479  QSqlQuery query(DataStore::self()->database());
480 
481  // Recover from possibly failed or interrupted update
482  // We don't care if this fails, it just means that there was no failed update
483  query.exec(QStringLiteral("ALTER TABLE %1_old RENAME TO %1").arg(table.name));
484  query.exec(QStringLiteral("DROP TABLE %1_new").arg(table.name));
485 
486  qCDebug(AKONADISERVER_LOG, "\tCreating table %s_new with foreign keys", qUtf8Printable(table.name));
487  {
488  const auto initializer = DbInitializer::createInstance(DataStore::self()->database());
489  TableDescription copy = table;
490  copy.name += QStringLiteral("_new");
491  if (!query.exec(initializer->buildCreateTableStatement(copy))) {
492  // If this fails we will recover on next start
493  return {false, query};
494  }
495  }
496 
497  qCDebug(AKONADISERVER_LOG,
498  "\tCopying values from %s to %s_new (this may take a very long of time...)",
499  qUtf8Printable(table.name),
500  qUtf8Printable(table.name));
501  if (!query.exec(QStringLiteral("INSERT INTO %1_new SELECT * FROM %1").arg(table.name))) {
502  // If this fails, we will recover on next start
503  return {false, query};
504  }
505 
506  qCDebug(AKONADISERVER_LOG, "\tSwapping %s_new for %s", qUtf8Printable(table.name), qUtf8Printable(table.name));
507  if (!query.exec(QStringLiteral("ALTER TABLE %1 RENAME TO %1_old").arg(table.name))) {
508  // If this fails we will recover on next start
509  return {false, query};
510  }
511 
512  if (!query.exec(QStringLiteral("ALTER TABLE %1_new RENAME TO %1").arg(table.name))) {
513  // If this fails we will recover on next start
514  return {false, query};
515  }
516 
517  qCDebug(AKONADISERVER_LOG, "\tRemoving table %s_old", qUtf8Printable(table.name));
518  if (!query.exec(QStringLiteral("DROP TABLE %1_old").arg(table.name))) {
519  // We don't care if this fails
520  qCWarning(AKONADISERVER_LOG, "Failed to DROP TABLE %s (not fatal, update will continue)", qUtf8Printable(table.name));
521  qCWarning(AKONADISERVER_LOG, "Error: %s", qUtf8Printable(query.lastError().text()));
522  }
523 
524  qCDebug(AKONADISERVER_LOG) << "\tOptimizing table %s", qUtf8Printable(table.name);
525  if (!query.exec(QStringLiteral("ANALYZE %1").arg(table.name))) {
526  // We don't care if this fails
527  qCWarning(AKONADISERVER_LOG, "Failed to ANALYZE %s (not fatal, update will continue)", qUtf8Printable(table.name));
528  qCWarning(AKONADISERVER_LOG, "Error: %s", qUtf8Printable(query.lastError().text()));
529  }
530 
531  qCDebug(AKONADISERVER_LOG) << "\tDone";
532  return {true, QSqlQuery()};
533  };
534 
535  AkonadiSchema schema;
536  const auto tables = schema.tables();
537  for (const auto &table : tables) {
538  if (!hasForeignKeys(table)) {
539  continue;
540  }
541 
542  const auto &[ok, query] = recreateTableWithForeignKeys(table);
543  if (!ok) {
544  qCCritical(AKONADISERVER_LOG, "SQL error when updating table %s", qUtf8Printable(table.name));
545  qCCritical(AKONADISERVER_LOG, "Query: %s", qUtf8Printable(query.executedQuery()));
546  qCCritical(AKONADISERVER_LOG, "Error: %s", qUtf8Printable(query.lastError().text()));
547  return false;
548  }
549  }
550 
551  const auto relations = schema.relations();
552  for (const auto &relation : relations) {
553  const RelationTableDescription table(relation);
554  const auto &[ok, query] = recreateTableWithForeignKeys(table);
555  if (!ok) {
556  qCCritical(AKONADISERVER_LOG, "SQL error when updating relation table %s", qUtf8Printable(table.name));
557  qCCritical(AKONADISERVER_LOG, "Query: %s", qUtf8Printable(query.executedQuery()));
558  qCCritical(AKONADISERVER_LOG, "Error: %s", qUtf8Printable(query.lastError().text()));
559  return false;
560  }
561  }
562 
563  qCDebug(AKONADISERVER_LOG) << "Running VACUUM to reduce DB size";
564  if (!query.exec(QStringLiteral("VACUUM"))) {
565  qCWarning(AKONADISERVER_LOG) << "Vacuum failed (not fatal, update will continue)";
566  qCWarning(AKONADISERVER_LOG) << "Error:" << query.lastError().text();
567  }
568 
569  return true;
570 }
QMap::const_iterator constBegin() const const
bool invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) const const
A helper class that describes a column of a table for the DbInitializer.
Definition: schematypes.h:32
QString text() const const
bool contains(const Key &key) const const
static DataStore * self()
Per thread singleton.
Definition: datastore.cpp:215
QString tagName() const const
Type type(const QSqlDatabase &db)
Returns the type of the given database object.
Definition: dbtype.cpp:11
QString message() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool isNull() const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString trimmed() const const
bool rollback()
A helper class that contains an update set.
Definition: dbupdater.h:24
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QByteArray toLatin1() const const
virtual bool beginTransaction(const QString &name)
Begins a transaction.
Definition: datastore.cpp:1350
QThread * thread() const const
QDBusError lastError() const const
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
QMap::iterator insert(const Key &key, const T &value)
QDBusConnection sessionBus()
static DbInitializer::Ptr createInstance(const QSqlDatabase &database, Schema *schema=nullptr)
Returns an initializer instance for a given backend.
bool unregisterService(const QString &serviceName)
TableDescription constructed based on RelationDescription.
Definition: schematypes.h:122
Type
Supported database types.
Definition: dbtype.h:19
virtual bool rollbackTransaction()
Reverts all changes within the current transaction.
Definition: datastore.cpp:1393
QMap::const_iterator constEnd() const const
QCoreApplication * instance()
A helper class that describes a table for the DbInitializer.
Definition: schematypes.h:92
QMetaMethod method(int index) const const
QThread * currentThread()
int toInt(bool *ok, int base) const const
QDomElement nextSiblingElement(const QString &tagName) const const
bool isEmpty() const const
virtual bool commitTransaction()
Commits all changes within the current transaction and emits all collected notification signals.
Definition: datastore.cpp:1415
int indexOfMethod(const char *method) const const
qint64 elapsed() const const
QDomElement documentElement() const const
QDomElement firstChildElement(const QString &tagName) const const
virtual const QMetaObject * metaObject() const const
QString text() const const
const char * constData() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
unsigned int version()
QString left(int n) const const
QString name(StandardShortcut id)
bool run()
Starts the update process.
Definition: dbupdater.cpp:43
bool transaction()
QString attribute(const QString &name, const QString &defValue) const const
DbUpdater(const QSqlDatabase &database, const QString &filename)
Creates a new database updates.
Definition: dbupdater.cpp:37
QSqlError lastError() const const
QString mid(int position, int n) const const
T value(int i) const const
const QList< QKeySequence > & copy()
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:31
Helper integration between Akonadi and Qt.
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jul 2 2022 06:41:47 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.