-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathtransaction.cpp
143 lines (118 loc) · 5.59 KB
/
transaction.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <ozo/connection_info.h>
#include <ozo/execute.h>
#include <ozo/transaction.h>
#include <ozo/shortcuts.h>
#include <ozo/request.h>
#include <ozo/pg/types/integer.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <iostream>
// This is an example of using the transaction mechanism is supplied by the library.
// It is based on https://www.postgresqltutorial.com/postgresql-transaction/.
namespace asio = boost::asio;
const auto throw_if_error = [](ozo::error_code ec, const auto& conn) {
if (ec) {
std::ostringstream s;
if (!ozo::is_null_recursive(conn)) {
s << "libpq error message: \"" << ozo::error_message(conn)
<< "\", error context: \"" << ozo::get_error_context(conn) << "\"";
}
throw ozo::system_error(ec, s.str());
}
};
int main(int argc, char **argv) {
std::cout << "OZO request example" << std::endl;
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <connection string>\n";
return 1;
}
using namespace ozo::literals;
using namespace std::chrono_literals;
// Ozo perform all IO using Boost.Asio, so first thing we need to do is setup asio::io_context
asio::io_context io;
// To make a request we need to make a ConnectionSource. It knows how to connect to database using
// connection string. See https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING
// how to make a connection string.
auto conn_info = ozo::connection_info(argv[1]);
// All IO is asynchronous, therefore we have a choice here, what should be our CompletionToken.
// We use Boost.Coroutines to write asynchronous code in synchronouse style. Coroutine will be
// called after io.run() is called.
asio::spawn(io, [&] (asio::yield_context yield) {
ozo::error_code ec;
// Prepare table and initial data for the example outside of transaction.
// Assume that autocommit feature of PostrgreSQL is on by default
auto conn = ozo::execute(conn_info[io], "DROP TABLE IF EXISTS accounts;"_SQL, yield);
// Let’s create a new table named accounts for the demonstration:
conn = ozo::execute(std::move(conn),
"CREATE TABLE accounts (\
id INT8 GENERATED BY DEFAULT AS IDENTITY,\
name TEXT NOT NULL,\
balance INT8 NOT NULL,\
PRIMARY KEY(id)\
);"_SQL, yield);
// When you execute the following INSERT statement PostgreSQL inserted a new row into the accounts table immediately.
// In this case, you did not know when the transaction began and had no chance to intercept the change such as undoing it.
conn = ozo::execute(std::move(conn), "INSERT INTO accounts(name,balance) VALUES('Bob',10000);"_SQL, yield);
//! [Beginning a transaction]
// To start a transaction, you use the following statement:
auto transaction = ozo::begin(std::move(conn), yield[ec]);
//! [Beginning a transaction]
throw_if_error(ec, transaction);
// The following statements insert a new account into the accounts table:
transaction = ozo::execute(std::move(transaction), "INSERT INTO accounts(name,balance) VALUES('Alice',10000);"_SQL, yield[ec]);
throw_if_error(ec, transaction);
using rows_type = ozo::rows_of<ozo::pg::int8, ozo::pg::text, ozo::pg::int8>;
const auto select_all = "SELECT id, name, balance FROM accounts"_SQL;
rows_type result;
const auto print_result = [](const rows_type& rows) {
std::cout << "id\tname\tbalance\n";
boost::for_each(rows, [](auto& x) {
boost::hana::for_each(x, [&](auto& v) {
std::cout << v << '\t';
});
std::cout << '\n';
});
};
// From the current session, you can see the change by querying the accounts table
transaction = ozo::request(std::move(transaction), select_all, 1s, ozo::into(result), yield[ec]);
throw_if_error(ec, transaction);
print_result(result);
// Should print:
// id name balance
// 1 Bob 10000
// 2 Alice 10000
result.clear();
// However, in a new session with conn2 the query above should not observe the change
auto conn2 = ozo::request(conn_info[io], select_all, 1s, ozo::into(result), yield[ec]);
throw_if_error(ec, conn2);
print_result(result);
// Should print:
// id name balance
// 1 Bob 10000
//! [Committing a transaction]
// To make the change become visible to other sessions (or users) you need to commit the transaction
// by using the following statement
conn = ozo::commit(std::move(transaction), yield[ec]);
//! [Committing a transaction]
throw_if_error(ec, conn);
// From any session, you can view this change by querying the accounts table
result.clear();
conn2 = ozo::request(std::move(conn2), select_all, 1s, ozo::into(result), yield[ec]);
throw_if_error(ec, conn2);
print_result(result);
// Should print:
// id name balance
// 1 Bob 10000
// 2 Alice 10000
result.clear();
conn = ozo::request(std::move(conn), select_all, 1s, ozo::into(result), yield[ec]);
throw_if_error(ec, conn2);
print_result(result);
// Should print:
// id name balance
// 1 Bob 10000
// 2 Alice 10000
});
io.run();
return 0;
}