Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
res_odbc_transaction.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2016, Digium, Inc.
5 *
6 * Mark Michelson <mmichelson@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19#include "asterisk.h"
20
21#include "asterisk/res_odbc.h"
23#include "asterisk/channel.h"
24#include "asterisk/pbx.h"
25#include "asterisk/app.h"
26#include "asterisk/module.h"
27
28/*** MODULEINFO
29 <depend>generic_odbc</depend>
30 <support_level>core</support_level>
31 ***/
32
33/*** DOCUMENTATION
34 <function name="ODBC" language="en_US">
35 <since>
36 <version>13.8.0</version>
37 </since>
38 <synopsis>
39 Controls ODBC transaction properties.
40 </synopsis>
41 <syntax>
42 <parameter name="property" required="true">
43 <enumlist>
44 <enum name="transaction">
45 <para>Gets or sets the active transaction ID. If set, and the transaction ID does not
46 exist and a <replaceable>database name</replaceable> is specified as an argument, it will be created.</para>
47 </enum>
48 <enum name="forcecommit">
49 <para>Controls whether a transaction will be automatically committed when the channel
50 hangs up. Defaults to forcecommit value from the relevant DSN (which defaults to false).
51 If a <replaceable>transaction ID</replaceable> is specified in the optional argument,
52 the property will be applied to that ID, otherwise to the current active ID.</para>
53 </enum>
54 <enum name="isolation">
55 <para>Controls the data isolation on uncommitted transactions. May be one of the
56 following: <literal>read_committed</literal>, <literal>read_uncommitted</literal>,
57 <literal>repeatable_read</literal>, or <literal>serializable</literal>. Defaults to the
58 database setting in <filename>res_odbc.conf</filename> or <literal>read_committed</literal>
59 if not specified. If a <replaceable>transaction ID</replaceable> is specified as an optional argument, it will be
60 applied to that ID, otherwise the current active ID.</para>
61 </enum>
62 </enumlist>
63 </parameter>
64 <parameter name="argument" required="false" />
65 </syntax>
66 <description>
67 <para>The ODBC() function allows setting several properties to influence how a connected
68 database processes transactions.</para>
69 </description>
70 </function>
71 <application name="ODBC_Commit" language="en_US">
72 <since>
73 <version>13.8.0</version>
74 </since>
75 <synopsis>
76 Commits a currently open database transaction.
77 </synopsis>
78 <syntax>
79 <parameter name="transaction ID" required="no" />
80 </syntax>
81 <description>
82 <para>Commits the database transaction specified by <replaceable>transaction ID</replaceable>
83 or the current active transaction, if not specified.</para>
84 </description>
85 </application>
86 <application name="ODBC_Rollback" language="en_US">
87 <since>
88 <version>13.8.0</version>
89 </since>
90 <synopsis>
91 Rollback a currently open database transaction.
92 </synopsis>
93 <syntax>
94 <parameter name="transaction ID" required="no" />
95 </syntax>
96 <description>
97 <para>Rolls back the database transaction specified by <replaceable>transaction ID</replaceable>
98 or the current active transaction, if not specified.</para>
99 </description>
100 </application>
101 ***/
102
103struct odbc_txn_frame {
105 struct odbc_obj *obj; /*!< Database handle within which transacted statements are run */
106 /*!\brief Is this record the current active transaction within the channel?
107 * Note that the active flag is really only necessary for statements which
108 * are triggered from the dialplan, as there isn't a direct correlation
109 * between multiple statements. Applications wishing to use transactions
110 * may simply perform each statement on the same odbc_obj, which keeps the
111 * transaction persistent.
112 */
113 unsigned int active:1;
114 unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
115 unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
116 char name[0]; /*!< Name of this transaction ID */
117};
118
119static struct odbc_txn_frame *release_transaction(struct odbc_txn_frame *tx);
120
121static void odbc_txn_free(void *vdata)
122{
123 struct odbc_txn_frame *tx;
124 AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
125
126 ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
127
128 AST_LIST_LOCK(oldlist);
129 while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
131 }
132 AST_LIST_UNLOCK(oldlist);
133 AST_LIST_HEAD_DESTROY(oldlist);
134 ast_free(oldlist);
135}
136
137static const struct ast_datastore_info txn_info = {
138 .type = "ODBC_Transaction",
139 .destroy = odbc_txn_free,
140};
141
142static struct odbc_txn_frame *create_transaction(struct ast_channel *chan, const char *name, const char *dsn)
143{
144 struct ast_datastore *txn_store;
145 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
146 struct odbc_txn_frame *txn = NULL;
147 struct odbc_txn_frame *otxn;
148
149 if (ast_strlen_zero(dsn)) {
150 return NULL;
151 }
152
153 ast_channel_lock(chan);
154 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
155 oldlist = txn_store->data;
156 } else {
157 if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) {
158 ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n");
159 ast_channel_unlock(chan);
160 return NULL;
161 }
162
163 if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
164 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n");
165 ast_datastore_free(txn_store);
166 ast_channel_unlock(chan);
167 return NULL;
168 }
169
170 txn_store->data = oldlist;
171 AST_LIST_HEAD_INIT(oldlist);
172 ast_channel_datastore_add(chan, txn_store);
173 }
174 ast_channel_unlock(chan);
175
176 txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1);
177 if (!txn) {
178 return NULL;
179 }
180
181 strcpy(txn->name, name); /* SAFE */
182 txn->obj = ast_odbc_request_obj(dsn, 0);
183 if (!txn->obj) {
184 ast_free(txn);
185 return NULL;
186 }
189 txn->active = 1;
190
191 if (SQLSetConnectAttr(txn->obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
192 ast_odbc_print_errors(SQL_HANDLE_DBC, txn->obj->con, "SetConnectAttr (Autocommit)");
194 ast_free(txn);
195 return NULL;
196 }
197
198 /* Set the isolation property */
199 if (SQLSetConnectAttr(txn->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)txn->isolation, 0) == SQL_ERROR) {
200 ast_odbc_print_errors(SQL_HANDLE_DBC, txn->obj->con, "SetConnectAttr");
202 ast_free(txn);
203 return NULL;
204 }
205
206 /* On creation, the txn becomes active, and all others inactive */
207 AST_LIST_LOCK(oldlist);
208 AST_LIST_TRAVERSE(oldlist, otxn, list) {
209 otxn->active = 0;
210 }
211 AST_LIST_INSERT_TAIL(oldlist, txn, list);
212 AST_LIST_UNLOCK(oldlist);
213
214 return txn;
215}
216
217static struct odbc_txn_frame *find_transaction(struct ast_channel *chan, const char *name, int active)
218{
219 struct ast_datastore *txn_store;
220 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
221 struct odbc_txn_frame *txn = NULL;
222
223 if (!chan || (!active && !name)) {
224 return NULL;
225 }
226
227 ast_channel_lock(chan);
228 txn_store = ast_channel_datastore_find(chan, &txn_info, NULL);
229 ast_channel_unlock(chan);
230
231 if (!txn_store) {
232 /* No datastore? Definitely no transaction then */
233 return NULL;
234 }
235
236 oldlist = txn_store->data;
237 AST_LIST_LOCK(oldlist);
238
239 AST_LIST_TRAVERSE(oldlist, txn, list) {
240 if (active) {
241 if (txn->active) {
242 break;
243 }
244 } else if (!strcasecmp(txn->name, name)) {
245 break;
246 }
247 }
248 AST_LIST_UNLOCK(oldlist);
249
250 return txn;
251}
252
254{
255 if (!tx) {
256 return NULL;
257 }
258
259 ast_debug(2, "release_transaction(%p) called (tx->obj = %p\n", tx, tx->obj);
260
261 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
262 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
263 ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLEndTran");
264 }
265
266 /* Transaction is done, reset autocommit
267 *
268 * XXX I'm unsure if this is actually necessary, since we're releasing
269 * the connection back to unixODBC. However, if unixODBC pooling is enabled,
270 * it can't hurt to do just in case.
271 */
272 if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
273 ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLSetAttr");
274 }
275
277 ast_free(tx);
278 return NULL;
279}
280
281static int commit_exec(struct ast_channel *chan, const char *data)
282{
283 struct odbc_txn_frame *tx;
284
285 if (ast_strlen_zero(data)) {
286 tx = find_transaction(chan, NULL, 1);
287 } else {
288 tx = find_transaction(chan, data, 0);
289 }
290
291 /* XXX COMMIT_RESULT is set to OK even if no transaction was found. Very misleading */
292 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
293
294 if (tx) {
295 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
296 struct ast_str *errors = ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLEndTran");
297 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
298 }
299 }
300 return 0;
301}
302
303static int rollback_exec(struct ast_channel *chan, const char *data)
304{
305 struct odbc_txn_frame *tx;
306
307 if (ast_strlen_zero(data)) {
308 tx = find_transaction(chan, NULL, 1);
309 } else {
310 tx = find_transaction(chan, data, 0);
311 }
312
313 /* XXX ROLLBACK_RESULT is set to OK even if no transaction was found. Very misleading */
314 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
315
316 if (tx) {
317 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
318 struct ast_str *errors = ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLEndTran");
319 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
320 }
321 }
322 return 0;
323}
324
325static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
326{
328 AST_APP_ARG(property);
329 AST_APP_ARG(opt);
330 );
331 struct odbc_txn_frame *tx;
332
334 if (strcasecmp(args.property, "transaction") == 0) {
335 if ((tx = find_transaction(chan, NULL, 1))) {
337 return 0;
338 }
339 } else if (strcasecmp(args.property, "isolation") == 0) {
340 if (!ast_strlen_zero(args.opt)) {
341 tx = find_transaction(chan, args.opt, 0);
342 } else {
343 tx = find_transaction(chan, NULL, 1);
344 }
345 if (tx) {
347 return 0;
348 }
349 } else if (strcasecmp(args.property, "forcecommit") == 0) {
350 if (!ast_strlen_zero(args.opt)) {
351 tx = find_transaction(chan, args.opt, 0);
352 } else {
353 tx = find_transaction(chan, NULL, 1);
354 }
355 if (tx) {
356 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
357 return 0;
358 }
359 }
360 return -1;
361}
362
363/* XXX The idea of "active" transactions is silly and makes things
364 * more prone to error. It would be much better if the transaction
365 * always had to be specified by name so that no implicit behavior
366 * occurred.
367 */
368static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
369{
370 struct ast_datastore *txn_store;
371 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
372 struct odbc_txn_frame *active = NULL, *txn;
373
374 if (!chan) {
375 return -1;
376 }
377
378 ast_channel_lock(chan);
379 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
380 ast_channel_unlock(chan);
381 return -1;
382 }
383
384 oldlist = txn_store->data;
385 AST_LIST_LOCK(oldlist);
386 AST_LIST_TRAVERSE(oldlist, txn, list) {
387 if (txn == tx) {
388 txn->active = 1;
389 active = txn;
390 } else {
391 txn->active = 0;
392 }
393 }
394 AST_LIST_UNLOCK(oldlist);
395 ast_channel_unlock(chan);
396 return active ? 0 : -1;
397}
398
399static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
400{
402 AST_APP_ARG(property);
403 AST_APP_ARG(opt);
404 );
405 struct odbc_txn_frame *tx;
406
408 if (strcasecmp(args.property, "transaction") == 0) {
409 /* Set active transaction */
410 if ((tx = find_transaction(chan, value, 0))) {
411 mark_transaction_active(chan, tx);
412 } else if (!create_transaction(chan, value, args.opt)) {
413 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
414 return -1;
415 }
416 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
417 return 0;
418 } else if (strcasecmp(args.property, "forcecommit") == 0) {
419 /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
420 if (ast_strlen_zero(args.opt)) {
421 tx = find_transaction(chan, NULL, 1);
422 } else {
423 tx = find_transaction(chan, args.opt, 0);
424 }
425 if (!tx) {
426 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
427 return -1;
428 }
429 if (ast_true(value)) {
430 tx->forcecommit = 1;
431 } else if (ast_false(value)) {
432 tx->forcecommit = 0;
433 } else {
434 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
435 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
436 return -1;
437 }
438
439 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
440 return 0;
441 } else if (strcasecmp(args.property, "isolation") == 0) {
442 /* How do uncommitted transactions affect reads? */
443 /* XXX This is completely useless. The problem is that setting the isolation here
444 * does not actually alter the connection. The only time the isolation gets set is
445 * when the transaction is created. The only way to set isolation is to set it on
446 * the ODBC class's configuration in res_odbc.conf.
447 */
449 if (ast_strlen_zero(args.opt)) {
450 tx = find_transaction(chan, NULL, 1);
451 } else {
452 tx = find_transaction(chan, args.opt, 0);
453 }
454 if (!tx) {
455 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
456 return -1;
457 }
458 if (isolation == 0) {
459 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
460 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
461 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
462 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
463 ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SetConnectAttr (Txn isolation)");
464 } else {
465 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
466 tx->isolation = isolation;
467 }
468 return 0;
469 } else {
470 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
471 return -1;
472 }
473}
474
475struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
476{
477 struct ast_datastore *txn_store;
478 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
479 struct odbc_txn_frame *txn = NULL;
480
481 if (!chan || !objname) {
482 /* No channel == no transaction */
483 return NULL;
484 }
485
486 ast_channel_lock(chan);
487 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
488 oldlist = txn_store->data;
489 } else {
490 ast_channel_unlock(chan);
491 return NULL;
492 }
493
494 AST_LIST_LOCK(oldlist);
495 ast_channel_unlock(chan);
496
497 AST_LIST_TRAVERSE(oldlist, txn, list) {
498 if (txn->obj && txn->obj->parent && !strcmp(ast_odbc_class_get_name(txn->obj->parent), objname)) {
499 AST_LIST_UNLOCK(oldlist);
500 return txn->obj;
501 }
502 }
503 AST_LIST_UNLOCK(oldlist);
504 return NULL;
505}
506
508 .name = "ODBC",
509 .read = acf_transaction_read,
510 .write = acf_transaction_write,
511};
512
513static const char * const app_commit = "ODBC_Commit";
514static const char * const app_rollback = "ODBC_Rollback";
515
516/* XXX res_odbc takes the path of disallowing unloads from happening.
517 * It's not a great precedent, but since trying to deal with unloading the module
518 * while transactions are active seems like a huge pain to deal with, we'll go
519 * the same way here.
520 */
521static int unload_module(void)
522{
523 return -1;
524}
525
526static int load_module(void)
527{
531 return 0;
532}
533
535 .support_level = AST_MODULE_SUPPORT_CORE,
536 .load = load_module,
537 .unload = unload_module,
538 .load_pri = AST_MODPRI_REALTIME_DEPEND,
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2414
#define ast_channel_lock(chan)
Definition: channel.h:2970
#define ast_channel_unlock(chan)
Definition: channel.h:2971
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2428
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static const char name[]
Definition: format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:626
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_REALTIME_DEPEND
Definition: module.h:335
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
ODBC resource manager.
unsigned int ast_odbc_class_get_isolation(struct odbc_class *class)
Get the transaction isolation setting for an ODBC class.
Definition: res_odbc.c:540
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:804
struct ast_str * ast_odbc_print_errors(SQLSMALLINT handle_type, SQLHANDLE handle, const char *operation)
Shortcut for printing errors to logs after a failed SQL operation.
Definition: res_odbc.c:515
const char * ast_odbc_isolation2text(int iso)
Convert from numeric transaction isolation values to their textual counterparts.
Definition: res_odbc.c:132
const char * ast_odbc_class_get_name(struct odbc_class *class)
Get the name of an ODBC class.
Definition: res_odbc.c:550
unsigned int ast_odbc_class_get_forcecommit(struct odbc_class *class)
Get the transaction forcecommit setting for an ODBC class.
Definition: res_odbc.c:545
int ast_odbc_text2isolation(const char *txt)
Convert from textual transaction isolation values to their numeric constants.
Definition: res_odbc.c:147
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
static struct odbc_txn_frame * find_transaction(struct ast_channel *chan, const char *name, int active)
static struct ast_custom_function odbc_function
struct odbc_obj * ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
Retrieve an ODBC transaction connection with the given ODBC class name.
static struct odbc_txn_frame * create_transaction(struct ast_channel *chan, const char *name, const char *dsn)
static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
static int commit_exec(struct ast_channel *chan, const char *data)
static const char *const app_rollback
static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
static void odbc_txn_free(void *vdata)
static const char *const app_commit
static struct odbc_txn_frame * release_transaction(struct odbc_txn_frame *tx)
static const struct ast_datastore_info txn_info
static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int rollback_exec(struct ast_channel *chan, const char *data)
static int load_module(void)
static int unload_module(void)
#define NULL
Definition: resample.c:96
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
Support for dynamic strings.
Definition: strings.h:623
Data source name.
Definition: func_odbc.c:181
ODBC container.
Definition: res_odbc.h:46
struct odbc_class * parent
Definition: res_odbc.h:48
SQLHDBC con
Definition: res_odbc.h:47
struct odbc_txn_frame::@447 list
char name[0]
Definition: res_odbc.c:129
unsigned int isolation
Definition: res_odbc.c:128
struct odbc_txn_frame::@446 list
unsigned int active
Is this record the current active transaction within the channel? Note that the active flag is really...
Definition: res_odbc.c:126
unsigned int forcecommit
Definition: res_odbc.c:127
struct odbc_obj * obj
Definition: res_odbc.c:118
int value
Definition: syslog.c:37
const char * args