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_impl.h"
0009 #include "datastore.h"
0010 #include "dbexception.h"
0011 #include "querybuilder.h"
0012 
0013 #include "akonadiserver_debug.h"
0014 
0015 using namespace Akonadi::Server;
0016 
0017 // BEGIN MySql
0018 
0019 DbIntrospectorMySql::DbIntrospectorMySql(const QSqlDatabase &database)
0020     : DbIntrospector(database)
0021 {
0022 }
0023 
0024 QString DbIntrospectorMySql::hasIndexQuery(const QString &tableName, const QString &indexName)
0025 {
0026     return QStringLiteral("SHOW INDEXES FROM %1 WHERE `Key_name` = '%2'").arg(tableName, indexName);
0027 }
0028 
0029 QList<DbIntrospector::ForeignKey> DbIntrospectorMySql::foreignKeyConstraints(const QString &tableName)
0030 {
0031     auto store = DataStore::dataStoreForDatabase(m_database);
0032     QueryBuilder qb(store, QStringLiteral("information_schema.REFERENTIAL_CONSTRAINTS"), QueryBuilder::Select);
0033     qb.addJoin(QueryBuilder::InnerJoin,
0034                QStringLiteral("information_schema.KEY_COLUMN_USAGE"),
0035                QStringLiteral("information_schema.REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME"),
0036                QStringLiteral("information_schema.KEY_COLUMN_USAGE.CONSTRAINT_NAME"));
0037     qb.addColumn(QStringLiteral("information_schema.REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME"));
0038     qb.addColumn(QStringLiteral("information_schema.KEY_COLUMN_USAGE.COLUMN_NAME"));
0039     qb.addColumn(QStringLiteral("information_schema.KEY_COLUMN_USAGE.REFERENCED_TABLE_NAME"));
0040     qb.addColumn(QStringLiteral("information_schema.KEY_COLUMN_USAGE.REFERENCED_COLUMN_NAME"));
0041     qb.addColumn(QStringLiteral("information_schema.REFERENTIAL_CONSTRAINTS.UPDATE_RULE"));
0042     qb.addColumn(QStringLiteral("information_schema.REFERENTIAL_CONSTRAINTS.DELETE_RULE"));
0043 
0044     qb.addValueCondition(QStringLiteral("information_schema.KEY_COLUMN_USAGE.TABLE_SCHEMA"), Query::Equals, m_database.databaseName());
0045     qb.addValueCondition(QStringLiteral("information_schema.KEY_COLUMN_USAGE.TABLE_NAME"), Query::Equals, tableName);
0046 
0047     if (!qb.exec()) {
0048         throw DbException(qb.query());
0049     }
0050 
0051     QList<ForeignKey> result;
0052     while (qb.query().next()) {
0053         ForeignKey fk;
0054         fk.name = qb.query().value(0).toString();
0055         fk.column = qb.query().value(1).toString();
0056         fk.refTable = qb.query().value(2).toString();
0057         fk.refColumn = qb.query().value(3).toString();
0058         fk.onUpdate = qb.query().value(4).toString();
0059         fk.onDelete = qb.query().value(5).toString();
0060         result.push_back(fk);
0061     }
0062     qb.query().finish();
0063 
0064     return result;
0065 }
0066 
0067 QString DbIntrospectorMySql::getAutoIncrementValueQuery(const QString &tableName, const QString &)
0068 {
0069     return QStringLiteral("SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_NAME = '%1'").arg(tableName);
0070 }
0071 
0072 QString DbIntrospectorMySql::updateAutoIncrementValueQuery(const QString &tableName, const QString &, qint64 value)
0073 {
0074     return QStringLiteral("ALTER TABLE %1 AUTO_INCREMENT = %2").arg(tableName).arg(value);
0075 }
0076 
0077 // END MySql
0078 
0079 // BEGIN Sqlite
0080 
0081 DbIntrospectorSqlite::DbIntrospectorSqlite(const QSqlDatabase &database)
0082     : DbIntrospector(database)
0083 {
0084 }
0085 
0086 QList<DbIntrospector::ForeignKey> DbIntrospectorSqlite::foreignKeyConstraints(const QString &tableName)
0087 {
0088     QSqlQuery query(m_database);
0089     if (!query.exec(QStringLiteral("PRAGMA foreign_key_list(%1)").arg(tableName))) {
0090         throw DbException(query);
0091     }
0092 
0093     QList<ForeignKey> result;
0094     while (query.next()) {
0095         ForeignKey fk;
0096         fk.column = query.value(3).toString();
0097         fk.refTable = query.value(2).toString();
0098         fk.refColumn = query.value(4).toString();
0099         fk.onUpdate = query.value(5).toString();
0100         fk.onDelete = query.value(6).toString();
0101         fk.name = tableName + fk.column + QLatin1Char('_') + fk.refTable + fk.refColumn + QStringLiteral("_fk");
0102         result.push_back(fk);
0103     }
0104 
0105     return result;
0106 }
0107 
0108 QString DbIntrospectorSqlite::hasIndexQuery(const QString &tableName, const QString &indexName)
0109 {
0110     return QStringLiteral("SELECT * FROM sqlite_master WHERE type='index' AND tbl_name='%1' AND name='%2';").arg(tableName, indexName);
0111 }
0112 
0113 QString DbIntrospectorSqlite::getAutoIncrementValueQuery(const QString &tableName, const QString &)
0114 {
0115     return QStringLiteral("SELECT seq FROM sqlite_sequence WHERE name = '%1'").arg(tableName);
0116 }
0117 
0118 QString DbIntrospectorSqlite::updateAutoIncrementValueQuery(const QString &tableName, const QString &, qint64 value)
0119 {
0120     return QStringLiteral("UPDATE sqlite_sequence SET seq = %1 WHERE name = '%2'").arg(value).arg(tableName);
0121 }
0122 
0123 // END Sqlite
0124 
0125 // BEGIN PostgreSql
0126 
0127 DbIntrospectorPostgreSql::DbIntrospectorPostgreSql(const QSqlDatabase &database)
0128     : DbIntrospector(database)
0129 {
0130 }
0131 
0132 QList<DbIntrospector::ForeignKey> DbIntrospectorPostgreSql::foreignKeyConstraints(const QString &tableName)
0133 {
0134 #define TABLE_CONSTRAINTS "information_schema.table_constraints"
0135 #define KEY_COLUMN_USAGE "information_schema.key_column_usage"
0136 #define REFERENTIAL_CONSTRAINTS "information_schema.referential_constraints"
0137 #define CONSTRAINT_COLUMN_USAGE "information_schema.constraint_column_usage"
0138 
0139     Query::Condition keyColumnUsageCondition(Query::And);
0140     keyColumnUsageCondition.addColumnCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_catalog"),
0141                                                Query::Equals,
0142                                                QStringLiteral(KEY_COLUMN_USAGE ".constraint_catalog"));
0143     keyColumnUsageCondition.addColumnCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_schema"),
0144                                                Query::Equals,
0145                                                QStringLiteral(KEY_COLUMN_USAGE ".constraint_schema"));
0146     keyColumnUsageCondition.addColumnCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_name"),
0147                                                Query::Equals,
0148                                                QStringLiteral(KEY_COLUMN_USAGE ".constraint_name"));
0149 
0150     Query::Condition referentialConstraintsCondition(Query::And);
0151     referentialConstraintsCondition.addColumnCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_catalog"),
0152                                                        Query::Equals,
0153                                                        QStringLiteral(REFERENTIAL_CONSTRAINTS ".constraint_catalog"));
0154     referentialConstraintsCondition.addColumnCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_schema"),
0155                                                        Query::Equals,
0156                                                        QStringLiteral(REFERENTIAL_CONSTRAINTS ".constraint_schema"));
0157     referentialConstraintsCondition.addColumnCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_name"),
0158                                                        Query::Equals,
0159                                                        QStringLiteral(REFERENTIAL_CONSTRAINTS ".constraint_name"));
0160 
0161     Query::Condition constraintColumnUsageCondition(Query::And);
0162     constraintColumnUsageCondition.addColumnCondition(QStringLiteral(REFERENTIAL_CONSTRAINTS ".unique_constraint_catalog"),
0163                                                       Query::Equals,
0164                                                       QStringLiteral(CONSTRAINT_COLUMN_USAGE ".constraint_catalog"));
0165     constraintColumnUsageCondition.addColumnCondition(QStringLiteral(REFERENTIAL_CONSTRAINTS ".unique_constraint_schema"),
0166                                                       Query::Equals,
0167                                                       QStringLiteral(CONSTRAINT_COLUMN_USAGE ".constraint_schema"));
0168     constraintColumnUsageCondition.addColumnCondition(QStringLiteral(REFERENTIAL_CONSTRAINTS ".unique_constraint_name"),
0169                                                       Query::Equals,
0170                                                       QStringLiteral(CONSTRAINT_COLUMN_USAGE ".constraint_name"));
0171 
0172     auto store = DataStore::dataStoreForDatabase(m_database);
0173     QueryBuilder qb(store, QStringLiteral(TABLE_CONSTRAINTS), QueryBuilder::Select);
0174     qb.addColumn(QStringLiteral(TABLE_CONSTRAINTS ".constraint_name"));
0175     qb.addColumn(QStringLiteral(KEY_COLUMN_USAGE ".column_name"));
0176     qb.addColumn(QStringLiteral(CONSTRAINT_COLUMN_USAGE ".table_name AS referenced_table"));
0177     qb.addColumn(QStringLiteral(CONSTRAINT_COLUMN_USAGE ".column_name AS referenced_column"));
0178     qb.addColumn(QStringLiteral(REFERENTIAL_CONSTRAINTS ".update_rule"));
0179     qb.addColumn(QStringLiteral(REFERENTIAL_CONSTRAINTS ".delete_rule"));
0180     qb.addJoin(QueryBuilder::LeftJoin, QStringLiteral(KEY_COLUMN_USAGE), keyColumnUsageCondition);
0181     qb.addJoin(QueryBuilder::LeftJoin, QStringLiteral(REFERENTIAL_CONSTRAINTS), referentialConstraintsCondition);
0182     qb.addJoin(QueryBuilder::LeftJoin, QStringLiteral(CONSTRAINT_COLUMN_USAGE), constraintColumnUsageCondition);
0183     qb.addValueCondition(QStringLiteral(TABLE_CONSTRAINTS ".constraint_type"), Query::Equals, QLatin1StringView("FOREIGN KEY"));
0184     qb.addValueCondition(QStringLiteral(TABLE_CONSTRAINTS ".table_name"), Query::Equals, tableName.toLower());
0185 
0186 #undef TABLE_CONSTRAINTS
0187 #undef KEY_COLUMN_USAGE
0188 #undef REFERENTIAL_CONSTRAINTS
0189 #undef CONSTRAINT_COLUMN_USAGE
0190 
0191     if (!qb.exec()) {
0192         throw DbException(qb.query());
0193     }
0194 
0195     QList<ForeignKey> result;
0196     while (qb.query().next()) {
0197         ForeignKey fk;
0198         fk.name = qb.query().value(0).toString();
0199         fk.column = qb.query().value(1).toString();
0200         fk.refTable = qb.query().value(2).toString();
0201         fk.refColumn = qb.query().value(3).toString();
0202         fk.onUpdate = qb.query().value(4).toString();
0203         fk.onDelete = qb.query().value(5).toString();
0204         result.push_back(fk);
0205     }
0206     qb.query().finish();
0207 
0208     return result;
0209 }
0210 
0211 QString DbIntrospectorPostgreSql::hasIndexQuery(const QString &tableName, const QString &indexName)
0212 {
0213     QString query = QStringLiteral("SELECT indexname FROM pg_catalog.pg_indexes");
0214     query += QStringLiteral(" WHERE tablename ilike '%1'").arg(tableName);
0215     query += QStringLiteral(" AND  indexname ilike '%1'").arg(indexName);
0216     query += QStringLiteral(" UNION SELECT conname FROM pg_catalog.pg_constraint ");
0217     query += QStringLiteral(" WHERE conname ilike '%1'").arg(indexName);
0218     return query;
0219 }
0220 
0221 QString DbIntrospectorPostgreSql::getAutoIncrementValueQuery(const QString &tableName, const QString &idColumn)
0222 {
0223     return QStringLiteral("SELECT nextval(pg_get_serial_sequence('%1', '%2'))").arg(tableName, idColumn);
0224 }
0225 
0226 QString DbIntrospectorPostgreSql::updateAutoIncrementValueQuery(const QString &tableName, const QString &idColumn, qint64 value)
0227 {
0228     // Can't use ALTER SEQUENCE, because it doesn't support expressions (like pg_get_serial_sequence())
0229     return QStringLiteral("SELECT setval(pg_get_serial_sequence('%1', '%2'), %3) FROM %1").arg(tableName, idColumn).arg(value);
0230 }
0231 
0232 // END PostgreSql