1
+ [/
2
+ Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
3
+
4
+ Distributed under the Boost Software License, Version 1.0. (See accompanying
5
+ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
+ ]
7
+
8
+ [section:tutorial_updates_transactions Tutorial 5: UPDATEs, transactions and semicolon-separated queries]
9
+
10
+ All the previous tutorials have only used `SELECT` statements, but
11
+ Boost.MySQL is not limited to them. Using [refmemunq any_connection async_execute]
12
+ you can run any SQL statement supported by MySQL.
13
+
14
+ In this tutorial, we will write a program that changes the first name of an
15
+ employee, given their ID, and prints the updated employee details.
16
+ We will use an `UPDATE` and transaction management statements.
17
+ `INSERT` and `DELETE` statements have similar mechanics.
18
+
19
+
20
+
21
+ [heading A simple UPDATE]
22
+
23
+ We can use the same tools and functions as in previous tutorials:
24
+
25
+ [tutorial_updates_transactions_update]
26
+
27
+ By default, auto-commit is enabled, meaning that when `async_execute`
28
+ returns, the `UPDATE` is visible to other client connections.
29
+
30
+
31
+
32
+
33
+ [heading Checking that the UPDATE took effect]
34
+
35
+ The above query will succeed even if there was no employee with the given ID.
36
+ We would like to retrieve the updated employee details on success, and emit
37
+ a useful error message if no employee was matched.
38
+
39
+ We may be tempted to use [refmem results affected_rows] at first, but
40
+ this doesn't convey the information we're looking for:
41
+ a row may be matched but not affected. For example, if you try to
42
+ set `first_name` to the same value it already has,
43
+ MySQL will count the row as a matched, but not affected.
44
+
45
+
46
+ MySQL does not support the `UPDATE ... RETURNING` syntax, so we will
47
+ have to retrieve the employee manually after updating it.
48
+ We can add the following after our `UPDATE`:
49
+
50
+ [tutorial_updates_transactions_select]
51
+
52
+ However, the code above contains a race condition. Imagine the following situation:
53
+
54
+ * The `UPDATE` is issued. No employee is matched.
55
+ * Before our program sends the `SELECT` query, a different program inserts
56
+ an employee with the ID that we're trying to update.
57
+ * Our program runs the `SELECT` statement and retrieves the newly inserted row.
58
+
59
+ To our program, it looks like we succeeded performing the update, when
60
+ we really didn't. Depending on the nature of our program, this may
61
+ or may not have serious consequences, but it's something we should avoid.
62
+
63
+
64
+ [heading Avoiding the race condition with a transaction block]
65
+
66
+ We can fix the race condition using transactions.
67
+ In MySQL, a transaction block is opened with `START TRANSACTION`.
68
+ Subsequent statements will belong to the transaction block,
69
+ until the transaction either commits or is rolled back.
70
+ A `COMMIT` statement commits the transaction.
71
+ A rollback happens if the connection that initiated the transaction
72
+ closes or an explicit `ROLLBACK` statement is used.
73
+
74
+ We will enclose our `UPDATE` and `SELECT` statements in
75
+ a transaction block. This will ensure that the `SELECT`
76
+ will get the updated row, if any:
77
+
78
+ [tutorial_updates_transactions_txn]
79
+
80
+
81
+
82
+
83
+ [heading Using multi-queries]
84
+
85
+ While the code we've written is correct, it's not very performant.
86
+ We're incurring in 4 round-trips to the server, when our queries don't depend
87
+ on the result of previous ones. The round-trips occur within a transaction
88
+ block, which causes certain database rows to be locked, increasing contention.
89
+ We can improve the situation by running our four statements in a single batch.
90
+
91
+ Multi-queries are a protocol feature that lets you execute several queries
92
+ at once. Individual queries must be separated by semicolons.
93
+
94
+ Multi-queries are disabled by default. To enable them, set
95
+ [refmem connect_params multi_queries] to `true` before connecting:
96
+
97
+ [tutorial_updates_transactions_connect]
98
+
99
+ Multi-queries can be composed an executed using the same
100
+ functions we've been using:
101
+
102
+ [tutorial_updates_transactions_multi_queries]
103
+
104
+ Accessing the results is slightly different. MySQL returns 4 resultsets,
105
+ one for each query. In Boost.MySQL, this operation is said to be
106
+ [link mysql.multi_resultset multi-resultset].
107
+ [reflink results] can actually store more than one resultset.
108
+ [refmem results rows] actually accesses the rows in the first resultset,
109
+ because it's the most common use case.
110
+
111
+ We want to get the rows retrieved by the `SELECT` statement,
112
+ which corresponds to the third resultset.
113
+ [refmem results at] returns a [reflink resultset_view] containing data
114
+ for the requested resultset:
115
+
116
+ [tutorial_updates_transactions_dynamic_results]
117
+
118
+
119
+ [heading Using manual indices in with_params]
120
+
121
+ Repeating `employee_id` in the parameter list passed to `with_params`
122
+ violates the DRY principle.
123
+ As with `std::format`, we can refer to a format argument more than once
124
+ by using manual indices:
125
+
126
+ [tutorial_updates_transactions_manual_indices]
127
+
128
+
129
+
130
+ [heading Using the static interface with multi-resultset]
131
+
132
+ Finally, we can rewrite our code to use the static interface so it's safer.
133
+ In multi-resultset scenarios, we can pass as many row types
134
+ to [reflink static_results] as resultsets we expect.
135
+ We can use the empty tuple (`std::tuple<>`) as a row type
136
+ for operations that don't return rows, like the `UPDATE`.
137
+ Our code becomes:
138
+
139
+ [tutorial_updates_transactions_static]
140
+
141
+
142
+ [heading Wrapping up]
143
+
144
+ Full program listing for this tutorial is [link mysql.examples.tutorial_updates_transactions here].
145
+
146
+ You can now proceed to [link mysql.tutorial_connection_pool the next tutorial].
147
+
148
+
149
+ [endsect]
0 commit comments