File indexing completed on 2024-12-08 12:44:36
0001 // SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert <jbb@kaidan.im> 0002 // 0003 // SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0004 0005 #pragma once 0006 0007 class QUrl; 0008 class QSqlDatabase; 0009 0010 #include <QString> 0011 #include <QObject> 0012 #include <QFuture> 0013 #include <QSharedDataPointer> 0014 0015 #include <memory> 0016 #include <optional> 0017 #include <tuple> 0018 #include <vector> 0019 0020 #include "threadeddatabase_p.h" 0021 0022 #include <futuresql_export.h> 0023 0024 struct DatabaseConfigurationPrivate; 0025 0026 /// 0027 /// Selection of database types. 0028 /// If the required one is not included, use the DatabaseConfiguration::setType QString overload instead. 0029 /// 0030 enum DatabaseType { 0031 SQLite 0032 }; 0033 0034 /// 0035 /// Options for connecting to a database 0036 /// 0037 class FUTURESQL_EXPORT DatabaseConfiguration { 0038 public: 0039 DatabaseConfiguration(); 0040 DatabaseConfiguration(const DatabaseConfiguration &); 0041 ~DatabaseConfiguration(); 0042 0043 /// Set the name of the database driver. If it is included in DatabaseType, use the enum overload instead 0044 void setType(const QString &type); 0045 /// Set the type of database. If DatabaseType doesn't include the one you need, use the QString overload instead 0046 void setType(DatabaseType type); 0047 /// Get the name of the database driver 0048 const QString &type() const; 0049 0050 /// Set the hostname 0051 void setHostName(const QString &hostName); 0052 const std::optional<QString> &hostName() const; 0053 0054 /// Set the name of the database (path of the file for SQLite) 0055 void setDatabaseName(const QString &databaseName); 0056 const std::optional<QString> &databaseName() const; 0057 0058 /// Set user name 0059 void setUserName(const QString &userName); 0060 const std::optional<QString> &userName() const; 0061 0062 /// Set password 0063 void setPassword(const QString &password); 0064 const std::optional<QString> &password() const; 0065 0066 private: 0067 QSharedDataPointer<DatabaseConfigurationPrivate> d; 0068 }; 0069 0070 template <typename T> 0071 concept FromSql = requires(T v, typename T::ColumnTypes row) 0072 { 0073 typename T::ColumnTypes; 0074 { std::tuple(row) } -> std::same_as<typename T::ColumnTypes>; 0075 }; 0076 0077 namespace detail { 0078 0079 template <typename ...Args> 0080 constexpr bool isQVariantConvertible = std::conjunction_v<std::is_convertible<Args, QVariant>...>; 0081 0082 } 0083 0084 struct ThreadedDatabasePrivate; 0085 0086 /// 0087 /// A database connection that lives on a new thread 0088 /// 0089 class FUTURESQL_EXPORT ThreadedDatabase : public QThread { 0090 public: 0091 /// 0092 /// \brief Connect to a database 0093 /// \param config Configuration of the database connection 0094 /// \return 0095 /// 0096 static std::unique_ptr<ThreadedDatabase> establishConnection(const DatabaseConfiguration &config); 0097 0098 /// 0099 /// \brief Execute an SQL query on the database, ignoring the result. 0100 /// \param sqlQuery SQL query string to execute 0101 /// \param args Parameters to bind to the placeholders in the SQL Query 0102 /// \return 0103 /// 0104 template <typename ...Args> 0105 requires detail::isQVariantConvertible<Args...> 0106 auto execute(const QString &sqlQuery, Args... args) -> QFuture<void> { 0107 return db().execute(sqlQuery, args...); 0108 } 0109 0110 /// 0111 /// Run the database migrations in the given directory. 0112 /// The directory needs to contain a subdirectory for each migration. 0113 /// The subdirectories need to be named so that when sorted alphabetically the migrations will be run in the correct order. 0114 /// Each subdirectory needs to contain a file named up.sql. 0115 /// 0116 /// \param migrationDirectory Directory which contains the migrations. 0117 /// \return a future that finishes when the database changes are finished 0118 /// 0119 auto runMigrations(const QString &migrationDirectory) -> QFuture<void>; 0120 0121 /// 0122 /// Declare that the database is currently at the state of the migration in the migration subdirectory 0123 /// migrationName. 0124 /// 0125 /// The automatic migrations will then start with all migrations that are newer than migrationName. 0126 /// 0127 /// @warning This function should only be used for the initial switch from a different migration system, for example a custom made one. 0128 /// \param migrationName 0129 /// \return a future that finishes when the database changes are finished 0130 /// 0131 auto setCurrentMigrationLevel(const QString &migrationName) -> QFuture<void>; 0132 0133 /// 0134 /// \brief Execute an SQL query on the database, retrieving the result. 0135 /// \param sqlQuery SQL Query to execute 0136 /// \param args Parameters to bind to the placeholders in the SQL query. 0137 /// \return Future of a list of lists of variants. 0138 /// 0139 /// T must provide a tuple of the column types as `using ColumnTypes = std::tuple<...>` 0140 /// and a, if the column types are not the same types in the same order as the attributes of the struct, 0141 /// a `static T fromSql(ColumnTypes tuple)` deserialization method. 0142 /// 0143 template <typename T, typename ...Args> 0144 requires FromSql<T> && detail::isQVariantConvertible<Args...> 0145 auto getResults(const QString &sqlQuery, Args... args) -> QFuture<std::vector<T>> { 0146 return db().getResults<T, Args...>(sqlQuery, args...); 0147 } 0148 0149 /// 0150 /// \brief Like getResults, but for retrieving just one row. 0151 /// \param sqlQuery SQL Query to execute 0152 /// \param args Parameters to bind to the placeholders in the SQL query. 0153 /// 0154 template <typename T, typename ...Args> 0155 requires FromSql<T> && detail::isQVariantConvertible<Args...> 0156 auto getResult(const QString &sqlQuery, Args... args) -> QFuture<std::optional<T>> { 0157 return db().getResult<T, Args...>(sqlQuery, args...); 0158 } 0159 0160 /// 0161 /// \brief Run a custom function on the database thread. The function is passed the internal QSqlDatabase. 0162 /// \param func A function that takes a QSqlDatabase 0163 /// \return The result of the function, wrapped in a QFuture 0164 /// 0165 template <typename Func> 0166 requires std::is_invocable_v<Func, const QSqlDatabase &> 0167 auto runOnThread(Func &&func) -> QFuture<std::invoke_result_t<Func, const QSqlDatabase &>> { 0168 return db().runOnThread(std::move(func)); 0169 } 0170 0171 ~ThreadedDatabase(); 0172 0173 private: 0174 ThreadedDatabase(); 0175 0176 asyncdatabase_private::AsyncSqlDatabase &db(); 0177 0178 std::unique_ptr<ThreadedDatabasePrivate> d; 0179 }; 0180 0181 /// 0182 /// \brief Deserialize just a single value from a query result. 0183 /// 0184 template <typename T> 0185 struct SingleValue { 0186 using ColumnTypes = std::tuple<T>; 0187 0188 static SingleValue fromSql(ColumnTypes tuple) { 0189 auto [value] = tuple; 0190 return SingleValue { value }; 0191 } 0192 0193 operator const T &() const { 0194 return value; 0195 } 0196 0197 T value; 0198 };