Skip to content

Commit 2003f02

Browse files
authored
Merge pull request #20 from jgaa/transactions
Transactions, persistent handle and Beta 2
2 parents 5bc7f92 + 74a949e commit 2003f02

File tree

8 files changed

+729
-147
lines changed

8 files changed

+729
-147
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ jobs:
2525

2626
steps:
2727
- name: Checkout code
28-
uses: actions/checkout@v2
28+
uses: actions/checkout@v4
2929
with:
3030
submodules: true
3131

3232
- name: Cache
33-
uses: actions/cache@v3
33+
uses: actions/cache@v4
3434
with:
3535
path: |
3636
~/vcpkg

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.24)
22

33
if (NOT DEFINED MYSQLPOOL_VERSION)
4-
set(MYSQLPOOL_VERSION 0.3.0)
4+
set(MYSQLPOOL_VERSION 0.4.0)
55
endif()
66

77
project(mysqlpool-cpp
@@ -90,6 +90,10 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
9090

9191
set(MYSQLPOOL_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
9292

93+
if(WIN32)
94+
add_compile_options(-D_WIN32_WINNT=0x0601 -DWINVER=0x0601 -DWIN32_LEAN_AND_MEAN=1)
95+
endif()
96+
9397
message(STATUS "Using ${CMAKE_CXX_COMPILER}")
9498

9599
if (MYSQLPOOL_WITH_CONAN)

README.md

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The library require C++20 and use C++20 coroutines.
88

99
## Status
1010

11-
First BETA release.
11+
Second BETA release.
1212

1313
Please create an issue if you find any problems or if you have suggestions
1414
on how to make the library more useful.
@@ -77,6 +77,8 @@ need. However, you will have to deal with the error handling yourself.
7777
- Requests are queued if all the available connections are in use. The pool will open more connections in the background up to the limit.
7878
- The high level `exec` method handles connection allocation, per request options, error handling and reporting and data binding.
7979
- Prepared Statements are handled automatically. Each connection has a cache of prepared statements, based on a cryptographic hash from the SQL query. If a new query is seen, a prepared statement is created and added to the cache before the query is executed.
80+
- Sequential execution of queries by using the same connection.
81+
- Transactions with automatic rollback if the transaction is not committed.
8082
- All SQL queries can be logged, include the arguments to prepared statements.
8183
- Time Zone can be specified for a query. The pool will then ensure that the connection
8284
used for that request use the specified time zone. Useful for servers that handle
@@ -365,6 +367,83 @@ boost::asio::awaitable<void> add_and_query_data(mp::Mysqlpool& pool) {
365367

366368
```
367369
370+
You can execute a series of queries using the same database connection.
371+
372+
```C++
373+
374+
boost::asio::awaitable<void> use_the_same_connection(mp::Mysqlpool& pool) {
375+
// Some times you may want to use the same connection for several queries
376+
// This can be done by using the same handle for several queries.
377+
378+
// Create the handle
379+
auto handle = co_await pool.getConnection();
380+
381+
// Make some queries
382+
// Note that we use `handle` to execute the queries.
383+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 7, "Ivan");
384+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 8, "Jane");
385+
386+
// Using the same connection lets you set specific options on that connection,
387+
// and it guarantees that the queries are executed in sequential order.
388+
}
389+
390+
```
391+
392+
Often you'll need transactions to ensure consistency. Either all queries in the
393+
transaction are successful, or all of them fail. A transaction also allows you
394+
to roll back the changes if something else in your application fails.
395+
396+
A transaction is handled by a transaction object. Normally you will create the
397+
transaction object, do some queries and then commit the transaction. If you
398+
don't commit a transaction by the time the transaction object goes out of scope,
399+
the transaction is rolled back.
400+
401+
```C++
402+
403+
boost::asio::awaitable<void> use_transaction(mp::Mysqlpool& pool) {
404+
405+
// Very often, you will need transactions in order to commit
406+
// several related queries to the database.
407+
// This is one area where the traditional SQL database servers
408+
// shine compared to most NoSQL databases.
409+
410+
// In order to use transactions, we need to use a Handle to a Connection
411+
// and keep that handle instance alive until the transaction is done.
412+
// In most cases that simply means to first create the handle, and
413+
// then the transaction.
414+
415+
// Create the handle
416+
auto handle = co_await pool.getConnection();
417+
418+
// Create the transaction
419+
auto trx = co_await handle.transaction();
420+
421+
// Make some queries - typically INSERT, UPDATE or DELETE
422+
// Note that you must use the `handle` instance to execute the queries,
423+
// since the transaction is bound to that handle.
424+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 5, "George");
425+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 6, "Hannah");
426+
427+
// Now you can commit the transaction or roll it back.
428+
// If the trx instance goes out of scope with an active transaction,
429+
// the transaction is rolled back.
430+
co_await trx.commit();
431+
432+
// You cannot use the trx instance after commit or rollback, but the handle is
433+
// still valid. You can use it directly with `exec(...)` or you can create
434+
// a new transaction.
435+
436+
co_return;
437+
}
438+
439+
```
440+
441+
## TLS
442+
443+
If you enable TLS using the `DbConfig.tls_mode` option, it should work without
444+
any further tweaks in most situations. If you use a DB-server on an insecure
445+
network, you may harden the TLS settings using the [DbConfig.tls](include/mysqlpool/conf.h) settings.
446+
368447
## Building
369448
370449
You can build the library using CMake.
@@ -395,4 +474,3 @@ add_subdirectory(dependencies/mysqlpool-cpp)
395474
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysqlpool-cpp/include)
396475
397476
```
398-

examples/simple/fun_with_sql.cpp

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,62 @@ boost::asio::awaitable<void> add_and_query_data(mp::Mysqlpool& pool) {
162162
res = co_await pool.exec("SELECT id, name, DATE_FORMAT(created_date, '%Y-%m-%d %H:%m') FROM test_table", opts);
163163
print(res.rows());
164164

165-
co_await pool.exec("DROP TABLE test_table");
166165
co_return;
167166
}
168167

168+
boost::asio::awaitable<void> use_transaction(mp::Mysqlpool& pool) {
169+
170+
// Very often, you will need transactions in order to commit
171+
// several related queries to the database.
172+
// This is one area where the traditional SQL database servers
173+
// shine compared to most NoSQL databases.
174+
175+
// In order to use transactions, we need to use a Handle to a Connection
176+
// and keep that handle instance alive until the transaction is done.
177+
// In most cases that simply means to first create the handle, and
178+
// then the transaction.
179+
180+
// Create the handle
181+
auto handle = co_await pool.getConnection();
182+
183+
// Create the transaction
184+
auto trx = co_await handle.transaction();
185+
186+
// Make some queries - typically INSERT, UPDATE or DELETE
187+
// Note that you must use the `handle` instance to execute the queries,
188+
// since the transaction is bound to that handle.
189+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 5, "George");
190+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 6, "Hannah");
191+
192+
// Now you can commit the transaction or roll it back.
193+
// If the trx instance goes out of scope with an active transaction,
194+
// the transaction is rolled back.
195+
co_await trx.commit();
196+
197+
// You cannot use the trx instance after commit or rollback, but the handle is
198+
// still valid. You can use it directly with `exec(...)` or you can create
199+
// a new transaction.
200+
201+
co_return;
202+
}
203+
204+
boost::asio::awaitable<void> use_the_same_connection(mp::Mysqlpool& pool) {
205+
// Some times you may want to use the same connection for several queries
206+
// This can be done by using the same handle for several queries.
207+
208+
// Create the handle
209+
auto handle = co_await pool.getConnection();
210+
211+
// Make some queries
212+
// Note that we use `handle` to execute the queries.
213+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 7, "Ivan");
214+
co_await handle.exec("INSERT INTO test_table (id, name) VALUES (?, ?)", 8, "Jane");
215+
216+
// Using the same connection lets you set specific options on that connection,
217+
// and it guarantees that the queries are executed in sequential order.
218+
}
219+
220+
169221
// Entry point from main()
170222
void run_examples(const mp::DbConfig& config){
171223

@@ -190,7 +242,10 @@ void run_examples(const mp::DbConfig& config){
190242
co_await get_db_version_using_boost_mysql(pool);
191243
co_await get_db_version(pool);
192244
co_await add_and_query_data(pool);
245+
co_await use_transaction(pool);
246+
co_await use_the_same_connection(pool);
193247

248+
co_await pool.exec("DROP TABLE test_table");
194249
// Gracefully shut down the connection-pool.
195250
co_await pool.close();
196251
} catch (const exception& ex) {

include/mysqlpool/conf.h

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
#include <string>
44
#include <cstdint>
5+
#include <vector>
6+
#include <string_view>
7+
#include <functional>
8+
#include <boost/asio/ssl.hpp>
59

610
#include "mysqlpool/config.h"
711

@@ -11,6 +15,57 @@ std::string getEnv(const std::string& name, std::string defaultValue = {});
1115

1216
/** Configuration for an instance of mysqlpool. */
1317

18+
struct TlsConfig {
19+
using password_cb_t = std::function<std::string (
20+
std::size_t, // The maximum size for a password.
21+
boost::asio::ssl::context_base::password_purpose // Whether password is for reading or writing.
22+
)>;
23+
24+
/*! TLS version to use.
25+
*
26+
* If unset, it use the default client settings for boost.asio (boost::asio::ssl::context_base::tls_client)
27+
*
28+
* One of:
29+
* - "" (empty string): Use the default settings for the client
30+
* - "": Use TLS 1.2
31+
* - "tls_1.3": Use TLS 1.3
32+
*/
33+
std::string version;
34+
35+
/*! Allow SSL versions. They are all depricated and should not be allowed */
36+
bool allow_ssl = false;
37+
38+
/*! Allow TLS 1.1 */
39+
bool allow_tls_1_1 = false;
40+
41+
/*! Allow TLS 1.2 */
42+
bool allow_tls_1_2 = true;
43+
44+
/*! Allow TLS 1.3 */
45+
bool allow_tls_1_3 = true;
46+
47+
/*! Very often the peer will use a self-signed certificate.
48+
*
49+
* Must be enabled if you use a public database server on the internet.
50+
*/
51+
bool verify_peer = false;
52+
53+
/*! CA files for verification */
54+
std::vector<std::string> ca_files;
55+
56+
/*! CA paths for verification */
57+
std::vector<std::string> ca_paths;
58+
59+
/*! Cert to use for the client */
60+
std::string cert_file;
61+
62+
/*! Key to use for the client */
63+
std::string key_file;
64+
65+
/*! Password callback if the private key use a password */
66+
password_cb_t password_callback;
67+
};
68+
1469
struct DbConfig {
1570
/// Host where the DB server is running.
1671
std::string host = getEnv(MYSQLPOOL_DBHOST, DEFAULT_MYSQLPOOL_HOST);
@@ -44,7 +99,10 @@ struct DbConfig {
4499
* - enable: Use TLS if the server supports it, fall back to non-encrypted connection if it does not.
45100
* - require: Always use TLS; abort the connection if the server does not support it.
46101
*/
47-
std::string ssl_mode = getEnv(MYSQLPOOL_DB_TLS_MODE, DEFAULT_MYSQLPOOL_TLS_MODE);
102+
std::string tls_mode = getEnv(MYSQLPOOL_DB_TLS_MODE, DEFAULT_MYSQLPOOL_TLS_MODE);
103+
104+
/*! TLS configuration */
105+
TlsConfig tls;
48106

49107
/*! Number of times to retry connecting to the database
50108
*

0 commit comments

Comments
 (0)