Asterisk - The Open Source Telephony Project  GIT-master-a24979a
Data Structures | Functions | Variables
res_odbc.c File Reference

ODBC resource manager. More...

#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"
#include "asterisk/time.h"
#include "asterisk/astobj2.h"
#include "asterisk/app.h"
#include "asterisk/strings.h"
#include "asterisk/threadstorage.h"
Include dependency graph for res_odbc.c:

Go to the source code of this file.

Data Structures

struct  odbc_class
 
struct  odbc_tables
 
struct  odbc_txn_frame
 

Functions

static void __init_errors_buf (void)
 
static void __reg_module (void)
 
static void __unreg_module (void)
 
struct odbc_obj_ast_odbc_request_obj (const char *name, int check, const char *file, const char *function, int lineno)
 
struct odbc_obj_ast_odbc_request_obj2 (const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
 
static int aoro2_class_cb (void *obj, void *arg, int flags)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
SQLRETURN ast_odbc_ast_str_SQLGetData (struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
 Wrapper for SQLGetData to use with dynamic strings. More...
 
int ast_odbc_backslash_is_escape (struct odbc_obj *obj)
 Checks if the database natively supports backslash as an escape character. More...
 
unsigned int ast_odbc_class_get_forcecommit (struct odbc_class *class)
 Get the transaction forcecommit setting for an ODBC class. More...
 
unsigned int ast_odbc_class_get_isolation (struct odbc_class *class)
 Get the transaction isolation setting for an ODBC class. More...
 
const char * ast_odbc_class_get_name (struct odbc_class *class)
 Get the name of an ODBC class. More...
 
int ast_odbc_clear_cache (const char *database, const char *tablename)
 Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call. More...
 
SQLHSTMT ast_odbc_direct_execute (struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
 Executes an non prepared statement and returns the resulting statement handle. More...
 
SQLRETURN ast_odbc_execute_sql (struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
 Execute a unprepared SQL query. More...
 
struct odbc_cache_columnsast_odbc_find_column (struct odbc_cache_tables *table, const char *colname)
 Find a column entry within a cached table structure. More...
 
struct odbc_cache_tablesast_odbc_find_table (const char *database, const char *tablename)
 Find or create an entry describing the table specified. More...
 
unsigned int ast_odbc_get_max_connections (const char *name)
 Return the current configured maximum number of connections for a class. More...
 
const char * ast_odbc_isolation2text (int iso)
 Convert from numeric transaction isolation values to their textual counterparts. More...
 
int ast_odbc_prepare (struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
 Prepares a SQL query on a statement. More...
 
SQLHSTMT ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
 Prepares, executes, and returns the resulting statement handle. More...
 
struct ast_strast_odbc_print_errors (SQLSMALLINT handle_type, SQLHANDLE handle, const char *operation)
 Shortcut for printing errors to logs after a failed SQL operation. More...
 
void ast_odbc_release_obj (struct odbc_obj *obj)
 Releases an ODBC object previously allocated by ast_odbc_request_obj() More...
 
int ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt)
 Executes a prepared statement handle. More...
 
int ast_odbc_text2isolation (const char *txt)
 Convert from textual transaction isolation values to their numeric constants. More...
 
static int connection_dead (struct odbc_obj *connection, struct odbc_class *class)
 Determine if the connection has died. More...
 
static void destroy_table_cache (struct odbc_cache_tables *table)
 
static char * handle_cli_odbc_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static int load_module (void)
 
static int load_odbc_config (void)
 
static void odbc_class_destructor (void *data)
 
static odbc_status odbc_obj_connect (struct odbc_obj *obj)
 
static void odbc_obj_destructor (void *data)
 
static odbc_status odbc_obj_disconnect (struct odbc_obj *obj)
 
static void odbc_register_class (struct odbc_class *class, int connect)
 
static int reload (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, .requires = "res_odbc_transaction", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ao2_containerclass_container
 
static struct ast_cli_entry cli_odbc []
 
static struct ast_threadstorage errors_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_errors_buf , .custom_init = NULL , }
 
static struct odbc_tables odbc_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 

Detailed Description

ODBC resource manager.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m
Anthony Minessale II anthm.nosp@m.ct@y.nosp@m.ahoo..nosp@m.com
Tilghman Lesher tilgh.nosp@m.man@.nosp@m.digiu.nosp@m.m.co.nosp@m.m

Definition in file res_odbc.c.

Function Documentation

◆ __init_errors_buf()

static void __init_errors_buf ( void  )
static

Definition at line 113 of file res_odbc.c.

115 {

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1140 of file res_odbc.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1140 of file res_odbc.c.

◆ _ast_odbc_request_obj()

struct odbc_obj* _ast_odbc_request_obj ( const char *  name,
int  check,
const char *  file,
const char *  function,
int  lineno 
)

Definition at line 975 of file res_odbc.c.

976 {
977  struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
978  /* XXX New flow means that the "check" parameter doesn't do anything. We're requesting
979  * a connection from ODBC. We'll either get a new one, which obviously is already connected, or
980  * we'll get one from the ODBC connection pool. In that case, it will ensure to only give us a
981  * live connection
982  */
983  return _ast_odbc_request_obj2(name, flags, file, function, lineno);
984 }
static const char name[]
Definition: format_mp3.c:68
struct odbc_obj * _ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
Definition: res_odbc.c:908
@ RES_ODBC_SANITY_CHECK
Definition: res_odbc.h:40
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200

References _ast_odbc_request_obj2(), make_ari_stubs::file, ast_flags::flags, name, and RES_ODBC_SANITY_CHECK.

◆ _ast_odbc_request_obj2()

struct odbc_obj* _ast_odbc_request_obj2 ( const char *  name,
struct ast_flags  flags,
const char *  file,
const char *  function,
int  lineno 
)

Definition at line 908 of file res_odbc.c.

909 {
910  struct odbc_obj *obj = NULL;
911  struct odbc_class *class;
912 
913  if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
914  ast_debug(1, "Class '%s' not found!\n", name);
915  return NULL;
916  }
917 
918  ast_mutex_lock(&class->lock);
919 
920  while (!obj) {
921  obj = AST_LIST_REMOVE_HEAD(&class->connections, list);
922 
923  if (!obj) {
924  if (class->connection_cnt < class->maxconnections) {
925  /* If no connection is immediately available establish a new
926  * one if allowed. If we try and fail we give up completely as
927  * we could go into an infinite loop otherwise.
928  */
929  obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
930  if (!obj) {
931  break;
932  }
933 
934  obj->parent = ao2_bump(class);
935  if (odbc_obj_connect(obj) == ODBC_FAIL) {
936  ao2_ref(obj->parent, -1);
937  ao2_ref(obj, -1);
938  obj = NULL;
939  break;
940  }
941 
942  class->connection_cnt++;
943  ast_debug(2, "Created ODBC handle %p on class '%s', new count is %zd\n", obj,
944  name, class->connection_cnt);
945  } else {
946  /* Otherwise if we're not allowed to create a new one we
947  * wait for another thread to give up the connection they
948  * own.
949  */
950  ast_cond_wait(&class->cond, &class->lock);
951  }
952  } else if (connection_dead(obj, class)) {
953  /* If the connection is dead try to grab another functional one from the
954  * pool instead of trying to resurrect this one.
955  */
956  ao2_ref(obj, -1);
957  obj = NULL;
958  class->connection_cnt--;
959  ast_debug(2, "ODBC handle %p dead - removing from class '%s', new count is %zd\n",
960  obj, name, class->connection_cnt);
961  } else {
962  /* We successfully grabbed a connection from the pool and all is well!
963  */
964  obj->parent = ao2_bump(class);
965  ast_debug(2, "Reusing ODBC handle %p from class '%s'\n", obj, name);
966  }
967  }
968 
969  ast_mutex_unlock(&class->lock);
970  ao2_ref(class, -1);
971 
972  return obj;
973 }
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define ast_cond_wait(cond, mutex)
Definition: lock.h:203
#define ast_mutex_unlock(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:187
static int aoro2_class_cb(void *obj, void *arg, int flags)
Definition: res_odbc.c:838
static void odbc_obj_destructor(void *data)
Definition: res_odbc.c:194
static odbc_status odbc_obj_connect(struct odbc_obj *obj)
Definition: res_odbc.c:1013
static struct ao2_container * class_container
Definition: res_odbc.c:105
static int connection_dead(struct odbc_obj *connection, struct odbc_class *class)
Determine if the connection has died.
Definition: res_odbc.c:872
@ ODBC_FAIL
Definition: res_odbc.h:36
#define NULL
Definition: resample.c:96
struct odbc_class::@470 list
ODBC container.
Definition: res_odbc.h:46
struct odbc_class * parent
Definition: res_odbc.h:48

References ao2_alloc, ao2_bump, ao2_callback, ao2_ref, aoro2_class_cb(), ast_cond_wait, ast_debug, AST_LIST_REMOVE_HEAD, ast_mutex_lock, ast_mutex_unlock, class_container, connection_dead(), odbc_class::list, name, NULL, ODBC_FAIL, odbc_obj_connect(), odbc_obj_destructor(), and odbc_obj::parent.

Referenced by _ast_odbc_request_obj().

◆ aoro2_class_cb()

static int aoro2_class_cb ( void *  obj,
void *  arg,
int  flags 
)
static

Definition at line 838 of file res_odbc.c.

839 {
840  struct odbc_class *class = obj;
841  char *name = arg;
842  if (!strcmp(class->name, name) && !class->delme) {
843  return CMP_MATCH | CMP_STOP;
844  }
845  return 0;
846 }
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028

References CMP_MATCH, CMP_STOP, and name.

Referenced by _ast_odbc_request_obj2(), and ast_odbc_get_max_connections().

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 1140 of file res_odbc.c.

◆ ast_odbc_ast_str_SQLGetData()

SQLRETURN ast_odbc_ast_str_SQLGetData ( struct ast_str **  buf,
int  pmaxlen,
SQLHSTMT  StatementHandle,
SQLUSMALLINT  ColumnNumber,
SQLSMALLINT  TargetType,
SQLLEN *  StrLen_or_Ind 
)

Wrapper for SQLGetData to use with dynamic strings.

Parameters
bufAddress of the pointer to the ast_str structure.
pmaxlenThe maximum size of the resulting string, or 0 for no limit.
StatementHandleThe statement handle from which to retrieve data.
ColumnNumberColumn number (1-based offset) for which to retrieve data.
TargetTypeThe SQL constant indicating what kind of data is to be retrieved (usually SQL_CHAR)
StrLen_or_IndA pointer to a length indicator, specifying the total length of data.

Definition at line 498 of file res_odbc.c.

499 {
500  SQLRETURN res;
501 
502  if (pmaxlen == 0) {
503  if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
504  ast_str_make_space(buf, *StrLen_or_Ind + 1);
505  }
506  } else if (pmaxlen > 0) {
507  ast_str_make_space(buf, pmaxlen);
508  }
509  res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
511 
512  return res;
513 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:739
#define ast_str_make_space(buf, new_len)
Definition: strings.h:806
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:684
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:723

References ast_str_buffer(), ast_str_make_space, ast_str_size(), ast_str_update(), and buf.

Referenced by acf_odbc_read(), and cli_odbc_read().

◆ ast_odbc_backslash_is_escape()

int ast_odbc_backslash_is_escape ( struct odbc_obj obj)

Checks if the database natively supports backslash as an escape character.

Parameters
objThe ODBC object
Return values
1if backslash is a native escape character
0if an ESCAPE clause is needed to support '\'

Definition at line 833 of file res_odbc.c.

834 {
835  return obj->parent->backslash_is_escape;
836 }
unsigned int backslash_is_escape
Definition: res_odbc.c:74

References odbc_class::backslash_is_escape, and odbc_obj::parent.

Referenced by odbc_log(), realtime_multi_odbc(), and realtime_odbc().

◆ ast_odbc_class_get_forcecommit()

unsigned int ast_odbc_class_get_forcecommit ( struct odbc_class class)

Get the transaction forcecommit setting for an ODBC class.

Definition at line 545 of file res_odbc.c.

546 {
547  return class->forcecommit;
548 }

Referenced by create_transaction().

◆ ast_odbc_class_get_isolation()

unsigned int ast_odbc_class_get_isolation ( struct odbc_class class)

Get the transaction isolation setting for an ODBC class.

Definition at line 540 of file res_odbc.c.

541 {
542  return class->isolation;
543 }

Referenced by create_transaction().

◆ ast_odbc_class_get_name()

const char* ast_odbc_class_get_name ( struct odbc_class class)

Get the name of an ODBC class.

Definition at line 550 of file res_odbc.c.

551 {
552  return class->name;
553 }

Referenced by ast_odbc_retrieve_transaction_obj().

◆ ast_odbc_clear_cache()

int ast_odbc_clear_cache ( const char *  database,
const char *  tablename 
)

Remove a cache entry from memory This function may be called to clear entries created and cached by the ast_odbc_find_table() API call.

Parameters
databaseName of an ODBC class (used to ensure like-named tables in different databases are not confused)
tablenameTablename for which a cached record should be removed
Return values
0if the cache entry was removed.
-1if no matching entry was found.
Since
1.6.1

Definition at line 343 of file res_odbc.c.

344 {
345  struct odbc_cache_tables *tableptr;
346 
348  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
349  if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
351  destroy_table_cache(tableptr);
352  break;
353  }
354  }
357  return tableptr ? 0 : -1;
358 }
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:545
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:617
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
static void destroy_table_cache(struct odbc_cache_tables *table)
Definition: res_odbc.c:201
char * connection
Definition: res_odbc.h:71

References AST_LIST_REMOVE_CURRENT, AST_RWLIST_TRAVERSE_SAFE_BEGIN, AST_RWLIST_TRAVERSE_SAFE_END, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, odbc_cache_tables::connection, destroy_table_cache(), and odbc_cache_tables::table.

◆ ast_odbc_direct_execute()

SQLHSTMT ast_odbc_direct_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  exec_cb,
void *  data 
)

Executes an non prepared statement and returns the resulting statement handle.

Parameters
objThe ODBC object
exec_cbA function callback, which, when called, should return a statement handle with result columns bound.
dataA parameter to be passed to the exec_cb parameter function, indicating which statement handle is to be prepared.
Returns
a statement handle
Return values
NULLon error

Definition at line 360 of file res_odbc.c.

361 {
362  struct timeval start;
363  SQLHSTMT stmt;
364 
365  if (obj->parent->logging) {
366  start = ast_tvnow();
367  }
368 
369  stmt = exec_cb(obj, data);
370 
371  if (obj->parent->logging) {
372  long execution_time = ast_tvdiff_ms(ast_tvnow(), start);
373 
374  if (obj->parent->slowquerylimit && execution_time > obj->parent->slowquerylimit) {
375  ast_log(LOG_WARNING, "SQL query '%s' took %ld milliseconds to execute on class '%s', this may indicate a database problem\n",
376  obj->sql_text, execution_time, obj->parent->name);
377  }
378 
379  ast_mutex_lock(&obj->parent->lock);
380  if (execution_time > obj->parent->longest_query_execution_time || !obj->parent->sql_text) {
381  obj->parent->longest_query_execution_time = execution_time;
382  /* Due to the callback nature of the res_odbc API it's not possible to ensure that
383  * the SQL text is removed from the connection in all cases, so only if it becomes the
384  * new longest executing query do we steal the SQL text. In other cases what will happen
385  * is that the SQL text will be freed if the connection is released back to the class or
386  * if a new query is done on the connection.
387  */
388  ast_free(obj->parent->sql_text);
389  obj->parent->sql_text = obj->sql_text;
390  obj->sql_text = NULL;
391  }
392  ast_mutex_unlock(&obj->parent->lock);
393  }
394 
395  return stmt;
396 }
#define ast_free(a)
Definition: astmm.h:180
#define ast_log
Definition: astobj2.c:42
#define LOG_WARNING
unsigned int slowquerylimit
Definition: res_odbc.c:102
char * sql_text
Definition: res_odbc.c:100
char name[80]
Definition: res_odbc.c:67
unsigned int logging
Definition: res_odbc.c:92
long longest_query_execution_time
Definition: res_odbc.c:98
ast_mutex_t lock
Definition: res_odbc.c:86
char * sql_text
Definition: res_odbc.h:54
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:105
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:157

References ast_free, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_tvdiff_ms(), ast_tvnow(), odbc_class::lock, LOG_WARNING, odbc_class::logging, odbc_class::longest_query_execution_time, odbc_class::name, NULL, odbc_obj::parent, odbc_class::slowquerylimit, odbc_obj::sql_text, and odbc_class::sql_text.

Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), connection_dead(), and odbc_log().

◆ ast_odbc_execute_sql()

SQLRETURN ast_odbc_execute_sql ( struct odbc_obj obj,
SQLHSTMT *  stmt,
const char *  sql 
)

Execute a unprepared SQL query.

Parameters
objThe ODBC object
stmtThe statement
sqlThe SQL query
Note
This should be used in place of SQLExecDirect

Definition at line 469 of file res_odbc.c.

470 {
471  if (obj->parent->logging) {
472  ast_free(obj->sql_text);
473  obj->sql_text = ast_strdup(sql);
475  }
476 
477  return SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
478 }
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:755
int queries_executed
Definition: res_odbc.c:96

References ast_atomic_fetchadd_int(), ast_free, ast_strdup, odbc_class::logging, odbc_obj::parent, odbc_class::queries_executed, and odbc_obj::sql_text.

Referenced by execute(), and execute_cb().

◆ ast_odbc_find_column()

struct odbc_cache_columns* ast_odbc_find_column ( struct odbc_cache_tables table,
const char *  colname 
)

Find a column entry within a cached table structure.

Parameters
tableCached table structure, as returned from ast_odbc_find_table()
colnameThe column name requested
Returns
A structure describing the column type, or NULL, if the column is not found.
Since
1.6.1

Definition at line 332 of file res_odbc.c.

333 {
334  struct odbc_cache_columns *col;
335  AST_RWLIST_TRAVERSE(&table->columns, col, list) {
336  if (strcasecmp(col->name, colname) == 0) {
337  return col;
338  }
339  }
340  return NULL;
341 }
static char * table
Definition: cdr_odbc.c:55
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
These structures are used for adaptive capabilities.
Definition: res_odbc.h:59

References AST_RWLIST_TRAVERSE, odbc_cache_columns::name, NULL, and table.

Referenced by update2_prepare(), and update_odbc().

◆ ast_odbc_find_table()

struct odbc_cache_tables* ast_odbc_find_table ( const char *  database,
const char *  tablename 
)

Find or create an entry describing the table specified.

XXX This creates a connection and disconnects it. In some situations, the caller of this function has its own connection and could donate it to this function instead of needing to create another one.

XXX The automatic readlock of the columns is awkward. It's done because it's possible for multiple threads to have references to the table, and the table is not refcounted. Possible changes here would be

  • Eliminate the table cache entirely. The use of ast_odbc_find_table() is generally questionable. The only real good use right now is from ast_realtime_require_field() in order to make sure the DB has the expected columns in it. Since that is only used sparingly, the need to cache tables is questionable. Instead, the table structure can be fetched from the DB directly each time, resulting in a single owner of the data.
  • Make odbc_cache_tables a refcounted object.

Definition at line 232 of file res_odbc.c.

233 {
234  struct odbc_cache_tables *tableptr;
235  struct odbc_cache_columns *entry;
236  char columnname[80];
237  SQLLEN sqlptr;
238  SQLHSTMT stmt = NULL;
239  int res = 0, error = 0;
240  struct odbc_obj *obj;
241 
243  AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
244  if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
245  break;
246  }
247  }
248  if (tableptr) {
249  AST_RWLIST_RDLOCK(&tableptr->columns);
251  return tableptr;
252  }
253 
254  if (!(obj = ast_odbc_request_obj(database, 0))) {
255  ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
257  return NULL;
258  }
259 
260  /* Table structure not already cached; build it now. */
261  do {
262  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
263  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
264  ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
265  break;
266  }
267 
268  res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
269  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
270  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
271  ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
272  break;
273  }
274 
275  if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
276  ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
277  break;
278  }
279 
280  tableptr->connection = (char *)tableptr + sizeof(*tableptr);
281  tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
282  strcpy(tableptr->connection, database); /* SAFE */
283  strcpy(tableptr->table, tablename); /* SAFE */
284  AST_RWLIST_HEAD_INIT(&(tableptr->columns));
285 
286  while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
287  SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
288 
289  if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
290  ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
291  error = 1;
292  break;
293  }
294  entry->name = (char *)entry + sizeof(*entry);
295  strcpy(entry->name, columnname);
296 
297  SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
298  SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
299  SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
300  SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
301  SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
302  SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
303 
304  /* Specification states that the octenlen should be the maximum number of bytes
305  * returned in a char or binary column, but it seems that some drivers just set
306  * it to NULL. (Bad Postgres! No biscuit!) */
307  if (entry->octetlen == 0) {
308  entry->octetlen = entry->size;
309  }
310 
311  ast_debug(3, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
312  /* Insert column info into column list */
313  AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
314  }
315  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
316 
318  AST_RWLIST_RDLOCK(&(tableptr->columns));
319  break;
320  } while (1);
321 
323 
324  if (error) {
325  destroy_table_cache(tableptr);
326  tableptr = NULL;
327  }
329  return tableptr;
330 }
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define LOG_ERROR
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_HEAD_INIT(head)
Initializes an rwlist head structure.
Definition: linkedlists.h:639
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
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
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
Definition: search.h:40
struct odbc_cache_tables::_columns columns
struct odbc_obj::@285 list
SQLHDBC con
Definition: res_odbc.h:47
int error(const char *format,...)
Definition: utils/frame.c:999

References ast_calloc, ast_debug, AST_LIST_INSERT_TAIL, ast_log, ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_HEAD_INIT, AST_RWLIST_INSERT_TAIL, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, odbc_cache_tables::columns, odbc_obj::con, odbc_cache_tables::connection, destroy_table_cache(), error(), odbc_obj::list, LOG_ERROR, LOG_WARNING, NULL, and odbc_cache_tables::table.

Referenced by require_odbc(), update2_odbc(), and update_odbc().

◆ ast_odbc_get_max_connections()

unsigned int ast_odbc_get_max_connections ( const char *  name)

Return the current configured maximum number of connections for a class.

Definition at line 848 of file res_odbc.c.

849 {
850  struct odbc_class *class;
851  unsigned int max_connections;
852 
853  class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name);
854  if (!class) {
855  return 0;
856  }
857 
858  max_connections = class->maxconnections;
859  ao2_ref(class, -1);
860 
861  return max_connections;
862 }

References ao2_callback, ao2_ref, aoro2_class_cb(), class_container, and name.

Referenced by release_obj_or_dsn().

◆ ast_odbc_isolation2text()

const char* ast_odbc_isolation2text ( int  iso)

Convert from numeric transaction isolation values to their textual counterparts.

Definition at line 132 of file res_odbc.c.

133 {
134  if (iso == SQL_TXN_READ_COMMITTED) {
135  return "read_committed";
136  } else if (iso == SQL_TXN_READ_UNCOMMITTED) {
137  return "read_uncommitted";
138  } else if (iso == SQL_TXN_SERIALIZABLE) {
139  return "serializable";
140  } else if (iso == SQL_TXN_REPEATABLE_READ) {
141  return "repeatable_read";
142  } else {
143  return "unknown";
144  }
145 }

Referenced by acf_transaction_read().

◆ ast_odbc_prepare()

int ast_odbc_prepare ( struct odbc_obj obj,
SQLHSTMT *  stmt,
const char *  sql 
)

Prepares a SQL query on a statement.

Parameters
objThe ODBC object
stmtThe statement
sqlThe SQL query
Note
This should be used in place of SQLPrepare

Definition at line 454 of file res_odbc.c.

455 {
456  if (obj->parent->logging) {
457  /* It is possible for this connection to be reused without being
458  * released back to the class, so we free what may already exist
459  * and place the new SQL in.
460  */
461  ast_free(obj->sql_text);
462  obj->sql_text = ast_strdup(sql);
464  }
465 
466  return SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
467 }
int prepares_executed
Definition: res_odbc.c:94

References ast_atomic_fetchadd_int(), ast_free, ast_strdup, odbc_class::logging, odbc_obj::parent, odbc_class::prepares_executed, and odbc_obj::sql_text.

Referenced by config_odbc_prepare(), custom_prepare(), generic_prepare(), length_determination_odbc_prepare(), and update2_prepare().

◆ ast_odbc_prepare_and_execute()

SQLHSTMT ast_odbc_prepare_and_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  prepare_cb,
void *  data 
)

Prepares, executes, and returns the resulting statement handle.

Parameters
objThe ODBC object
prepare_cbA function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound.
dataA parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared.
Returns
a statement handle
Return values
NULLon error

Definition at line 398 of file res_odbc.c.

399 {
400  struct timeval start;
401  int res = 0;
402  SQLHSTMT stmt;
403 
404  if (obj->parent->logging) {
405  start = ast_tvnow();
406  }
407 
408  /* This prepare callback may do more than just prepare -- it may also
409  * bind parameters, bind results, etc. The real key, here, is that
410  * when we disconnect, all handles become invalid for most databases.
411  * We must therefore redo everything when we establish a new
412  * connection. */
413  stmt = prepare_cb(obj, data);
414  if (!stmt) {
415  return NULL;
416  }
417 
418  res = SQLExecute(stmt);
419  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
420  if (res == SQL_ERROR) {
421  ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
422  }
423 
424  ast_log(LOG_WARNING, "SQL Execute error %d!\n", res);
425  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
426  stmt = NULL;
427  } else if (obj->parent->logging) {
428  long execution_time = ast_tvdiff_ms(ast_tvnow(), start);
429 
430  if (obj->parent->slowquerylimit && execution_time > obj->parent->slowquerylimit) {
431  ast_log(LOG_WARNING, "SQL query '%s' took %ld milliseconds to execute on class '%s', this may indicate a database problem\n",
432  obj->sql_text, execution_time, obj->parent->name);
433  }
434 
435  ast_mutex_lock(&obj->parent->lock);
436 
437  /* If this takes the record on longest query execution time, update the parent class
438  * with the information.
439  */
440  if (execution_time > obj->parent->longest_query_execution_time || !obj->parent->sql_text) {
441  obj->parent->longest_query_execution_time = execution_time;
442  ast_free(obj->parent->sql_text);
443  obj->parent->sql_text = obj->sql_text;
444  obj->sql_text = NULL;
445  }
446  ast_mutex_unlock(&obj->parent->lock);
447 
449  }
450 
451  return stmt;
452 }
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

References ast_atomic_fetchadd_int(), ast_free, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_odbc_print_errors(), ast_tvdiff_ms(), ast_tvnow(), odbc_class::lock, LOG_WARNING, odbc_class::logging, odbc_class::longest_query_execution_time, odbc_class::name, NULL, odbc_obj::parent, odbc_class::queries_executed, odbc_class::slowquerylimit, odbc_obj::sql_text, and odbc_class::sql_text.

Referenced by config_odbc(), destroy_odbc(), odbc_log(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), update2_odbc(), and update_odbc().

◆ ast_odbc_print_errors()

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.

Parameters
handle_typeThe type of SQL handle on which to gather diagnostics
handleThe SQL handle to gather diagnostics from
operationThe name of the failed operation.
Returns
The error string that was printed to the logs

Definition at line 515 of file res_odbc.c.

516 {
517  struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
518  SQLINTEGER nativeerror = 0;
519  SQLSMALLINT diagbytes = 0;
520  SQLSMALLINT i;
521  unsigned char state[10];
522  unsigned char diagnostic[256];
523 
524  ast_str_reset(errors);
525  i = 0;
526  while (SQLGetDiagRec(handle_type, handle, ++i, state, &nativeerror,
527  diagnostic, sizeof(diagnostic), &diagbytes) == SQL_SUCCESS) {
528  ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
529  ast_log(LOG_WARNING, "%s returned an error: %s: %s\n", operation, state, diagnostic);
530  /* XXX Why is this here? */
531  if (i > 10) {
532  ast_log(LOG_WARNING, "There are more than 10 diagnostic records! Ignore the rest.\n");
533  break;
534  }
535  }
536 
537  return errors;
538 }
static struct ast_threadstorage errors_buf
Definition: res_odbc.c:113
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1117
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:674
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:711
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:887
Support for dynamic strings.
Definition: strings.h:604

References ast_log, ast_str_append(), ast_str_reset(), ast_str_strlen(), ast_str_thread_get(), errors_buf, and LOG_WARNING.

Referenced by acf_transaction_write(), ast_odbc_prepare_and_execute(), ast_odbc_smart_execute(), commit_exec(), create_transaction(), custom_prepare(), release_transaction(), rollback_exec(), and update2_prepare().

◆ ast_odbc_release_obj()

void ast_odbc_release_obj ( struct odbc_obj obj)

Releases an ODBC object previously allocated by ast_odbc_request_obj()

Parameters
objThe ODBC object

Definition at line 804 of file res_odbc.c.

805 {
806  struct odbc_class *class = obj->parent;
807 
808  ast_debug(2, "Releasing ODBC handle %p into pool\n", obj);
809 
810  /* The odbc_obj only holds a reference to the class when it is
811  * actively being used. This guarantees no circular reference
812  * between odbc_class and odbc_obj. Since it is being released
813  * we also release our class reference. If a reload occurred before
814  * the class will go away automatically once all odbc_obj are
815  * released back.
816  */
817  obj->parent = NULL;
818 
819  /* Free the SQL text so that the next user of this connection has
820  * a fresh start.
821  */
822  ast_free(obj->sql_text);
823  obj->sql_text = NULL;
824 
825  ast_mutex_lock(&class->lock);
826  AST_LIST_INSERT_HEAD(&class->connections, obj, list);
827  ast_cond_signal(&class->cond);
828  ast_mutex_unlock(&class->lock);
829 
830  ao2_ref(class, -1);
831 }
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define ast_cond_signal(cond)
Definition: lock.h:201

References ao2_ref, ast_cond_signal, ast_debug, ast_free, AST_LIST_INSERT_HEAD, ast_mutex_lock, ast_mutex_unlock, NULL, odbc_obj::parent, and odbc_obj::sql_text.

Referenced by ast_odbc_find_table(), config_odbc(), create_transaction(), destroy_odbc(), dsn_destructor(), get_dsn(), load_config(), odbc_log(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), release_obj_or_dsn(), release_transaction(), store_odbc(), update2_odbc(), and update_odbc().

◆ ast_odbc_smart_execute()

int ast_odbc_smart_execute ( struct odbc_obj obj,
SQLHSTMT  stmt 
)

Executes a prepared statement handle.

Parameters
objThe non-NULL result of odbc_request_obj()
stmtThe prepared statement handle
Return values
0on success
-1on failure

This function was originally designed simply to execute a prepared statement handle and to retry if the initial execution failed. Unfortunately, it did this by disconnecting and reconnecting the database handle which on most databases causes the statement handle to become invalid. Therefore, this method has been deprecated in favor of odbc_prepare_and_execute() which allows the statement to be prepared multiple times, if necessary, in case of a loss of connection.

This function really only ever worked with MySQL, where the statement handle is not prepared on the server. If you are not using MySQL, you should avoid it.

Definition at line 480 of file res_odbc.c.

481 {
482  int res = 0;
483 
484  res = SQLExecute(stmt);
485  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
486  if (res == SQL_ERROR) {
487  ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
488  }
489  }
490 
491  if (obj->parent->logging) {
493  }
494 
495  return res;
496 }

References ast_atomic_fetchadd_int(), ast_odbc_print_errors(), odbc_class::logging, odbc_obj::parent, and odbc_class::queries_executed.

◆ ast_odbc_text2isolation()

int ast_odbc_text2isolation ( const char *  txt)

Convert from textual transaction isolation values to their numeric constants.

Definition at line 147 of file res_odbc.c.

148 {
149  if (strncasecmp(txt, "read_", 5) == 0) {
150  if (strncasecmp(txt + 5, "c", 1) == 0) {
151  return SQL_TXN_READ_COMMITTED;
152  } else if (strncasecmp(txt + 5, "u", 1) == 0) {
153  return SQL_TXN_READ_UNCOMMITTED;
154  } else {
155  return 0;
156  }
157  } else if (strncasecmp(txt, "ser", 3) == 0) {
158  return SQL_TXN_SERIALIZABLE;
159  } else if (strncasecmp(txt, "rep", 3) == 0) {
160  return SQL_TXN_REPEATABLE_READ;
161  } else {
162  return 0;
163  }
164 }

Referenced by acf_transaction_write(), and load_odbc_config().

◆ connection_dead()

static int connection_dead ( struct odbc_obj connection,
struct odbc_class class 
)
static

Determine if the connection has died.

Parameters
connectionThe connection to check
classThe ODBC class
Return values
1Yep, it's dead
0It's alive and well

Definition at line 872 of file res_odbc.c.

873 {
874  char *test_sql = "select 1";
875  SQLINTEGER dead;
876  SQLRETURN res;
877  SQLHSTMT stmt;
878 
879  res = SQLGetConnectAttr(connection->con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0);
880  if (SQL_SUCCEEDED(res)) {
881  return dead == SQL_CD_TRUE ? 1 : 0;
882  }
883 
884  /* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a
885  * probing query instead
886  */
887  res = SQLAllocHandle(SQL_HANDLE_STMT, connection->con, &stmt);
888  if (!SQL_SUCCEEDED(res)) {
889  return 1;
890  }
891 
892  if (!ast_strlen_zero(class->sanitysql)) {
893  test_sql = class->sanitysql;
894  }
895 
896  res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
897  if (!SQL_SUCCEEDED(res)) {
898  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
899  return 1;
900  }
901 
902  res = SQLExecute(stmt);
903  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
904 
905  return SQL_SUCCEEDED(res) ? 0 : 1;
906 }
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * sanitysql
Definition: res_odbc.c:71

References ast_strlen_zero(), and odbc_obj::con.

Referenced by _ast_odbc_request_obj2().

◆ destroy_table_cache()

static void destroy_table_cache ( struct odbc_cache_tables table)
static

Definition at line 201 of file res_odbc.c.

202 {
203  struct odbc_cache_columns *col;
204 
205  ast_debug(1, "Destroying table cache for %s\n", table->table);
206 
207  AST_RWLIST_WRLOCK(&table->columns);
208  while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
209  ast_free(col);
210  }
211  AST_RWLIST_UNLOCK(&table->columns);
212  AST_RWLIST_HEAD_DESTROY(&table->columns);
213 
214  ast_free(table);
215 }
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
#define AST_RWLIST_HEAD_DESTROY(head)
Destroys an rwlist head structure.
Definition: linkedlists.h:667

References ast_debug, ast_free, AST_RWLIST_HEAD_DESTROY, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and table.

Referenced by ast_odbc_clear_cache(), ast_odbc_find_table(), and reload().

◆ handle_cli_odbc_show()

static char* handle_cli_odbc_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 707 of file res_odbc.c.

708 {
709  struct ao2_iterator aoi;
710  struct odbc_class *class;
711  int length = 0;
712  int which = 0;
713  char *ret = NULL;
714 
715  switch (cmd) {
716  case CLI_INIT:
717  e->command = "odbc show";
718  e->usage =
719  "Usage: odbc show [class]\n"
720  " List settings of a particular ODBC class or,\n"
721  " if not specified, all classes.\n";
722  return NULL;
723  case CLI_GENERATE:
724  if (a->pos != 2)
725  return NULL;
726  length = strlen(a->word);
728  while ((class = ao2_iterator_next(&aoi))) {
729  if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
730  ret = ast_strdup(class->name);
731  }
732  ao2_ref(class, -1);
733  if (ret) {
734  break;
735  }
736  }
737  ao2_iterator_destroy(&aoi);
738  if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
739  ret = ast_strdup("all");
740  }
741  return ret;
742  }
743 
744  ast_cli(a->fd, "\nODBC DSN Settings\n");
745  ast_cli(a->fd, "-----------------\n\n");
747  while ((class = ao2_iterator_next(&aoi))) {
748  if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
749  char timestr[80];
750  struct ast_tm tm;
751 
752  ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
753 
754  if (class->last_negative_connect.tv_sec > 0) {
755  ast_localtime(&class->last_negative_connect, &tm, NULL);
756  ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
757  ast_cli(a->fd, " Last fail connection attempt: %s\n", timestr);
758  }
759 
760  ast_cli(a->fd, " Number of active connections: %zd (out of %d)\n", class->connection_cnt, class->maxconnections);
761  ast_cli(a->fd, " Logging: %s\n", class->logging ? "Enabled" : "Disabled");
762  if (class->logging) {
763  ast_cli(a->fd, " Number of prepares executed: %d\n", class->prepares_executed);
764  ast_cli(a->fd, " Number of queries executed: %d\n", class->queries_executed);
765  ast_mutex_lock(&class->lock);
766  if (class->sql_text) {
767  ast_cli(a->fd, " Longest running SQL query: %s (%ld milliseconds)\n", class->sql_text, class->longest_query_execution_time);
768  }
769  ast_mutex_unlock(&class->lock);
770  }
771  ast_cli(a->fd, "\n");
772  }
773  ao2_ref(class, -1);
774  }
775  ao2_iterator_destroy(&aoi);
776 
777  return CLI_SUCCESS;
778 }
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define CLI_SUCCESS
Definition: cli.h:44
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
const char * name
static struct test_val a

◆ load_module()

static int load_module ( void  )
static

Definition at line 1116 of file res_odbc.c.

1117 {
1119  if (!class_container) {
1120  return AST_MODULE_LOAD_DECLINE;
1121  }
1122 
1123  if (load_odbc_config() == -1) {
1124  return AST_MODULE_LOAD_DECLINE;
1125  }
1126 
1129 
1130  return AST_MODULE_LOAD_SUCCESS;
1131 }
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
int ao2_match_by_addr(void *obj, void *arg, int flags)
A common ao2_callback is one that matches by address.
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct ast_cli_entry cli_odbc[]
Definition: res_odbc.c:780
static int load_odbc_config(void)
Definition: res_odbc.c:555
struct ast_module * self
Definition: module.h:342
#define ARRAY_LEN(a)
Definition: utils.h:661

◆ load_odbc_config()

static int load_odbc_config ( void  )
static

Definition at line 555 of file res_odbc.c.

556 {
557  static char *cfg = "res_odbc.conf";
558  struct ast_config *config;
559  struct ast_variable *v;
560  char *cat;
561  const char *dsn, *username, *password, *sanitysql;
562  int enabled, bse, conntimeout, forcecommit, isolation, maxconnections, logging, slowquerylimit;
563  struct timeval ncache = { 0, 0 };
564  int preconnect = 0, res = 0;
565  struct ast_flags config_flags = { 0 };
566 
567  struct odbc_class *new;
568 
569  config = ast_config_load(cfg, config_flags);
571  ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
572  return -1;
573  }
574  for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
575  if (!strcasecmp(cat, "ENV")) {
576  for (v = ast_variable_browse(config, cat); v; v = v->next) {
577  setenv(v->name, v->value, 1);
578  ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
579  }
580  } else {
581  /* Reset all to defaults for each class of odbc connections */
583  enabled = 1;
584  preconnect = 0;
585  bse = 1;
586  conntimeout = 10;
587  forcecommit = 0;
588  isolation = SQL_TXN_READ_COMMITTED;
589  maxconnections = 1;
590  logging = 0;
591  slowquerylimit = 5000;
592  for (v = ast_variable_browse(config, cat); v; v = v->next) {
593  if (!strcasecmp(v->name, "pooling") ||
594  !strncasecmp(v->name, "share", 5) ||
595  !strcasecmp(v->name, "limit") ||
596  !strcasecmp(v->name, "idlecheck")) {
597  ast_log(LOG_WARNING, "The 'pooling', 'shared_connections', 'limit', and 'idlecheck' options were replaced by 'max_connections'. See res_odbc.conf.sample.\n");
598  } else if (!strcasecmp(v->name, "enabled")) {
599  enabled = ast_true(v->value);
600  } else if (!strcasecmp(v->name, "pre-connect")) {
601  preconnect = ast_true(v->value);
602  } else if (!strcasecmp(v->name, "dsn")) {
603  dsn = v->value;
604  } else if (!strcasecmp(v->name, "username")) {
605  username = v->value;
606  } else if (!strcasecmp(v->name, "password")) {
607  password = v->value;
608  } else if (!strcasecmp(v->name, "sanitysql")) {
609  sanitysql = v->value;
610  } else if (!strcasecmp(v->name, "backslash_is_escape")) {
611  bse = ast_true(v->value);
612  } else if (!strcasecmp(v->name, "connect_timeout")) {
613  if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
614  ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
615  conntimeout = 10;
616  }
617  } else if (!strcasecmp(v->name, "negative_connection_cache")) {
618  double dncache;
619  if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
620  ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
621  /* 5 minutes sounds like a reasonable default */
622  ncache.tv_sec = 300;
623  ncache.tv_usec = 0;
624  } else {
625  ncache.tv_sec = (int)dncache;
626  ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
627  }
628  } else if (!strcasecmp(v->name, "forcecommit")) {
629  forcecommit = ast_true(v->value);
630  } else if (!strcasecmp(v->name, "isolation")) {
631  if ((isolation = ast_odbc_text2isolation(v->value)) == 0) {
632  ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
633  isolation = SQL_TXN_READ_COMMITTED;
634  }
635  } else if (!strcasecmp(v->name, "max_connections")) {
636  if (sscanf(v->value, "%30d", &maxconnections) != 1 || maxconnections < 1) {
637  ast_log(LOG_WARNING, "max_connections must be a positive integer\n");
638  maxconnections = 1;
639  }
640  } else if (!strcasecmp(v->name, "logging")) {
641  logging = ast_true(v->value);
642  } else if (!strcasecmp(v->name, "slow_query_limit")) {
643  if (sscanf(v->value, "%30d", &slowquerylimit) != 1) {
644  ast_log(LOG_WARNING, "slow_query_limit must be a positive integer\n");
645  slowquerylimit = 5000;
646  }
647  }
648  }
649 
650  if (enabled && !ast_strlen_zero(dsn)) {
651  new = ao2_alloc(sizeof(*new), odbc_class_destructor);
652 
653  if (!new) {
654  res = -1;
655  break;
656  }
657 
658  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
659  res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
660 
661  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
662  ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
663  ao2_ref(new, -1);
664  return res;
665  }
666 
667  new->backslash_is_escape = bse ? 1 : 0;
668  new->forcecommit = forcecommit ? 1 : 0;
669  new->isolation = isolation;
670  new->conntimeout = conntimeout;
671  new->negative_connection_cache = ncache;
672  new->maxconnections = maxconnections;
673  new->logging = logging;
674  new->slowquerylimit = slowquerylimit;
675 
676  if (cat)
677  ast_copy_string(new->name, cat, sizeof(new->name));
678  if (dsn)
679  ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
680  if (username && !(new->username = ast_strdup(username))) {
681  ao2_ref(new, -1);
682  break;
683  }
684  if (password && !(new->password = ast_strdup(password))) {
685  ao2_ref(new, -1);
686  break;
687  }
688  if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
689  ao2_ref(new, -1);
690  break;
691  }
692 
693  ast_mutex_init(&new->lock);
694  ast_cond_init(&new->cond, NULL);
695 
696  odbc_register_class(new, preconnect);
697  ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
698  ao2_ref(new, -1);
699  new = NULL;
700  }
701  }
702  }
704  return res;
705 }
static char * dsn
Definition: cdr_odbc.c:55
static const char config[]
Definition: chan_ooh323.c:111
static int enabled
Definition: dnsmgr.c:91
int setenv(const char *name, const char *value, int overwrite)
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3327
#define CONFIG_STATUS_FILEMISSING
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define LOG_NOTICE
#define ast_cond_init(cond, attr)
Definition: lock.h:199
#define ast_mutex_init(pmutex)
Definition: lock.h:184
static void odbc_register_class(struct odbc_class *class, int connect)
Definition: res_odbc.c:784
static void odbc_class_destructor(void *data)
Definition: res_odbc.c:166
int ast_odbc_text2isolation(const char *txt)
Convert from textual transaction isolation values to their numeric constants.
Definition: res_odbc.c:147
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: main/utils.c:2097
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:406
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Data source name.
Definition: func_odbc.c:163
char * password
Definition: res_odbc.c:70
unsigned int maxconnections
Definition: res_odbc.c:78
unsigned int isolation
Definition: res_odbc.c:76
char * username
Definition: res_odbc.c:69
unsigned int conntimeout
Definition: res_odbc.c:77
unsigned int forcecommit
Definition: res_odbc.c:75

References ao2_alloc, ao2_ref, ast_category_browse(), ast_cond_init, ast_config_destroy(), ast_config_load, ast_copy_string(), ast_log, ast_mutex_init, ast_odbc_text2isolation(), ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), config, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, odbc_class::conntimeout, dsn, enabled, odbc_class::forcecommit, odbc_class::isolation, LOG_ERROR, LOG_NOTICE, LOG_WARNING, odbc_class::logging, odbc_class::maxconnections, ast_variable::name, ast_variable::next, NULL, odbc_class_destructor(), odbc_register_class(), odbc_class::password, odbc_class::sanitysql, setenv(), odbc_class::slowquerylimit, odbc_class::username, and ast_variable::value.

Referenced by reload().

◆ odbc_class_destructor()

static void odbc_class_destructor ( void *  data)
static

Definition at line 166 of file res_odbc.c.

167 {
168  struct odbc_class *class = data;
169  struct odbc_obj *obj;
170 
171  /* Due to refcounts, we can safely assume that any objects with a reference
172  * to us will prevent our destruction, so we don't need to worry about them.
173  */
174  if (class->username) {
175  ast_free(class->username);
176  }
177  if (class->password) {
178  ast_free(class->password);
179  }
180  if (class->sanitysql) {
181  ast_free(class->sanitysql);
182  }
183 
184  while ((obj = AST_LIST_REMOVE_HEAD(&class->connections, list))) {
185  ao2_ref(obj, -1);
186  }
187 
188  SQLFreeHandle(SQL_HANDLE_ENV, class->env);
189  ast_mutex_destroy(&class->lock);
190  ast_cond_destroy(&class->cond);
191  ast_free(class->sql_text);
192 }
#define ast_cond_destroy(cond)
Definition: lock.h:200
#define ast_mutex_destroy(a)
Definition: lock.h:186

References ao2_ref, ast_cond_destroy, ast_free, AST_LIST_REMOVE_HEAD, ast_mutex_destroy, and odbc_obj::list.

Referenced by load_odbc_config().

◆ odbc_obj_connect()

static odbc_status odbc_obj_connect ( struct odbc_obj obj)
static

Definition at line 1013 of file res_odbc.c.

1014 {
1015  int res;
1016  SQLINTEGER err;
1017  short int mlen;
1018  unsigned char msg[200], state[10];
1019 #ifdef NEEDTRACE
1020  SQLINTEGER enable = 1;
1021  char *tracefile = "/tmp/odbc.trace";
1022 #endif
1023  SQLHDBC con;
1024  long int negative_cache_expiration;
1025 
1026  ast_assert(obj->con == NULL);
1027  ast_debug(3, "Connecting %s(%p)\n", obj->parent->name, obj);
1028 
1029  /* Dont connect while server is marked as unreachable via negative_connection_cache */
1030  negative_cache_expiration = obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec;
1031  if (time(NULL) < negative_cache_expiration) {
1032  char secs[AST_TIME_T_LEN];
1033  ast_time_t_to_string(negative_cache_expiration - time(NULL), secs, sizeof(secs));
1034  ast_log(LOG_WARNING, "Not connecting to %s. Negative connection cache for %s seconds\n", obj->parent->name, secs);
1035  return ODBC_FAIL;
1036  }
1037 
1038  res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
1039 
1040  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1041  ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
1043  return ODBC_FAIL;
1044  }
1045  SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
1046  SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
1047 #ifdef NEEDTRACE
1048  SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
1049  SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
1050 #endif
1051 
1052  res = SQLConnect(con,
1053  (SQLCHAR *) obj->parent->dsn, SQL_NTS,
1054  (SQLCHAR *) obj->parent->username, SQL_NTS,
1055  (SQLCHAR *) obj->parent->password, SQL_NTS);
1056 
1057  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1058  SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
1060  ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
1061  if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con)) != SQL_SUCCESS) {
1062  SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
1063  ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
1064  }
1065  return ODBC_FAIL;
1066  } else {
1067  ast_debug(3, "res_odbc: Connected to %s [%s (%p)]\n", obj->parent->name, obj->parent->dsn, obj);
1068  }
1069 
1070  obj->con = con;
1071  return ODBC_SUCCESS;
1072 }
@ ODBC_SUCCESS
Definition: res_odbc.h:36
char dsn[80]
Definition: res_odbc.c:68
struct timeval negative_connection_cache
Definition: res_odbc.c:80
SQLHENV env
Definition: res_odbc.c:72
struct timeval last_negative_connect
Definition: res_odbc.c:82
int ast_time_t_to_string(time_t time, char *buf, size_t length)
Converts to a string representation of a time_t as decimal seconds since the epoch....
Definition: time.c:152
#define AST_TIME_T_LEN
Definition: time.h:43
#define ast_assert(a)
Definition: utils.h:734

References ast_assert, ast_debug, ast_log, AST_TIME_T_LEN, ast_time_t_to_string(), ast_tvnow(), odbc_obj::con, odbc_class::conntimeout, odbc_class::dsn, odbc_class::env, odbc_class::last_negative_connect, LOG_WARNING, odbc_class::name, odbc_class::negative_connection_cache, NULL, ODBC_FAIL, ODBC_SUCCESS, odbc_obj::parent, odbc_class::password, and odbc_class::username.

Referenced by _ast_odbc_request_obj2().

◆ odbc_obj_destructor()

static void odbc_obj_destructor ( void *  data)
static

Definition at line 194 of file res_odbc.c.

195 {
196  struct odbc_obj *obj = data;
197 
198  odbc_obj_disconnect(obj);
199 }
static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
Definition: res_odbc.c:986

References odbc_obj_disconnect().

Referenced by _ast_odbc_request_obj2().

◆ odbc_obj_disconnect()

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj)
static

Definition at line 986 of file res_odbc.c.

987 {
988  int res;
989  SQLINTEGER err;
990  short int mlen;
991  unsigned char msg[200], state[10];
992  SQLHDBC con;
993 
994  /* Nothing to disconnect */
995  if (!obj->con) {
996  return ODBC_SUCCESS;
997  }
998 
999  con = obj->con;
1000  obj->con = NULL;
1001  res = SQLDisconnect(con);
1002 
1003  if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con)) == SQL_SUCCESS) {
1004  ast_debug(3, "Database handle %p (connection %p) deallocated\n", obj, con);
1005  } else {
1006  SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
1007  ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
1008  }
1009 
1010  return ODBC_SUCCESS;
1011 }

References ast_debug, ast_log, odbc_obj::con, LOG_WARNING, NULL, and ODBC_SUCCESS.

Referenced by odbc_obj_destructor().

◆ odbc_register_class()

static void odbc_register_class ( struct odbc_class class,
int  connect 
)
static

Definition at line 784 of file res_odbc.c.

785 {
786  struct odbc_obj *obj;
787 
788  ao2_link(class_container, class);
789  /* I still have a reference in the caller, so a deref is NOT missing here. */
790 
791  if (!preconnect) {
792  return;
793  }
794 
795  /* Request and release builds a connection */
796  obj = ast_odbc_request_obj(class->name, 0);
797  if (obj) {
799  }
800 
801  return;
802 }
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532

References ao2_link, ast_odbc_release_obj(), ast_odbc_request_obj, and class_container.

Referenced by load_odbc_config().

◆ reload()

static int reload ( void  )
static

Definition at line 1074 of file res_odbc.c.

1075 {
1076  struct odbc_cache_tables *table;
1077  struct odbc_class *class;
1079 
1080  /* First, mark all to be purged */
1081  while ((class = ao2_iterator_next(&aoi))) {
1082  class->delme = 1;
1083  ao2_ref(class, -1);
1084  }
1085  ao2_iterator_destroy(&aoi);
1086 
1087  load_odbc_config();
1088 
1090  while ((class = ao2_iterator_next(&aoi))) {
1091  if (class->delme) {
1092  ao2_unlink(class_container, class);
1093  }
1094  ao2_ref(class, -1);
1095  }
1096  ao2_iterator_destroy(&aoi);
1097 
1098  /* Empty the cache; it will get rebuilt the next time the tables are needed. */
1100  while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
1102  }
1104 
1105  return 0;
1106 }
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, class_container, destroy_table_cache(), load_odbc_config(), and table.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1108 of file res_odbc.c.

1109 {
1112 
1113  return 0;
1114 }
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30

References ao2_cleanup, ARRAY_LEN, ast_cli_unregister_multiple(), class_container, and cli_odbc.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DEPEND, .requires = "res_odbc_transaction", }
static

Definition at line 1116 of file res_odbc.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1140 of file res_odbc.c.

◆ class_container

struct ao2_container* class_container
static

◆ cli_odbc

struct ast_cli_entry cli_odbc[]
static
Initial value:
= {
{ .handler = handle_cli_odbc_show , .summary = "List ODBC DSN(s)" ,}
}
static char * handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: res_odbc.c:707

Definition at line 707 of file res_odbc.c.

Referenced by unload_module().

◆ errors_buf

struct ast_threadstorage errors_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_errors_buf , .custom_init = NULL , }
static

Definition at line 113 of file res_odbc.c.

Referenced by ast_odbc_print_errors().

◆ odbc_tables

struct odbc_tables odbc_tables = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
static