Mstdlib-1.24.0
m_sql_trans.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2017 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_SQL_TRANS_H__
25#define __M_SQL_TRANS_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/base/m_defs.h>
30#include <mstdlib/base/m_types.h>
31#include <mstdlib/sql/m_sql.h>
32#include <mstdlib/sql/m_sql_stmt.h>
33
34/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
35
36__BEGIN_DECLS
37
38
39
40/*! \addtogroup m_sql_trans SQL Transaction Handling
41 * \ingroup m_sql
42 *
43 * SQL Transaction Handling
44 *
45 * @{
46 */
47
48struct M_sql_trans;
49/*! Object holding the state for an active transaction */
50typedef struct M_sql_trans M_sql_trans_t;
51
52/*! Transaction isolation levels */
53typedef enum {
54 M_SQL_ISOLATION_UNKNOWN = 0, /*!< Unknown, used for error conditions, never set */
55 M_SQL_ISOLATION_READUNCOMMITTED = 1, /*!< Read Uncommitted */
56 M_SQL_ISOLATION_READCOMMITTED = 2, /*!< Read Committed */
57 M_SQL_ISOLATION_REPEATABLEREAD = 3, /*!< Repeatable Read */
58 M_SQL_ISOLATION_SNAPSHOT = 4, /*!< Snapshot */
59 M_SQL_ISOLATION_SERIALIZABLE = 5 /*!< Serializable */
61
62
63/*! Begin a new SQL transaction at the requested isolation level.
64 *
65 * Beginning a new transaction will reserve an SQL connection from the pool
66 * until either a rollback or commit is performed. Callers in most cases
67 * should not start more than one SQL transaction per thread as it could lead
68 * to deadlocks waiting on a connection to become available if insufficient
69 * connections are available in the pool.
70 *
71 * In order to clean up the returned transaction handle, a caller must call
72 * either M_sql_trans_commit() or M_sql_trans_rollback() as appropriate.
73 *
74 * \note It is recommended to use the M_sql_trans_process() helper rather than calling
75 * M_sql_trans_begin(), M_sql_trans_rollback() or M_sql_trans_commit() yourself.
76 *
77 * \param[out] trans Returns initialized transaction handle to be used for queries.
78 * \param[in] pool Initialized #M_sql_connpool_t object
79 * \param[in] isolation Requested isolation level. The database may choose the closest match
80 * if the isolation level requested is not supported.
81 * \param[out] error User-supplied buffer to hold error message.
82 * \param[in] error_size Size of User-supplied buffer.
83 * \return #M_SQL_ERROR_SUCCESS on success, or one of the #M_sql_error_t results on failure.
84 */
85M_API M_sql_error_t M_sql_trans_begin(M_sql_trans_t **trans, M_sql_connpool_t *pool, M_sql_isolation_t isolation, char *error, size_t error_size);
86
87/*! Rollback an SQL transaction.
88 *
89 * This function should be called if the caller needs to cancel the transaction, or must be
90 * called to clean up the #M_sql_trans_t handle when an unrecoverable error has occurred such
91 * as a server disconnect or deadlock.
92 *
93 * The passed in trans handle will be destroyed regardless if this function returns success
94 * or fail.
95 *
96 * \note It is recommended to use the M_sql_trans_process() helper rather than calling
97 * M_sql_trans_begin(), M_sql_trans_rollback() or M_sql_trans_commit() yourself.
98 *
99 * \param[in] trans Initialized transaction handle that will be used to rollback the
100 * pending transaction, and will be will be destroyed automatically
101 * upon return of this function.
102 * \return #M_SQL_ERROR_SUCCESS on success, or one of the #M_sql_error_t results on failure.
103 */
105
106
107/*! Commit a pending SQL transaction.
108 *
109 * Any statements executed against the transaction handle will not be applied to the
110 * database until this command is called.
111 *
112 * The associated transaction handle will be automatically destroyed regardless if
113 * this function returns success or fail. If a failure occurs, the caller must assume
114 * the transaction was NOT applied (e.g. rolled back).
115 *
116 * \note It is recommended to use the M_sql_trans_process() helper rather than calling
117 * M_sql_trans_begin(), M_sql_trans_rollback() or M_sql_trans_commit() yourself.
118 *
119 * \param[in] trans Initialized transaction handle that will be used to commit the
120 * pending transaction, and will be will be destroyed automatically
121 * upon return of this function.
122 * \param[out] error User-supplied buffer to hold error message.
123 * \param[in] error_size Size of User-supplied buffer.
124 * \return #M_SQL_ERROR_SUCCESS on success, or one of the #M_sql_error_t results on failure.
125 */
126M_API M_sql_error_t M_sql_trans_commit(M_sql_trans_t *trans, char *error, size_t error_size);
127
128
129/*! Execute a query against the database that is part of an open transaction. This
130 * request will not automatically commit and must be manually committed via M_sql_trans_commit().
131 *
132 * Must call M_sql_stmt_prepare() or M_sql_stmt_prepare_buf() prior to execution.
133 * Must also bind any parameters using \link m_sql_stmt_bind M_sql_stmt_bind_*() \endlink series of functions.
134 *
135 * This function will NOT destroy the passed in #M_sql_trans_t object, it is kept open
136 * so additional statements can be executed within the same transaction. If NOT using
137 * the M_sql_trans_process() helper, it is the caller's responsibility to call
138 * M_sql_trans_commit() or M_sql_trans_rollback() as appropriate.
139 *
140 * \param[in] trans Initialized #M_sql_trans_t object.
141 * \param[in] stmt Initialized and prepared #M_sql_stmt_t object
142 * \return #M_SQL_ERROR_SUCCESS on success, or one of the #M_sql_error_t values on failure.
143 */
145
146
147/*! Function prototype called by M_sql_trans_process().
148 *
149 * Inside the function created, the integrator should perform each step of the SQL
150 * transaction, and if an error occurs, return the appropriate error condition, whether
151 * it is an error condition as returned by M_sql_trans_execute(), which should be passed
152 * through unmodified, or an internally generated error condition if internal logic fails.
153 * For user-logic generated errors, special error conditions of #M_SQL_ERROR_USER_SUCCESS,
154 * #M_SQL_ERROR_USER_RETRY and #M_SQL_ERROR_USER_FAILURE exist to more accurately identify
155 * the condition rather than attempting to map to the generic SQL subsystem condtions.
156 *
157 * \note The function should expect to be called potentially multiple times, so state tracking
158 * must be reset on entry to this user-specified function. If a rollback or connectivity
159 * failure condition is met, it will automatically be called again.
160 *
161 * \warning This function should NEVER call M_sql_trans_commit() or M_sql_trans_rollback() as that
162 * is handled internally by the helper M_sql_trans_process().
163 *
164 * \param[in] trans Pointer to initialized transaction object to use to execute the transaction.
165 * \param[in] arg User-specified argument used for storing metadata about the flow/process.
166 * \param[out] error User-supplied error buffer to output error message.
167 * \param[in] error_size Size of user-supplied error buffer.
168 * \return #M_SQL_ERROR_SUCCESS or #M_SQL_ERROR_USER_SUCCESS on successful completion, or one of the #M_sql_error_t error conditions.
169 */
170typedef M_sql_error_t (*M_sql_trans_commands_t)(M_sql_trans_t *trans, void *arg, char *error, size_t error_size);
171
172
173/*! Helper function for processing a sequence of SQL commands as a single atomic operation, while
174 * automatically handling things like rollback and connectivity failure situations.
175 *
176 * \warning The user-supplied function being called should expect to be called, potentially, multiple
177 * times when errors occur. State MUST NOT be maintained from call to call or risk having
178 * inconstent data.
179 *
180 * Usage Example:
181 * \code{.c}
182 * typedef struct {
183 * M_int64 id;
184 * M_int64 inc;
185 * M_int64 result;
186 * } my_counter_metadata_t;
187 *
188 * // Table: CREATE TABLE counters (id INTEGER, val INTEGER, PRIMARY KEY(id))
189 * // Increment requested id by requested amount
190 * static M_sql_error_t my_counter_inc(M_sql_trans_t *trans, void *arg, char *error, size_t error_size)
191 * {
192 * my_counter_metadata_t *data = arg;
193 * M_sql_stmt_t *stmt;
194 * M_sql_error_t err;
195 * M_int64 curr_val = 0;
196 * M_buf_t *query;
197 *
198 * M_mem_set(error, 0, error_size);
199 *
200 * // Retrieve current value for id - don't forget to use update locks!
201 * stmt = M_sql_stmt_create();
202 * query = M_buf_create();
203 * M_buf_add_str(query, "SELECT \"val\" FROM \"counters\"");
204 * M_sql_query_append_updlock(M_sql_trans_get_pool(trans), query, M_SQL_QUERY_UPDLOCK_TABLE);
205 * M_buf_add_str(query, " WHERE \"id\" = ?");
206 * M_sql_query_append_updlock(M_sql_trans_get_pool(trans), query, M_SQL_QUERY_UPDLOCK_QUERYEND);
207 * M_sql_stmt_prepare_buf(stmt, query);
208 * M_sql_stmt_bind_int64(stmt, data->id);
209 * err = M_sql_trans_execute(trans, stmt);
210 * if (err != M_SQL_ERROR_SUCCESS)
211 * goto done;
212 *
213 * if (M_sql_stmt_result_int64(stmt, 0, 0, &curr_val) != M_SQL_ERROR_SUCCESS) {
214 * M_snprintf(error, error_size, "id %lld not found", data->id);
215 * err = M_SQL_ERROR_QUERY_FAILED;
216 * goto done;
217 * }
218 * M_sql_stmt_destroy(stmt);
219 *
220 * // Increment the value for the id
221 * data->result = curr_val + data->inc;
222 * stmt = M_sql_stmt_create();
223 * M_sql_stmt_prepare(stmt, "UPDATE \"counters\" SET \"val\" = ? WHERE \"id\" = ?");
224 * M_sql_stmt_bind_int64(stmt, data->result);
225 * M_sql_stmt_bind_int64(stmt, data->id);
226 * err = M_sql_trans_execute(trans, stmt);
227 * if (err != M_SQL_ERROR_SUCCESS)
228 * goto done;
229 *
230 * done:
231 * if (err != M_SQL_ERROR_SUCCESS && M_str_isempty(error)) {
232 * M_snprintf(error, error_size, "%s", M_sql_stmt_get_error_string(stmt));
233 * }
234 * M_sql_stmt_destroy(stmt);
235 *
236 * return err;
237 * }
238 *
239 * static void run_txn(M_sql_connpool_t *pool)
240 * {
241 * my_counter_metadata_t data;
242 * M_sql_error_t err;
243 * char msg[256];
244 *
245 * data.id = 5;
246 * data.inc = 25;
247 * data.result = 0;
248 *
249 * err = M_sql_trans_process(pool, M_SQL_ISOLATION_SERIALIZABLE, my_counter_inc, &data, msg, sizeof(msg));
250 * if (err != M_SQL_ERROR_SUCCESS) {
251 * M_printf("Error: %s: %s\n", M_sql_error_string(err), msg);
252 * return;
253 * }
254 * M_printf("Success! Final result: %lld\n", data.result);
255 * }
256 * \endcode
257 *
258 * \param[in] pool Initialized and started pool object.
259 * \param[in] isolation Requested isolation level. The database may choose the closest match
260 * if the isolation level requested is not supported.
261 * \param[in] cmd User-specified function to call to step through the sequence of SQL commands
262 * to run as part of the transaction.
263 * \param[in] cmd_arg Argument to pass to User-specified function for metadata about the command(s)
264 * being executed.
265 * \param[out] error User-supplied error buffer to output error message.
266 * \param[in] error_size Size of user-supplied error buffer.
267 * \return #M_SQL_ERROR_SUCCESS if executed to completion, or one of the #M_sql_error_t fatal errors on
268 * failure (but never #M_SQL_ERROR_QUERY_DEADLOCK or #M_SQL_ERROR_CONN_LOST as those are
269 * automatic retry events)
270 */
271M_API M_sql_error_t M_sql_trans_process(M_sql_connpool_t *pool, M_sql_isolation_t isolation, M_sql_trans_commands_t cmd, void *cmd_arg, char *error, size_t error_size);
272
273/*! Retrieve the #M_sql_connpool_t object from a transaction handle typically used within M_sql_trans_process()
274 * for using the SQL helpers like M_sql_query_append_updlock() and M_sql_query_append_bitop().
275 *
276 * \param[in] trans Transaction object
277 * \return #M_sql_connpool_t object
278 */
280
281/*! @} */
282
283__END_DECLS
284
285#endif /* __M_SQL_TRANS_H__ */
struct M_sql_connpool M_sql_connpool_t
Definition: m_sql.h:335
M_sql_error_t
Definition: m_sql.h:190
struct M_sql_stmt M_sql_stmt_t
Definition: m_sql_stmt.h:46
M_sql_error_t M_sql_trans_commit(M_sql_trans_t *trans, char *error, size_t error_size)
M_sql_isolation_t
Definition: m_sql_trans.h:53
M_sql_error_t M_sql_trans_execute(M_sql_trans_t *trans, M_sql_stmt_t *stmt)
M_sql_error_t M_sql_trans_begin(M_sql_trans_t **trans, M_sql_connpool_t *pool, M_sql_isolation_t isolation, char *error, size_t error_size)
M_sql_error_t M_sql_trans_rollback(M_sql_trans_t *trans)
M_sql_error_t M_sql_trans_process(M_sql_connpool_t *pool, M_sql_isolation_t isolation, M_sql_trans_commands_t cmd, void *cmd_arg, char *error, size_t error_size)
M_sql_error_t(* M_sql_trans_commands_t)(M_sql_trans_t *trans, void *arg, char *error, size_t error_size)
Definition: m_sql_trans.h:170
struct M_sql_trans M_sql_trans_t
Definition: m_sql_trans.h:50
M_sql_connpool_t * M_sql_trans_get_pool(M_sql_trans_t *trans)
@ M_SQL_ISOLATION_READCOMMITTED
Definition: m_sql_trans.h:56
@ M_SQL_ISOLATION_SERIALIZABLE
Definition: m_sql_trans.h:59
@ M_SQL_ISOLATION_UNKNOWN
Definition: m_sql_trans.h:54
@ M_SQL_ISOLATION_READUNCOMMITTED
Definition: m_sql_trans.h:55
@ M_SQL_ISOLATION_SNAPSHOT
Definition: m_sql_trans.h:58
@ M_SQL_ISOLATION_REPEATABLEREAD
Definition: m_sql_trans.h:57