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 ```