81{
82 std::cout << "=== transaction_management example ===" << std::endl;
83
84
85 auto context = std::make_shared<database_context>();
86 auto db_manager = std::make_shared<database_manager>(context);
87 db_manager->set_mode(database_types::postgres);
88
89 std::string connection_string
90 = "host=localhost port=5432 dbname=example_db user=user password=password";
91
92 auto connect_result = db_manager->connect_result(connection_string);
93 if (!connect_result.is_ok())
94 {
95 std::cerr << "Connection failed. Ensure PostgreSQL is running." << std::endl;
96 return 1;
97 }
98 std::cout << "Connected" << std::endl;
99
100
101 db_manager->create_query_result(R"(
102 CREATE TABLE IF NOT EXISTS accounts (
103 id SERIAL PRIMARY KEY,
104 name VARCHAR(100) NOT NULL,
105 balance DECIMAL(12,2) NOT NULL DEFAULT 0.00
106 )
107 )");
108 db_manager->execute_query_result("DELETE FROM accounts");
109 db_manager->execute_query_result(
110 "INSERT INTO accounts (name, balance) VALUES ('Alice', 1000.00)");
111 db_manager->execute_query_result(
112 "INSERT INTO accounts (name, balance) VALUES ('Bob', 500.00)");
113
114
115
116
117 std::cout << "\n--- Scenario 1: Successful transfer ---" << std::endl;
118 {
119 auto begin_result = db_manager->begin_transaction();
120 if (!begin_result.is_ok())
121 {
122 std::cerr << "Failed to begin transaction" << std::endl;
123 return 1;
124 }
125 std::cout << "Transaction started, in_transaction = "
126 << std::boolalpha << db_manager->in_transaction() << std::endl;
127
128
129 db_manager->execute_query_result(
130 "UPDATE accounts SET balance = balance - 200 WHERE name = 'Alice'");
131 db_manager->execute_query_result(
132 "UPDATE accounts SET balance = balance + 200 WHERE name = 'Bob'");
133
134 auto commit_result = db_manager->commit_transaction();
135 if (commit_result.is_ok())
136 {
137 std::cout << "Transaction committed" << std::endl;
138 }
139
140
141 auto result = db_manager->select_query_result(
142 "SELECT name, balance FROM accounts ORDER BY name");
143 if (result.is_ok())
144 {
145 for (const auto& row : result.value())
146 {
147 for (const auto& [col, val] : row)
148 {
149 std::cout << " " << col << " = ";
150 std::visit([](const auto& v) { std::cout << v; }, val);
151 std::cout << " ";
152 }
153 std::cout << std::endl;
154 }
155 }
156 }
157
158
159
160
161 std::cout << "\n--- Scenario 2: Rollback on error ---" << std::endl;
162 {
163 auto begin_result = db_manager->begin_transaction();
164 if (!begin_result.is_ok())
165 {
166 std::cerr << "Failed to begin transaction" << std::endl;
167 return 1;
168 }
169
170
171 db_manager->execute_query_result(
172 "UPDATE accounts SET balance = balance - 5000 WHERE name = 'Alice'");
173
174
175 auto check = db_manager->select_query_result(
176 "SELECT balance FROM accounts WHERE name = 'Alice'");
177
178 bool should_rollback = false;
179 if (check.is_ok() && !check.value().empty())
180 {
181 const auto& balance_val = check.value()[0].at("balance");
182
183 std::visit(
184 [&should_rollback](const auto& v)
185 {
186 using T = std::decay_t<decltype(v)>;
187 if constexpr (std::is_same_v<T, double>)
188 {
189 should_rollback = (v < 0.0);
190 }
191 else if constexpr (std::is_same_v<T, std::string>)
192 {
193
194 should_rollback = (!v.empty() && v[0] == '-');
195 }
196 },
197 balance_val);
198 }
199
200 if (should_rollback)
201 {
202 std::cout << "Business rule violation detected, rolling back" << std::endl;
203 db_manager->rollback_transaction();
204 std::cout << "Transaction rolled back, in_transaction = "
205 << std::boolalpha << db_manager->in_transaction() << std::endl;
206 }
207 else
208 {
209 db_manager->commit_transaction();
210 std::cout << "Transaction committed (balance was not negative)" << std::endl;
211 }
212
213
214 auto result = db_manager->select_query_result(
215 "SELECT name, balance FROM accounts ORDER BY name");
216 if (result.is_ok())
217 {
218 std::cout << "Balances after rollback:" << std::endl;
219 for (const auto& row : result.value())
220 {
221 for (const auto& [col, val] : row)
222 {
223 std::cout << " " << col << " = ";
224 std::visit([](const auto& v) { std::cout << v; }, val);
225 std::cout << " ";
226 }
227 std::cout << std::endl;
228 }
229 }
230 }
231
232
233
234
235 std::cout << "\n--- Scenario 3: RAII transaction guard ---" << std::endl;
236 {
237 try
238 {
240 std::cout << "Guard started transaction" << std::endl;
241
242 db_manager->execute_query_result(
243 "INSERT INTO accounts (name, balance) VALUES ('Carol', 750.00)");
244
245
246 guard.commit();
247 std::cout << "Guard committed transaction" << std::endl;
248 }
249 catch (const std::exception& e)
250 {
251 std::cerr << "Transaction error: " << e.what() << std::endl;
252 }
253
254
255 auto result = db_manager->select_query_result(
256 "SELECT name, balance FROM accounts ORDER BY name");
257 if (result.is_ok())
258 {
259 std::cout << "Accounts after guard commit:" << std::endl;
260 for (const auto& row : result.value())
261 {
262 for (const auto& [col, val] : row)
263 {
264 std::cout << " " << col << " = ";
265 std::visit([](const auto& v) { std::cout << v; }, val);
266 std::cout << " ";
267 }
268 std::cout << std::endl;
269 }
270 }
271 }
272
273
274 db_manager->disconnect_result();
275 std::cout << "\nDisconnected" << std::endl;
276
277 std::cout << "=== transaction_management example completed ===" << std::endl;
278 return 0;
279}
RAII transaction guard that rolls back on destruction unless committed.