Akonadi

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

KDE's Doxygen guidelines are available online.