Futuresql

threadeddatabase.h
1 // SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert <[email protected]>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
4 
5 #pragma once
6 
7 class QUrl;
8 class QSqlDatabase;
9 
10 #include <QString>
11 #include <QObject>
12 #include <QFuture>
13 #include <QSharedDataPointer>
14 
15 #include <memory>
16 #include <optional>
17 #include <tuple>
18 #include <vector>
19 
20 #include "threadeddatabase_p.h"
21 
22 #include <futuresql_export.h>
23 
24 struct DatabaseConfigurationPrivate;
25 
26 ///
27 /// Selection of database types.
28 /// If the required one is not included, use the DatabaseConfiguration::setType QString overload instead.
29 ///
30 enum DatabaseType {
31  SQLite
32 };
33 
34 ///
35 /// Options for connecting to a database
36 ///
37 class FUTURESQL_EXPORT DatabaseConfiguration {
38 public:
42 
43  /// Set the name of the database driver. If it is included in DatabaseType, use the enum overload instead
44  void setType(const QString &type);
45  /// Set the type of database. If DatabaseType doesn't include the one you need, use the QString overload instead
46  void setType(DatabaseType type);
47  /// Get the name of the database driver
48  const QString &type() const;
49 
50  /// Set the hostname
51  void setHostName(const QString &hostName);
52  const std::optional<QString> &hostName() const;
53 
54  /// Set the name of the database (path of the file for SQLite)
55  void setDatabaseName(const QString &databaseName);
56  const std::optional<QString> &databaseName() const;
57 
58  /// Set user name
59  void setUserName(const QString &userName);
60  const std::optional<QString> &userName() const;
61 
62  /// Set password
63  void setPassword(const QString &password);
64  const std::optional<QString> &password() const;
65 
66 private:
68 };
69 
70 template <typename T>
71 concept FromSql = requires(T v, typename T::ColumnTypes row)
72 {
73  typename T::ColumnTypes;
74  { std::tuple(row) } -> std::same_as<typename T::ColumnTypes>;
75 };
76 
77 namespace detail {
78 
79 template <typename ...Args>
80 constexpr bool isQVariantConvertible = std::conjunction_v<std::is_convertible<Args, QVariant>...>;
81 
82 }
83 
84 struct ThreadedDatabasePrivate;
85 
86 ///
87 /// A database connection that lives on a new thread
88 ///
89 class FUTURESQL_EXPORT ThreadedDatabase : public QThread {
90 public:
91  ///
92  /// \brief Connect to a database
93  /// \param config Configuration of the database connection
94  /// \return
95  ///
96  static std::unique_ptr<ThreadedDatabase> establishConnection(const DatabaseConfiguration &config);
97 
98  ///
99  /// \brief Execute an SQL query on the database, ignoring the result.
100  /// \param sqlQuery SQL query string to execute
101  /// \param args Parameters to bind to the placeholders in the SQL Query
102  /// \return
103  ///
104  template <typename ...Args>
105  requires detail::isQVariantConvertible<Args...>
106  auto execute(const QString &sqlQuery, Args... args) -> QFuture<void> {
107  return db().execute(sqlQuery, args...);
108  }
109 
110  ///
111  /// Run the database migrations in the given directory.
112  /// The directory needs to contain a subdirectory for each migration.
113  /// The subdirectories need to be named so that when sorted alphabetically the migrations will be run in the correct order.
114  /// Each subdirectory needs to contain a file named up.sql.
115  ///
116  /// \param migrationDirectory Directory which contains the migrations.
117  /// \return a future that finishes when the database changes are finished
118  ///
119  auto runMigrations(const QString &migrationDirectory) -> QFuture<void>;
120 
121  ///
122  /// Declare that the database is currently at the state of the migration in the migration subdirectory
123  /// migrationName.
124  ///
125  /// The automatic migrations will then start with all migrations that are newer than migrationName.
126  ///
127  /// @warning This function should only be used for the initial switch from a different migration system, for example a custom made one.
128  /// \param migrationName
129  /// \return a future that finishes when the database changes are finished
130  ///
131  auto setCurrentMigrationLevel(const QString &migrationName) -> QFuture<void>;
132 
133  ///
134  /// \brief Execute an SQL query on the database, retrieving the result.
135  /// \param sqlQuery SQL Query to execute
136  /// \param args Parameters to bind to the placeholders in the SQL query.
137  /// \return Future of a list of lists of variants.
138  ///
139  /// T must provide a tuple of the column types as `using ColumnTypes = std::tuple<...>`
140  /// and a, if the column types are not the same types in the same order as the attributes of the struct,
141  /// a `static T fromSql(ColumnTypes tuple)` deserialization method.
142  ///
143  template <typename T, typename ...Args>
144  requires FromSql<T> && detail::isQVariantConvertible<Args...>
145  auto getResults(const QString &sqlQuery, Args... args) -> QFuture<std::vector<T>> {
146  return db().getResults<T, Args...>(sqlQuery, args...);
147  }
148 
149  ///
150  /// \brief Like getResults, but for retrieving just one row.
151  /// \param sqlQuery SQL Query to execute
152  /// \param args Parameters to bind to the placeholders in the SQL query.
153  ///
154  template <typename T, typename ...Args>
155  requires FromSql<T> && detail::isQVariantConvertible<Args...>
156  auto getResult(const QString &sqlQuery, Args... args) -> QFuture<std::optional<T>> {
157  return db().getResult<T, Args...>(sqlQuery, args...);
158  }
159 
160  ///
161  /// \brief Run a custom function on the database thread. The function is passed the internal QSqlDatabase.
162  /// \param func A function that takes a QSqlDatabase
163  /// \return The result of the function, wrapped in a QFuture
164  ///
165  template <typename Func>
166  requires std::is_invocable_v<Func, const QSqlDatabase &>
168  return db().runOnThread(std::move(func));
169  }
170 
171  ~ThreadedDatabase();
172 
173 private:
175 
176  asyncdatabase_private::AsyncSqlDatabase &db();
177 
178  std::unique_ptr<ThreadedDatabasePrivate> d;
179 };
180 
181 ///
182 /// \brief Deserialize just a single value from a query result.
183 ///
184 template <typename T>
185 struct SingleValue {
186  using ColumnTypes = std::tuple<T>;
187 
188  static SingleValue fromSql(ColumnTypes tuple) {
189  auto [value] = tuple;
190  return SingleValue { value };
191  }
192 
193  operator const T &() const {
194  return value;
195  }
196 
197  T value;
198 };
requires FromSql< T > &&detail::isQVariantConvertible< Args... > auto getResult(const QString &sqlQuery, Args... args) -> QFuture< std::optional< T >>
Like getResults, but for retrieving just one row.
Options for connecting to a database.
requires detail::isQVariantConvertible< Args... > auto execute(const QString &sqlQuery, Args... args) -> QFuture< void >
Execute an SQL query on the database, ignoring the result.
Deserialize just a single value from a query result.
KSharedConfigPtr config()
A database connection that lives on a new thread.
requires std::is_invocable_v< Func, const QSqlDatabase & > auto runOnThread(Func &&func) -> QFuture< std::invoke_result_t< Func, const QSqlDatabase & >>
Run a custom function on the database thread.
requires FromSql< T > &&detail::isQVariantConvertible< Args... > auto getResults(const QString &sqlQuery, Args... args) -> QFuture< std::vector< T >>
Execute an SQL query on the database, retrieving the result.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Oct 1 2023 03:47:36 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.