// SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert // // SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL #pragma once class QUrl; class QSqlDatabase; #include #include #include #include #include #include #include #include #include "threadeddatabase_p.h" #include struct DatabaseConfigurationPrivate; /// /// Selection of database types. /// If the required one is not included, use the DatabaseConfiguration::setType QString overload instead. /// enum DatabaseType { SQLite }; /// /// Options for connecting to a database /// class FUTURESQL_EXPORT DatabaseConfiguration { public: DatabaseConfiguration(); DatabaseConfiguration(const DatabaseConfiguration &); ~DatabaseConfiguration(); /// Set the name of the database driver. If it is included in DatabaseType, use the enum overload instead void setType(const QString &type); /// Set the type of database. If DatabaseType doesn't include the one you need, use the QString overload instead void setType(DatabaseType type); /// Get the name of the database driver const QString &type() const; /// Set the hostname void setHostName(const QString &hostName); const std::optional &hostName() const; /// Set the name of the database (path of the file for SQLite) void setDatabaseName(const QString &databaseName); const std::optional &databaseName() const; /// Set user name void setUserName(const QString &userName); const std::optional &userName() const; /// Set password void setPassword(const QString &password); const std::optional &password() const; private: QSharedDataPointer d; }; template concept FromSql = requires(T v, typename T::ColumnTypes row) { typename T::ColumnTypes; { std::tuple(row) } -> std::same_as; }; namespace detail { template constexpr bool isQVariantConvertible = std::conjunction_v...>; } struct ThreadedDatabasePrivate; /// /// A database connection that lives on a new thread /// class FUTURESQL_EXPORT ThreadedDatabase : public QThread { public: /// /// \brief Connect to a database /// \param config Configuration of the database connection /// \return /// static std::unique_ptr establishConnection(const DatabaseConfiguration &config); /// /// \brief Execute an SQL query on the database, ignoring the result. /// \param sqlQuery SQL query string to execute /// \param args Parameters to bind to the placeholders in the SQL Query /// \return /// template requires detail::isQVariantConvertible auto execute(const QString &sqlQuery, Args... args) -> QFuture { return db().execute(sqlQuery, args...); } /// /// Run the database migrations in the given directory. /// The directory needs to contain a subdirectory for each migration. /// The subdirectories need to be named so that when sorted alphabetically the migrations will be run in the correct order. /// Each subdirectory needs to contain a file named up.sql. /// /// \param migrationDirectory Directory which contains the migrations. /// \return a future that finishes when the database changes are finished /// auto runMigrations(const QString &migrationDirectory) -> QFuture; /// /// Declare that the database is currently at the state of the migration in the migration subdirectory /// migrationName. /// /// The automatic migrations will then start with all migrations that are newer than migrationName. /// /// @warning This function should only be used for the initial switch from a different migration system, for example a custom made one. /// \param migrationName /// \return a future that finishes when the database changes are finished /// auto setCurrentMigrationLevel(const QString &migrationName) -> QFuture; /// /// \brief Execute an SQL query on the database, retrieving the result. /// \param sqlQuery SQL Query to execute /// \param args Parameters to bind to the placeholders in the SQL query. /// \return Future of a list of lists of variants. /// /// T must provide a tuple of the column types as `using ColumnTypes = std::tuple<...>` /// and a, if the column types are not the same types in the same order as the attributes of the struct, /// a `static T fromSql(ColumnTypes tuple)` deserialization method. /// template requires FromSql && detail::isQVariantConvertible auto getResults(const QString &sqlQuery, Args... args) -> QFuture> { return db().getResults(sqlQuery, args...); } /// /// \brief Like getResults, but for retrieving just one row. /// \param sqlQuery SQL Query to execute /// \param args Parameters to bind to the placeholders in the SQL query. /// template requires FromSql && detail::isQVariantConvertible auto getResult(const QString &sqlQuery, Args... args) -> QFuture> { return db().getResult(sqlQuery, args...); } /// /// \brief Run a custom function on the database thread. The function is passed the internal QSqlDatabase. /// \param func A function that takes a QSqlDatabase /// \return The result of the function, wrapped in a QFuture /// template requires std::is_invocable_v auto runOnThread(Func &&func) -> QFuture> { return db().runOnThread(std::move(func)); } ~ThreadedDatabase(); private: ThreadedDatabase(); asyncdatabase_private::AsyncSqlDatabase &db(); std::unique_ptr d; }; /// /// \brief Deserialize just a single value from a query result. /// template struct SingleValue { using ColumnTypes = std::tuple; static SingleValue fromSql(ColumnTypes tuple) { auto [value] = tuple; return SingleValue { value }; } operator const T &() const { return value; } T value; };