File indexing completed on 2025-01-05 04:46:58

0001 /*
0002     SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
0003     SPDX-FileCopyrightText: 2012 Volker Krause <vkrause@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "dbintrospector.h"
0009 #include "akonadiserver_debug.h"
0010 #include "datastore.h"
0011 #include "dbexception.h"
0012 #include "dbintrospector_impl.h"
0013 #include "dbtype.h"
0014 #include "querybuilder.h"
0015 
0016 #include <QSqlField>
0017 #include <QSqlQuery>
0018 #include <QSqlRecord>
0019 
0020 using namespace Akonadi::Server;
0021 
0022 DbIntrospector::Ptr DbIntrospector::createInstance(const QSqlDatabase &database)
0023 {
0024     switch (DbType::type(database)) {
0025     case DbType::MySQL:
0026         return Ptr(new DbIntrospectorMySql(database));
0027     case DbType::Sqlite:
0028         return Ptr(new DbIntrospectorSqlite(database));
0029     case DbType::PostgreSQL:
0030         return Ptr(new DbIntrospectorPostgreSql(database));
0031     case DbType::Unknown:
0032         break;
0033     }
0034     qCCritical(AKONADISERVER_LOG) << database.driverName() << "backend  not supported";
0035     return Ptr();
0036 }
0037 
0038 DbIntrospector::DbIntrospector(const QSqlDatabase &database)
0039     : m_database(database)
0040 {
0041 }
0042 
0043 DbIntrospector::~DbIntrospector()
0044 {
0045 }
0046 
0047 bool DbIntrospector::hasTable(const QString &tableName)
0048 {
0049     return m_database.tables().contains(tableName, Qt::CaseInsensitive);
0050 }
0051 
0052 bool DbIntrospector::hasIndex(const QString &tableName, const QString &indexName)
0053 {
0054     QSqlQuery query(m_database);
0055     if (!query.exec(hasIndexQuery(tableName, indexName))) {
0056         throw DbException(query, "Failed to query index");
0057     }
0058     return query.next();
0059 }
0060 
0061 bool DbIntrospector::hasColumn(const QString &tableName, const QString &columnName)
0062 {
0063     QStringList columns = m_columnCache.value(tableName);
0064 
0065     if (columns.isEmpty()) {
0066         // QPSQL requires the name to be lower case, but it breaks compatibility with existing
0067         // tables with other drivers (see BKO#409234). Yay for abstraction...
0068         const auto name = (DbType::type(m_database) == DbType::PostgreSQL) ? tableName.toLower() : tableName;
0069         const QSqlRecord table = m_database.record(name);
0070         const int numTables = table.count();
0071         columns.reserve(numTables);
0072         for (int i = 0; i < numTables; ++i) {
0073             const QSqlField column = table.field(i);
0074             columns.push_back(column.name().toLower());
0075         }
0076 
0077         m_columnCache.insert(tableName, columns);
0078     }
0079 
0080     return columns.contains(columnName.toLower());
0081 }
0082 
0083 bool DbIntrospector::isTableEmpty(const QString &tableName)
0084 {
0085     auto *store = DataStore::dataStoreForDatabase(m_database);
0086     QueryBuilder queryBuilder(store, tableName, QueryBuilder::Select);
0087     queryBuilder.addColumn(QStringLiteral("*"));
0088     queryBuilder.setLimit(1);
0089     if (!queryBuilder.exec()) {
0090         throw DbException(queryBuilder.query(), "Unable to retrieve data from table.");
0091     }
0092 
0093     QSqlQuery query = queryBuilder.query();
0094     return (query.size() == 0 || !query.first());
0095 }
0096 
0097 QList<DbIntrospector::ForeignKey> DbIntrospector::foreignKeyConstraints(const QString &tableName)
0098 {
0099     Q_UNUSED(tableName)
0100     return QList<ForeignKey>();
0101 }
0102 
0103 QString DbIntrospector::hasIndexQuery(const QString &tableName, const QString &indexName)
0104 {
0105     Q_UNUSED(tableName)
0106     Q_UNUSED(indexName)
0107     qCCritical(AKONADISERVER_LOG) << "Implement index support for your database!";
0108     return QString();
0109 }