Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 <synopsis>
36 Controls ODBC transaction properties.
37 </synopsis>
38 <syntax>
39 <parameter name="property" required="true">
40 <enumlist>
41 <enum name="transaction">
42 <para>Gets or sets the active transaction ID. If set, and the transaction ID does not
43 exist and a <replaceable>database name</replaceable> is specified as an argument, it will be created.</para>
44 </enum>
45 <enum name="forcecommit">
46 <para>Controls whether a transaction will be automatically committed when the channel
47 hangs up. Defaults to forcecommit value from the relevant DSN (which defaults to false).
48 If a <replaceable>transaction ID</replaceable> is specified in the optional argument,
49 the property will be applied to that ID, otherwise to the current active ID.</para>
50 </enum>
51 <enum name="isolation">
52 <para>Controls the data isolation on uncommitted transactions. May be one of the
53 following: <literal>read_committed</literal>, <literal>read_uncommitted</literal>,
54 <literal>repeatable_read</literal>, or <literal>serializable</literal>. Defaults to the
55 database setting in <filename>res_odbc.conf</filename> or <literal>read_committed</literal>
56 if not specified. If a <replaceable>transaction ID</replaceable> is specified as an optional argument, it will be
57 applied to that ID, otherwise the current active ID.</para>
58 </enum>
59 </enumlist>
60 </parameter>
61 <parameter name="argument" required="false" />
62 </syntax>
63 <description>
64 <para>The ODBC() function allows setting several properties to influence how a connected
65 database processes transactions.</para>
66 </description>
67 </function>
68 <application name="ODBC_Commit" language="en_US">
69 <synopsis>
70 Commits a currently open database transaction.
71 </synopsis>
72 <syntax>
73 <parameter name="transaction ID" required="no" />
74 </syntax>
75 <description>
76 <para>Commits the database transaction specified by <replaceable>transaction ID</replaceable>
77 or the current active transaction, if not specified.</para>
78 </description>
79 </application>
80 <application name="ODBC_Rollback" language="en_US">
81 <synopsis>
82 Rollback a currently open database transaction.
83 </synopsis>
84 <syntax>
85 <parameter name="transaction ID" required="no" />
86 </syntax>
87 <description>
88 <para>Rolls back the database transaction specified by <replaceable>transaction ID</replaceable>
89 or the current active transaction, if not specified.</para>
90 </description>
91 </application>
92 ***/
93
94struct odbc_txn_frame {
96 struct odbc_obj *obj; /*!< Database handle within which transacted statements are run */
97 /*!\brief Is this record the current active transaction within the channel?
98 * Note that the active flag is really only necessary for statements which
99 * are triggered from the dialplan, as there isn't a direct correlation
100 * between multiple statements. Applications wishing to use transactions
101 * may simply perform each statement on the same odbc_obj, which keeps the
102 * transaction persistent.
103 */
104 unsigned int active:1;
105 unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
106 unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
107 char name[0]; /*!< Name of this transaction ID */
108};
109
110static struct odbc_txn_frame *release_transaction(struct odbc_txn_frame *tx);
111
112static void odbc_txn_free(void *vdata)
113{
114 struct odbc_txn_frame *tx;
115 AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
116
117 ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
118
119 AST_LIST_LOCK(oldlist);
120 while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
122 }
123 AST_LIST_UNLOCK(oldlist);
124 AST_LIST_HEAD_DESTROY(oldlist);
125 ast_free(oldlist);
126}
127
128static const struct ast_datastore_info txn_info = {
129 .type = "ODBC_Transaction",
130 .destroy = odbc_txn_free,
131};
132
133static struct odbc_txn_frame *create_transaction(struct ast_channel *chan, const char *name, const char *dsn)
134{
135 struct ast_datastore *txn_store;
136 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
137 struct odbc_txn_frame *txn = NULL;
138 struct odbc_txn_frame *otxn;
139
140 if (ast_strlen_zero(dsn)) {
141 return NULL;
142 }
143
144 ast_channel_lock(chan);
145 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
146 oldlist = txn_store->data;
147 } else {
148 if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) {
149 ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n");
150 ast_channel_unlock(chan);
151 return NULL;
152 }
153
154 if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
155 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n");
156 ast_datastore_free(txn_store);
157 ast_channel_unlock(chan);
158 return NULL;
159 }
160
161 txn_store->data = oldlist;
162 AST_LIST_HEAD_INIT(oldlist);
163 ast_channel_datastore_add(chan, txn_store);
164 }
165 ast_channel_unlock(chan);
166
167 txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1);
168 if (!txn) {
169 return NULL;
170 }
171
172 strcpy(txn->name, name); /* SAFE */
173 txn->obj = ast_odbc_request_obj(dsn, 0);
174 if (!txn->obj) {
175 ast_free(txn);
176 return NULL;
177 }
180 txn->active = 1;
181
182 if (SQLSetConnectAttr(txn->obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
183 ast_odbc_print_errors(SQL_HANDLE_DBC, txn->obj->con, "SetConnectAttr (Autocommit)");
185 ast_free(txn);
186 return NULL;
187 }
188
189 /* Set the isolation property */
190 if (SQLSetConnectAttr(txn->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)txn->isolation, 0) == SQL_ERROR) {
191 ast_odbc_print_errors(SQL_HANDLE_DBC, txn->obj->con, "SetConnectAttr");
193 ast_free(txn);
194 return NULL;
195 }
196
197 /* On creation, the txn becomes active, and all others inactive */
198 AST_LIST_LOCK(oldlist);
199 AST_LIST_TRAVERSE(oldlist, otxn, list) {
200 otxn->active = 0;
201 }
202 AST_LIST_INSERT_TAIL(oldlist, txn, list);
203 AST_LIST_UNLOCK(oldlist);
204
205 return txn;
206}
207
208static struct odbc_txn_frame *find_transaction(struct ast_channel *chan, const char *name, int active)
209{
210 struct ast_datastore *txn_store;
211 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
212 struct odbc_txn_frame *txn = NULL;
213
214 if (!chan || (!active && !name)) {
215 return NULL;
216 }
217
218 ast_channel_lock(chan);
219 txn_store = ast_channel_datastore_find(chan, &txn_info, NULL);
220 ast_channel_unlock(chan);
221
222 if (!txn_store) {
223 /* No datastore? Definitely no transaction then */
224 return NULL;
225 }
226
227 oldlist = txn_store->data;
228 AST_LIST_LOCK(oldlist);
229
230 AST_LIST_TRAVERSE(oldlist, txn, list) {
231 if (active) {
232 if (txn->active) {
233 break;
234 }
235 } else if (!strcasecmp(txn->name, name)) {
236 break;
237 }
238 }
239 AST_LIST_UNLOCK(oldlist);
240
241 return txn;
242}
243
245{
246 if (!tx) {
247 return NULL;
248 }
249
250 ast_debug(2, "release_transaction(%p) called (tx->obj = %p\n", tx, tx->obj);
251
252 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
253 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
254 ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLEndTran");
255 }
256
257 /* Transaction is done, reset autocommit
258 *
259 * XXX I'm unsure if this is actually necessary, since we're releasing
260 * the connection back to unixODBC. However, if unixODBC pooling is enabled,
261 * it can't hurt to do just in case.
262 */
263 if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
264 ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLSetAttr");
265 }
266
268 ast_free(tx);
269 return NULL;
270}
271
272static int commit_exec(struct ast_channel *chan, const char *data)
273{
274 struct odbc_txn_frame *tx;
275
276 if (ast_strlen_zero(data)) {
277 tx = find_transaction(chan, NULL, 1);
278 } else {
279 tx = find_transaction(chan, data, 0);
280 }
281
282 /* XXX COMMIT_RESULT is set to OK even if no transaction was found. Very misleading */
283 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
284
285 if (tx) {
286 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
287 struct ast_str *errors = ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLEndTran");
288 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
289 }
290 }
291 return 0;
292}
293
294static int rollback_exec(struct ast_channel *chan, const char *data)
295{
296 struct odbc_txn_frame *tx;
297
298 if (ast_strlen_zero(data)) {
299 tx = find_transaction(chan, NULL, 1);
300 } else {
301 tx = find_transaction(chan, data, 0);
302 }
303
304 /* XXX ROLLBACK_RESULT is set to OK even if no transaction was found. Very misleading */
305 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
306
307 if (tx) {
308 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
309 struct ast_str *errors = ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SQLEndTran");
310 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
311 }
312 }
313 return 0;
314}
315
316static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
317{
319 AST_APP_ARG(property);
320 AST_APP_ARG(opt);
321 );
322 struct odbc_txn_frame *tx;
323
325 if (strcasecmp(args.property, "transaction") == 0) {
326 if ((tx = find_transaction(chan, NULL, 1))) {
328 return 0;
329 }
330 } else if (strcasecmp(args.property, "isolation") == 0) {
331 if (!ast_strlen_zero(args.opt)) {
332 tx = find_transaction(chan, args.opt, 0);
333 } else {
334 tx = find_transaction(chan, NULL, 1);
335 }
336 if (tx) {
338 return 0;
339 }
340 } else if (strcasecmp(args.property, "forcecommit") == 0) {
341 if (!ast_strlen_zero(args.opt)) {
342 tx = find_transaction(chan, args.opt, 0);
343 } else {
344 tx = find_transaction(chan, NULL, 1);
345 }
346 if (tx) {
347 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
348 return 0;
349 }
350 }
351 return -1;
352}
353
354/* XXX The idea of "active" transactions is silly and makes things
355 * more prone to error. It would be much better if the transaction
356 * always had to be specified by name so that no implicit behavior
357 * occurred.
358 */
359static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
360{
361 struct ast_datastore *txn_store;
362 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
363 struct odbc_txn_frame *active = NULL, *txn;
364
365 if (!chan) {
366 return -1;
367 }
368
369 ast_channel_lock(chan);
370 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
371 ast_channel_unlock(chan);
372 return -1;
373 }
374
375 oldlist = txn_store->data;
376 AST_LIST_LOCK(oldlist);
377 AST_LIST_TRAVERSE(oldlist, txn, list) {
378 if (txn == tx) {
379 txn->active = 1;
380 active = txn;
381 } else {
382 txn->active = 0;
383 }
384 }
385 AST_LIST_UNLOCK(oldlist);
386 ast_channel_unlock(chan);
387 return active ? 0 : -1;
388}
389
390static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
391{
393 AST_APP_ARG(property);
394 AST_APP_ARG(opt);
395 );
396 struct odbc_txn_frame *tx;
397
399 if (strcasecmp(args.property, "transaction") == 0) {
400 /* Set active transaction */
401 if ((tx = find_transaction(chan, value, 0))) {
402 mark_transaction_active(chan, tx);
403 } else if (!create_transaction(chan, value, args.opt)) {
404 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
405 return -1;
406 }
407 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
408 return 0;
409 } else if (strcasecmp(args.property, "forcecommit") == 0) {
410 /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
411 if (ast_strlen_zero(args.opt)) {
412 tx = find_transaction(chan, NULL, 1);
413 } else {
414 tx = find_transaction(chan, args.opt, 0);
415 }
416 if (!tx) {
417 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
418 return -1;
419 }
420 if (ast_true(value)) {
421 tx->forcecommit = 1;
422 } else if (ast_false(value)) {
423 tx->forcecommit = 0;
424 } else {
425 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
426 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
427 return -1;
428 }
429
430 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
431 return 0;
432 } else if (strcasecmp(args.property, "isolation") == 0) {
433 /* How do uncommitted transactions affect reads? */
434 /* XXX This is completely useless. The problem is that setting the isolation here
435 * does not actually alter the connection. The only time the isolation gets set is
436 * when the transaction is created. The only way to set isolation is to set it on
437 * the ODBC class's configuration in res_odbc.conf.
438 */
440 if (ast_strlen_zero(args.opt)) {
441 tx = find_transaction(chan, NULL, 1);
442 } else {
443 tx = find_transaction(chan, args.opt, 0);
444 }
445 if (!tx) {
446 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
447 return -1;
448 }
449 if (isolation == 0) {
450 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
451 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
452 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
453 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
454 ast_odbc_print_errors(SQL_HANDLE_DBC, tx->obj->con, "SetConnectAttr (Txn isolation)");
455 } else {
456 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
457 tx->isolation = isolation;
458 }
459 return 0;
460 } else {
461 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
462 return -1;
463 }
464}
465
466struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
467{
468 struct ast_datastore *txn_store;
469 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
470 struct odbc_txn_frame *txn = NULL;
471
472 if (!chan || !objname) {
473 /* No channel == no transaction */
474 return NULL;
475 }
476
477 ast_channel_lock(chan);
478 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
479 oldlist = txn_store->data;
480 } else {
481 ast_channel_unlock(chan);
482 return NULL;
483 }
484
485 AST_LIST_LOCK(oldlist);
486 ast_channel_unlock(chan);
487
488 AST_LIST_TRAVERSE(oldlist, txn, list) {
489 if (txn->obj && txn->obj->parent && !strcmp(ast_odbc_class_get_name(txn->obj->parent), objname)) {
490 AST_LIST_UNLOCK(oldlist);
491 return txn->obj;
492 }
493 }
494 AST_LIST_UNLOCK(oldlist);
495 return NULL;
496}
497
499 .name = "ODBC",
500 .read = acf_transaction_read,
501 .write = acf_transaction_write,
502};
503
504static const char * const app_commit = "ODBC_Commit";
505static const char * const app_rollback = "ODBC_Rollback";
506
507/* XXX res_odbc takes the path of disallowing unloads from happening.
508 * It's not a great precedent, but since trying to deal with unloading the module
509 * while transactions are active seems like a huge pain to deal with, we'll go
510 * the same way here.
511 */
512static int unload_module(void)
513{
514 return -1;
515}
516
517static int load_module(void)
518{
522 return 0;
523}
524
526 .support_level = AST_MODULE_SUPPORT_CORE,
527 .load = load_module,
528 .unload = unload_module,
529 .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:2404
#define ast_channel_lock(chan)
Definition: channel.h:2968
#define ast_channel_unlock(chan)
Definition: channel.h:2969
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:2418
#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:1558
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:167
ODBC container.
Definition: res_odbc.h:46
struct odbc_class * parent
Definition: res_odbc.h:48
SQLHDBC con
Definition: res_odbc.h:47
char name[0]
Definition: res_odbc.c:129
unsigned int isolation
Definition: res_odbc.c:128
struct odbc_txn_frame::@444 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_txn_frame::@443 list
struct odbc_obj * obj
Definition: res_odbc.c:118
int value
Definition: syslog.c:37
const char * args