Warning, /libraries/futuresql/README.md is written in an unsupported language. File is not indexed.
0001 <!--
0002 SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im
0003
0004 SPDX-License-Identifier: BSD-2-Clause
0005 -->
0006
0007 # FutureSQL
0008
0009 A non-blocking database framework for Qt.
0010
0011 FutureSQL was in part inspired by Diesel, and provides a higher level of abstraction than QtSql.
0012 Its features include non-blocking database access by default, relatively boilderplate-free queries,
0013 automatic database migrations and simple mapping to objects.
0014
0015 In order to make FutureSQL's use of templates less confusing, FutureSQL uses C++20 concepts,
0016 and requires a C++20 compiler.
0017
0018 Warning: The API is not finalized yet.
0019
0020 ## Usage
0021
0022 The following example demonstrates the usage of FutureSQL in conjunction with QCoro:
0023 ```cpp
0024 // Qt
0025 #include <QCoreApplication>
0026 #include <QTimer>
0027
0028 // QCoro
0029 #include <QCoro/QCoroTask>
0030 #include <QCoro/QCoroFuture>
0031
0032 // FutureSQL
0033 #include <ThreadedDatabase>
0034
0035 // STL
0036 #include <tuple>
0037
0038
0039 // A data structure that represents data from the "test" table
0040 struct HelloWorld {
0041 // Types that the database columns can be converted to. The types must be convertible from QVariant.
0042 using ColumnTypes = std::tuple<int, QString>;
0043
0044 // This function gets a row from the database as a tuple, and puts it into the HelloWorld structs.
0045 // If the ColumnTypes already match the types and order of the attributes in the struct, you don't need to implement it.
0046 //
0047 // Try to comment it out, the example should still compile and work.
0048 static HelloWorld fromSql(ColumnTypes &&tuple) {
0049 auto [id, data] = tuple;
0050 return HelloWorld { id, data };
0051 }
0052
0053 // attributes
0054 int id;
0055 QString data;
0056 };
0057
0058 QCoro::Task<> databaseExample() {
0059 // This object contains the database configuration,
0060 // in this case just the path to the SQLite file, and the database type (SQLite).
0061 DatabaseConfiguration config;
0062 config.setDatabaseName("database.sqlite");
0063 config.setType(DatabaseType::SQLite);
0064
0065 // Here we open the database file, and get a handle to the database.
0066 auto database = ThreadedDatabase::establishConnection(config);
0067
0068 // Execute some queries.
0069 co_await database->execute("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT)");
0070
0071 // Query parameters are bound by position in the query. The execute function is variadic and you can add as many parameters as you need.
0072 co_await database->execute("INSERT INTO test (data) VALUES (?)", QStringLiteral("Hello World"));
0073
0074 // Retrieve some data from the database.
0075 // The data is directly returned as our HelloWorld struct.
0076 auto results = co_await database->getResults<HelloWorld>("SELECT * FROM test");
0077
0078 // Print out the data in the result list
0079 for (const auto &result : results) {
0080 qDebug() << result.id << result.data;
0081 }
0082
0083 // Quit the event loop as we are done
0084 QCoreApplication::instance()->quit();
0085 }
0086
0087 // Just a minimal main function for QCoro, to start the Qt event loop.
0088 int main(int argc, char *argv[]) {
0089 QCoreApplication app(argc, argv);
0090 QTimer::singleShot(0, databaseExample);
0091 return app.exec();
0092 }
0093 ```
0094
0095 In code were coroutines don't make sense, you can use `QCoro::connect` of QCoro > v0.8.0,
0096 or `QFuture<T>::then()` on Qt6, to run a callback once a database query finished.
0097
0098 ## Migrations
0099
0100 FutureSQL can manage database migrations in a way that is mostly compatible with diesel.
0101 You just need to pass it a directory (preferably in QRC) that contains migrations in the following format:
0102
0103 ```
0104 ├── 2022-05-20-194850_init
0105 │ └── up.sql
0106 └── 2022-05-25-212054_playlists
0107 └── up.sql
0108 ```
0109
0110 Naming the migration directories after dates is a good practice, as the migrations are run in sorted order.
0111 Naming them for example by a counting number would break once the numbers get to large.
0112 For example, if you don't use leading zeros and only one digit, you'd only have up to 10 migrations.
0113
0114 The migration directory structure can be generated by the `diesel` command line tool. You can install it using cargo as follows:
0115 ```bash
0116 cargo install diesel_cli --features sqlite --no-default-features
0117 ```
0118
0119 Finally, you can run the migrations from your C++ code:
0120 ```cpp
0121 database->runMigrations(":/migrations/");
0122 ```