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

0001 /***************************************************************************
0002  *   SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>            *
0003  *                                                                         *
0004  *   SPDX-License-Identifier: LGPL-2.0-or-later                            *
0005  ***************************************************************************/
0006 
0007 #pragma once
0008 
0009 #include "dbintrospector.h"
0010 #include "schematypes.h"
0011 
0012 #include <QHash>
0013 #include <QSharedPointer>
0014 #include <QSqlDatabase>
0015 #include <QStringList>
0016 
0017 class DbInitializerTest;
0018 
0019 namespace Akonadi
0020 {
0021 namespace Server
0022 {
0023 class Schema;
0024 class DbUpdater;
0025 
0026 class TestInterface
0027 {
0028 public:
0029     virtual ~TestInterface() = default;
0030 
0031     virtual void execStatement(const QString &statement) = 0;
0032 
0033 protected:
0034     explicit TestInterface() = default;
0035 
0036 private:
0037     Q_DISABLE_COPY_MOVE(TestInterface)
0038 };
0039 
0040 /**
0041  * A helper class which takes a reference to a database object and
0042  * the file name of a template file and initializes the database
0043  * according to the rules in the template file.
0044  *
0045  * TODO: Refactor this to be easily reusable for updater too
0046  */
0047 class DbInitializer
0048 {
0049     friend class DbUpdater;
0050 
0051 public:
0052     using Ptr = QSharedPointer<DbInitializer>;
0053 
0054     /**
0055       Returns an initializer instance for a given backend.
0056     */
0057     static DbInitializer::Ptr createInstance(const QSqlDatabase &database, Schema *schema = nullptr);
0058 
0059     /**
0060      * Destroys the database initializer.
0061      */
0062     virtual ~DbInitializer();
0063 
0064     /**
0065      * Starts the initialization process.
0066      * On success true is returned, false otherwise.
0067      *
0068      * If something went wrong @see errorMsg() can be used to retrieve more
0069      * information.
0070      */
0071     bool run();
0072 
0073     /**
0074      * Returns the textual description of an occurred error.
0075      */
0076     QString errorMsg() const;
0077 
0078     /**
0079      * Checks and creates missing indexes.
0080      *
0081      * This method is run after DbUpdater to ensure that data in new columns
0082      * are populated and creation of indexes and foreign keys does not fail.
0083      */
0084     bool updateIndexesAndConstraints();
0085 
0086     /**
0087      * Returns a backend-specific CREATE TABLE SQL query describing given table
0088      */
0089     virtual QString buildCreateTableStatement(const TableDescription &tableDescription) const = 0;
0090 
0091 protected:
0092     /**
0093      * Creates a new database initializer.
0094      *
0095      * @param database The reference to the database.
0096      */
0097     DbInitializer(const QSqlDatabase &database);
0098 
0099     /**
0100      * Overwrite in backend-specific sub-classes to return the SQL type for a given C++ type.
0101      * @param type Name of the C++ type.
0102      * @param size Optional size hint for the column, if -1 use the default SQL type for @p type.
0103      */
0104     virtual QString sqlType(const ColumnDescription &col, int size) const;
0105     /** Overwrite in backend-specific sub-classes to return the SQL value for a given C++ value. */
0106     virtual QString sqlValue(const ColumnDescription &col, const QString &value) const;
0107 
0108     virtual QString buildColumnStatement(const ColumnDescription &columnDescription, const TableDescription &tableDescription) const = 0;
0109     virtual QString buildAddColumnStatement(const TableDescription &tableDescription, const ColumnDescription &columnDescription) const;
0110     virtual QString buildCreateIndexStatement(const TableDescription &tableDescription, const IndexDescription &indexDescription) const;
0111     virtual QString buildInsertValuesStatement(const TableDescription &tableDescription, const DataDescription &dataDescription) const = 0;
0112 
0113     /**
0114      * Returns an SQL statements to add a foreign key constraint to an existing column @p column.
0115      * The default implementation returns an empty string, so any backend supporting foreign key constraints
0116      * must reimplement this.
0117      */
0118     virtual QStringList buildAddForeignKeyConstraintStatements(const TableDescription &table, const ColumnDescription &column) const;
0119 
0120     /**
0121      * Returns an SQL statements to remove the foreign key constraint @p fk from table @p table.
0122      * The default implementation returns an empty string, so any backend supporting foreign key constraints
0123      * must reimplement this.
0124      */
0125     virtual QStringList buildRemoveForeignKeyConstraintStatements(const DbIntrospector::ForeignKey &fk, const TableDescription &table) const;
0126 
0127     static QString buildReferentialAction(ColumnDescription::ReferentialAction onUpdate, ColumnDescription::ReferentialAction onDelete);
0128     /// Use for multi-column primary keys during table creation
0129     static QString buildPrimaryKeyStatement(const TableDescription &table);
0130 
0131 private:
0132     friend class ::DbInitializerTest;
0133     Q_DISABLE_COPY_MOVE(DbInitializer)
0134 
0135     /**
0136      * Sets the debug @p interface that shall be used on unit test run.
0137      */
0138     void setTestInterface(TestInterface *interface);
0139 
0140     /**
0141      * Sets a different DbIntrospector. This allows unit tests to simulate certain
0142      * states of the database.
0143      */
0144     void setIntrospector(const DbIntrospector::Ptr &introspector);
0145 
0146     /** Helper method for executing a query.
0147      * If a debug interface is set for testing, that gets the queries instead.
0148      * @throws DbException if something went wrong.
0149      */
0150     void execQuery(const QString &queryString);
0151 
0152     bool checkTable(const TableDescription &tableDescription);
0153     /**
0154      * Checks foreign key constraints on table @p tableDescription and fixes them if necessary.
0155      */
0156     void checkForeignKeys(const TableDescription &tableDescription);
0157     void checkIndexes(const TableDescription &tableDescription);
0158     bool checkRelation(const RelationDescription &relationDescription);
0159 
0160     static QString referentialActionToString(ColumnDescription::ReferentialAction action);
0161 
0162     void execPendingQueries(const QStringList &queries);
0163 
0164     QSqlDatabase mDatabase;
0165     Schema *mSchema = nullptr;
0166     QString mErrorMsg;
0167     TestInterface *mTestInterface = nullptr;
0168     DbIntrospector::Ptr m_introspector;
0169     QStringList m_pendingIndexes;
0170     QStringList m_pendingForeignKeys;
0171     QStringList m_removedForeignKeys;
0172 };
0173 
0174 } // namespace Server
0175 } // namespace Akonadi