Merge the realtime failover branch
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@278957 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
30dfe48aa7
commit
6bb04df2e6
1
CHANGES
1
CHANGES
|
@ -524,6 +524,7 @@ Miscellaneous
|
||||||
during device configuration.
|
during device configuration.
|
||||||
* The UNISTIM channel driver (chan_unistim) has been updated to support devices that
|
* The UNISTIM channel driver (chan_unistim) has been updated to support devices that
|
||||||
have less than 3 lines on the LCD.
|
have less than 3 lines on the LCD.
|
||||||
|
* Realtime now supports database failover. See the sample extconfig.conf for details.
|
||||||
|
|
||||||
CLI Changes
|
CLI Changes
|
||||||
-----------
|
-----------
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
;
|
;
|
||||||
; Static configuration files:
|
; Static configuration files:
|
||||||
;
|
;
|
||||||
; file.conf => driver,database[,table]
|
; file.conf => driver,database[,table[,priority]]
|
||||||
;
|
;
|
||||||
; maps a particular configuration file to the given
|
; maps a particular configuration file to the given
|
||||||
; database driver, database and table (or uses the
|
; database driver, database and table (or uses the
|
||||||
|
@ -40,14 +40,26 @@
|
||||||
; database and table (or uses the name of
|
; database and table (or uses the name of
|
||||||
; the family if the table is not specified
|
; the family if the table is not specified
|
||||||
;
|
;
|
||||||
;example => odbc,asterisk,alttable
|
;example => odbc,asterisk,alttable,1
|
||||||
|
;example => mysql,asterisk,alttable,2
|
||||||
;example2 => ldap,"dc=oxymium,dc=net",example2
|
;example2 => ldap,"dc=oxymium,dc=net",example2
|
||||||
;
|
;
|
||||||
|
; Additionally, priorities are now supported for use as failover methods
|
||||||
|
; for retrieving realtime data. If one connection fails to retrieve any
|
||||||
|
; information, the next sequential priority will be tried next. This
|
||||||
|
; especially works well with ODBC connections, since res_odbc now caches
|
||||||
|
; when connection failures occur and prevents immediately retrying those
|
||||||
|
; connections until after a specified timeout. Note: priorities must
|
||||||
|
; start at 1 and be sequential (i.e. if you have only priorities 1, 2,
|
||||||
|
; and 4, then 4 will be ignored, because there is no 3).
|
||||||
|
;
|
||||||
; "odbc" is shown in the examples below, but is not the only valid realtime
|
; "odbc" is shown in the examples below, but is not the only valid realtime
|
||||||
; engine. There is:
|
; engine. There is:
|
||||||
; odbc ... res_config_odbc
|
; odbc ... res_config_odbc
|
||||||
; sqlite ... res_config_sqlite
|
; sqlite ... res_config_sqlite
|
||||||
; pgsql ... res_config_pgsql
|
; pgsql ... res_config_pgsql
|
||||||
|
; curl ... res_config_curl
|
||||||
|
; ldap ... res_config_ldap
|
||||||
;
|
;
|
||||||
;iaxusers => odbc,asterisk
|
;iaxusers => odbc,asterisk
|
||||||
;iaxpeers => odbc,asterisk
|
;iaxpeers => odbc,asterisk
|
||||||
|
|
|
@ -10,18 +10,69 @@
|
||||||
|
|
||||||
; All other sections are arbitrary names for database connections.
|
; All other sections are arbitrary names for database connections.
|
||||||
|
|
||||||
|
;
|
||||||
|
; The context name is what will be used in other configuration files, such
|
||||||
|
; as extconfig.conf and func_odbc.conf, to reference this connection.
|
||||||
[asterisk]
|
[asterisk]
|
||||||
|
;
|
||||||
|
; Permit disabling sections without needing to comment them out.
|
||||||
|
; If not specified, it is assumed the section is enabled.
|
||||||
enabled => no
|
enabled => no
|
||||||
|
;
|
||||||
|
; This value should match an entry in /etc/odbc.ini
|
||||||
|
; (or /usr/local/etc/odbc.ini, on FreeBSD and similar systems).
|
||||||
dsn => asterisk
|
dsn => asterisk
|
||||||
|
;
|
||||||
|
; Username for connecting to the database. The default user is "root".
|
||||||
;username => myuser
|
;username => myuser
|
||||||
|
;
|
||||||
|
; Password for authenticating the user to the database. The default
|
||||||
|
; password is blank.
|
||||||
;password => mypass
|
;password => mypass
|
||||||
|
;
|
||||||
|
; Build a connection at startup?
|
||||||
pre-connect => yes
|
pre-connect => yes
|
||||||
;
|
;
|
||||||
; What should we execute to ensure that our connection is still alive? The
|
; What should we execute to ensure that our connection is still alive? The
|
||||||
; statement should return a non-zero value in the first field of its first
|
; statement should return a non-zero value in the first field of its first
|
||||||
; record. The default is "select 1".
|
; record. The default is "select 1".
|
||||||
;sanitysql => select 1
|
;sanitysql => select 1
|
||||||
|
;
|
||||||
|
; On some databases, the connection times out and a reconnection will be
|
||||||
|
; necessary. This setting configures the amount of time a connection
|
||||||
|
; may sit idle (in seconds) before a reconnection will be attempted.
|
||||||
|
;idlecheck => 3600
|
||||||
|
;
|
||||||
|
; Should we use a single connection for all queries? Most databases will
|
||||||
|
; allow sharing the connection, though Sybase and MS SQL Server will not.
|
||||||
|
;share_connections => yes
|
||||||
|
;
|
||||||
|
; If we aren't sharing connections, what is the maximum number of connections
|
||||||
|
; that we should attempt?
|
||||||
|
;limit => 5
|
||||||
|
;
|
||||||
|
; When the channel is destroyed, should any uncommitted open transactions
|
||||||
|
; automatically be committed?
|
||||||
|
;forcecommit => no
|
||||||
|
;
|
||||||
|
; How should we perceive data in other transactions within the database?
|
||||||
|
; Possible values are read_uncommitted, read_committed, repeatable_read,
|
||||||
|
; and serializable. The default is read_committed.
|
||||||
|
;isolation => repeatable_read
|
||||||
|
;
|
||||||
|
; Is the backslash a native escape character? The default is yes, but for
|
||||||
|
; MS SQL Server, the answer is no.
|
||||||
|
;backslash_is_escape => yes
|
||||||
|
;
|
||||||
|
; How long (in seconds) should we attempt to connect before considering the
|
||||||
|
; connection dead? The default is 10 seconds, but you may wish to reduce it,
|
||||||
|
; to increase responsiveness.
|
||||||
|
;connect_timeout => 10
|
||||||
|
;
|
||||||
|
; When a connection fails, how long (in seconds) should we cache that
|
||||||
|
; information before we attempt another connection? This increases
|
||||||
|
; responsiveness, when a database resource is not working.
|
||||||
|
;negative_connection_cache => 300
|
||||||
|
|
||||||
[mysql2]
|
[mysql2]
|
||||||
enabled => no
|
enabled => no
|
||||||
|
@ -29,11 +80,6 @@ dsn => MySQL-asterisk
|
||||||
username => myuser
|
username => myuser
|
||||||
password => mypass
|
password => mypass
|
||||||
pre-connect => yes
|
pre-connect => yes
|
||||||
;
|
|
||||||
; On some databases, the connection times out and a reconnection will be
|
|
||||||
; necessary. This setting configures the amount of time a connection
|
|
||||||
; may sit idle (in seconds) before a reconnection will be attempted.
|
|
||||||
;idlecheck => 3600
|
|
||||||
|
|
||||||
; Certain servers, such as MS SQL Server and Sybase use the TDS protocol, which
|
; Certain servers, such as MS SQL Server and Sybase use the TDS protocol, which
|
||||||
; limits the number of active queries per connection to 1. By telling res_odbc
|
; limits the number of active queries per connection to 1. By telling res_odbc
|
||||||
|
@ -64,5 +110,12 @@ sanitysql => select count(*) from systables
|
||||||
; Server does not.
|
; Server does not.
|
||||||
backslash_is_escape => no
|
backslash_is_escape => no
|
||||||
|
|
||||||
|
;
|
||||||
|
; If you are having problems with concurrency, please read this note from the
|
||||||
|
; mailing lists, regarding UnixODBC:
|
||||||
|
;
|
||||||
|
; http://lists.digium.com/pipermail/asterisk-dev/2009-February/036539.html
|
||||||
|
;
|
||||||
|
; In summary, try setting "Threading=2" in the relevant section within your
|
||||||
|
; odbcinst.ini.
|
||||||
|
;
|
||||||
|
|
|
@ -39,6 +39,7 @@ typedef enum { ODBC_SUCCESS=0, ODBC_FAIL=-1} odbc_status;
|
||||||
enum {
|
enum {
|
||||||
RES_ODBC_SANITY_CHECK = (1 << 0),
|
RES_ODBC_SANITY_CHECK = (1 << 0),
|
||||||
RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1),
|
RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1),
|
||||||
|
RES_ODBC_CONNECTED = (1 << 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief ODBC container */
|
/*! \brief ODBC container */
|
||||||
|
@ -59,6 +60,7 @@ struct odbc_obj {
|
||||||
AST_LIST_ENTRY(odbc_obj) list;
|
AST_LIST_ENTRY(odbc_obj) list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!\brief These structures are used for adaptive capabilities */
|
||||||
struct odbc_cache_columns {
|
struct odbc_cache_columns {
|
||||||
char *name;
|
char *name;
|
||||||
SQLSMALLINT type;
|
SQLSMALLINT type;
|
||||||
|
@ -99,17 +101,20 @@ struct odbc_cache_tables {
|
||||||
*/
|
*/
|
||||||
int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) __attribute__((deprecated));
|
int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) __attribute__((deprecated));
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Retrieves a connected ODBC object
|
* \brief Retrieves a connected ODBC object
|
||||||
* \param name The name of the ODBC class for which a connection is needed.
|
* \param name The name of the ODBC class for which a connection is needed.
|
||||||
* \param flags Set of flags used to control which connection is returned.
|
* \param flags One or more of the following flags:
|
||||||
* \retval ODBC object
|
* \li RES_ODBC_SANITY_CHECK Whether to ensure that a connection is valid before returning the handle. Usually unnecessary.
|
||||||
* \retval NULL if there is no connection available with the requested name.
|
* \li RES_ODBC_INDEPENDENT_CONNECTION Return a handle which is independent from all others. Usually used when starting a transaction.
|
||||||
|
* \li RES_ODBC_CONNECTED Only return a connected handle. Intended for use with peers which use idlecheck, which are checked periodically for reachability.
|
||||||
|
* \return ODBC object
|
||||||
|
* \retval NULL if there is no connection available with the requested name.
|
||||||
*
|
*
|
||||||
* Connection classes may, in fact, contain multiple connection handles. If
|
* Connection classes may, in fact, contain multiple connection handles. If
|
||||||
* the connection is pooled, then each connection will be dedicated to the
|
* the connection is pooled, then each connection will be dedicated to the
|
||||||
* thread which requests it. Note that all connections should be released
|
* thread which requests it. Note that all connections should be released
|
||||||
* when the thread is done by calling odbc_release_obj(), below.
|
* when the thread is done by calling ast_odbc_release_obj(), below.
|
||||||
*/
|
*/
|
||||||
struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, 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);
|
||||||
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_obj(const char *name, int check, const char *file, const char *function, int lineno);
|
||||||
|
@ -130,7 +135,7 @@ struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *
|
||||||
struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname);
|
struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Releases an ODBC object previously allocated by odbc_request_obj()
|
* \brief Releases an ODBC object previously allocated by ast_odbc_request_obj()
|
||||||
* \param obj The ODBC object
|
* \param obj The ODBC object
|
||||||
*/
|
*/
|
||||||
void ast_odbc_release_obj(struct odbc_obj *obj);
|
void ast_odbc_release_obj(struct odbc_obj *obj);
|
||||||
|
@ -175,7 +180,9 @@ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_c
|
||||||
* \param tablename Tablename to describe
|
* \param tablename Tablename to describe
|
||||||
* \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
|
* \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
|
||||||
* When a structure is returned, the contained columns list will be
|
* When a structure is returned, the contained columns list will be
|
||||||
* rdlock'ed, to ensure that it will be retained in memory.
|
* rdlock'ed, to ensure that it will be retained in memory. The information
|
||||||
|
* will be cached until a reload event or when ast_odbc_clear_cache() is called
|
||||||
|
* with the relevant parameters.
|
||||||
* \since 1.6.1
|
* \since 1.6.1
|
||||||
*/
|
*/
|
||||||
struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename);
|
struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename);
|
||||||
|
@ -191,6 +198,8 @@ struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table,
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Remove a cache entry from memory
|
* \brief 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.
|
||||||
* \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused)
|
* \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused)
|
||||||
* \param tablename Tablename for which a cached record should be removed
|
* \param tablename Tablename for which a cached record should be removed
|
||||||
* \retval 0 if the cache entry was removed, or -1 if no matching entry was found.
|
* \retval 0 if the cache entry was removed, or -1 if no matching entry was found.
|
||||||
|
|
143
main/config.c
143
main/config.c
|
@ -163,6 +163,7 @@ static int hashtab_compare_strings(void *a, void *b, int flags)
|
||||||
|
|
||||||
static struct ast_config_map {
|
static struct ast_config_map {
|
||||||
struct ast_config_map *next;
|
struct ast_config_map *next;
|
||||||
|
int priority;
|
||||||
char *name;
|
char *name;
|
||||||
char *driver;
|
char *driver;
|
||||||
char *database;
|
char *database;
|
||||||
|
@ -1858,7 +1859,7 @@ static void clear_config_maps(void)
|
||||||
ast_mutex_unlock(&config_lock);
|
ast_mutex_unlock(&config_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
|
static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
|
||||||
{
|
{
|
||||||
struct ast_config_map *map;
|
struct ast_config_map *map;
|
||||||
int length;
|
int length;
|
||||||
|
@ -1883,6 +1884,7 @@ static int append_mapping(const char *name, const char *driver, const char *data
|
||||||
map->table = map->database + strlen(map->database) + 1;
|
map->table = map->database + strlen(map->database) + 1;
|
||||||
strcpy(map->table, table);
|
strcpy(map->table, table);
|
||||||
}
|
}
|
||||||
|
map->priority = priority;
|
||||||
map->next = config_maps;
|
map->next = config_maps;
|
||||||
|
|
||||||
ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
|
ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
|
||||||
|
@ -1895,8 +1897,9 @@ int read_config_maps(void)
|
||||||
{
|
{
|
||||||
struct ast_config *config, *configtmp;
|
struct ast_config *config, *configtmp;
|
||||||
struct ast_variable *v;
|
struct ast_variable *v;
|
||||||
char *driver, *table, *database, *stringp, *tmp;
|
char *driver, *table, *database, *textpri, *stringp, *tmp;
|
||||||
struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
|
struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
|
||||||
|
int pri;
|
||||||
|
|
||||||
clear_config_maps();
|
clear_config_maps();
|
||||||
|
|
||||||
|
@ -1930,6 +1933,10 @@ int read_config_maps(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
table = strsep(&stringp, ",");
|
table = strsep(&stringp, ",");
|
||||||
|
textpri = strsep(&stringp, ",");
|
||||||
|
if (!textpri || !(pri = atoi(textpri))) {
|
||||||
|
pri = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(v->name, extconfig_conf)) {
|
if (!strcmp(v->name, extconfig_conf)) {
|
||||||
ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
|
ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
|
||||||
|
@ -1950,14 +1957,14 @@ int read_config_maps(void)
|
||||||
continue;
|
continue;
|
||||||
if (!strcasecmp(v->name, "sipfriends")) {
|
if (!strcasecmp(v->name, "sipfriends")) {
|
||||||
ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
|
ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
|
||||||
append_mapping("sipusers", driver, database, table ? table : "sipfriends");
|
append_mapping("sipusers", driver, database, table ? table : "sipfriends", pri);
|
||||||
append_mapping("sippeers", driver, database, table ? table : "sipfriends");
|
append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
|
||||||
} else if (!strcasecmp(v->name, "iaxfriends")) {
|
} else if (!strcasecmp(v->name, "iaxfriends")) {
|
||||||
ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
|
ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
|
||||||
append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
|
append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
|
||||||
append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
|
append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
|
||||||
} else
|
} else
|
||||||
append_mapping(v->name, driver, database, table);
|
append_mapping(v->name, driver, database, table, pri);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_config_destroy(config);
|
ast_config_destroy(config);
|
||||||
|
@ -2006,7 +2013,7 @@ int ast_config_engine_deregister(struct ast_config_engine *del)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Find realtime engine for realtime family */
|
/*! \brief Find realtime engine for realtime family */
|
||||||
static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
|
static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
|
||||||
{
|
{
|
||||||
struct ast_config_engine *eng, *ret = NULL;
|
struct ast_config_engine *eng, *ret = NULL;
|
||||||
struct ast_config_map *map;
|
struct ast_config_map *map;
|
||||||
|
@ -2014,7 +2021,7 @@ static struct ast_config_engine *find_engine(const char *family, char *database,
|
||||||
ast_mutex_lock(&config_lock);
|
ast_mutex_lock(&config_lock);
|
||||||
|
|
||||||
for (map = config_maps; map; map = map->next) {
|
for (map = config_maps; map; map = map->next) {
|
||||||
if (!strcasecmp(family, map->name)) {
|
if (!strcasecmp(family, map->name) && (priority == map->priority)) {
|
||||||
if (database)
|
if (database)
|
||||||
ast_copy_string(database, map->database, dbsiz);
|
ast_copy_string(database, map->database, dbsiz);
|
||||||
if (table)
|
if (table)
|
||||||
|
@ -2063,13 +2070,13 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
|
||||||
if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
|
if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
|
||||||
struct ast_config_engine *eng;
|
struct ast_config_engine *eng;
|
||||||
|
|
||||||
eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
|
eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
|
||||||
|
|
||||||
|
|
||||||
if (eng && eng->load_func) {
|
if (eng && eng->load_func) {
|
||||||
loader = eng;
|
loader = eng;
|
||||||
} else {
|
} else {
|
||||||
eng = find_engine("global", db, sizeof(db), table, sizeof(table));
|
eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
|
||||||
if (eng && eng->load_func)
|
if (eng && eng->load_func)
|
||||||
loader = eng;
|
loader = eng;
|
||||||
}
|
}
|
||||||
|
@ -2107,10 +2114,17 @@ static struct ast_variable *ast_load_realtime_helper(const char *family, va_list
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
struct ast_variable *res=NULL;
|
struct ast_variable *res=NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->realtime_func)
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->realtime_func(db, table, ap);
|
if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -2168,7 +2182,7 @@ int ast_check_realtime(const char *family)
|
||||||
return 0; /* There are no engines at all so fail early */
|
return 0; /* There are no engines at all so fail early */
|
||||||
}
|
}
|
||||||
|
|
||||||
eng = find_engine(family, NULL, 0, NULL, 0);
|
eng = find_engine(family, 1, NULL, 0, NULL, 0);
|
||||||
if (eng)
|
if (eng)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2186,12 +2200,18 @@ int ast_realtime_require_field(const char *family, ...)
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int res = -1;
|
int res = -1, i;
|
||||||
|
|
||||||
va_start(ap, family);
|
va_start(ap, family);
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->require_func) {
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->require_func(db, table, ap);
|
/* If the require succeeds, it returns 0. */
|
||||||
|
if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
|
@ -2203,11 +2223,17 @@ int ast_unload_realtime(const char *family)
|
||||||
struct ast_config_engine *eng;
|
struct ast_config_engine *eng;
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
int res = -1;
|
int res = -1, i;
|
||||||
|
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->unload_func) {
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->unload_func(db, table);
|
if (eng->unload_func) {
|
||||||
|
/* Do this for ALL engines */
|
||||||
|
res = eng->unload_func(db, table);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -2219,11 +2245,18 @@ struct ast_config *ast_load_realtime_multientry(const char *family, ...)
|
||||||
char table[256];
|
char table[256];
|
||||||
struct ast_config *res = NULL;
|
struct ast_config *res = NULL;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
int i;
|
||||||
|
|
||||||
va_start(ap, family);
|
va_start(ap, family);
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->realtime_multi_func)
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->realtime_multi_func(db, table, ap);
|
if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -2232,15 +2265,22 @@ struct ast_config *ast_load_realtime_multientry(const char *family, ...)
|
||||||
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
|
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
|
||||||
{
|
{
|
||||||
struct ast_config_engine *eng;
|
struct ast_config_engine *eng;
|
||||||
int res = -1;
|
int res = -1, i;
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, lookup);
|
va_start(ap, lookup);
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->update_func)
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->update_func(db, table, keyfield, lookup, ap);
|
/* If the update succeeds, it returns 0. */
|
||||||
|
if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -2249,15 +2289,21 @@ int ast_update_realtime(const char *family, const char *keyfield, const char *lo
|
||||||
int ast_update2_realtime(const char *family, ...)
|
int ast_update2_realtime(const char *family, ...)
|
||||||
{
|
{
|
||||||
struct ast_config_engine *eng;
|
struct ast_config_engine *eng;
|
||||||
int res = -1;
|
int res = -1, i;
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, family);
|
va_start(ap, family);
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->update2_func)
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->update2_func(db, table, ap);
|
if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -2266,15 +2312,22 @@ int ast_update2_realtime(const char *family, ...)
|
||||||
int ast_store_realtime(const char *family, ...)
|
int ast_store_realtime(const char *family, ...)
|
||||||
{
|
{
|
||||||
struct ast_config_engine *eng;
|
struct ast_config_engine *eng;
|
||||||
int res = -1;
|
int res = -1, i;
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, family);
|
va_start(ap, family);
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->store_func)
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->store_func(db, table, ap);
|
/* If the store succeeds, it returns 0. */
|
||||||
|
if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -2283,15 +2336,21 @@ int ast_store_realtime(const char *family, ...)
|
||||||
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
|
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
|
||||||
{
|
{
|
||||||
struct ast_config_engine *eng;
|
struct ast_config_engine *eng;
|
||||||
int res = -1;
|
int res = -1, i;
|
||||||
char db[256];
|
char db[256];
|
||||||
char table[256];
|
char table[256];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, lookup);
|
va_start(ap, lookup);
|
||||||
eng = find_engine(family, db, sizeof(db), table, sizeof(table));
|
for (i = 1; ; i++) {
|
||||||
if (eng && eng->destroy_func)
|
if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
|
||||||
res = eng->destroy_func(db, table, keyfield, lookup, ap);
|
if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -167,6 +167,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
|
||||||
SQLLEN indicator;
|
SQLLEN indicator;
|
||||||
va_list aq;
|
va_list aq;
|
||||||
struct custom_prepare_struct cps = { .sql = sql };
|
struct custom_prepare_struct cps = { .sql = sql };
|
||||||
|
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
|
||||||
|
|
||||||
if (ast_string_field_init(&cps, 256)) {
|
if (ast_string_field_init(&cps, 256)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -179,7 +180,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = ast_odbc_request_obj(database, 0);
|
obj = ast_odbc_request_obj2(database, connected_flag);
|
||||||
|
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
|
ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
|
||||||
|
@ -325,6 +326,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
|
||||||
struct ast_variable *var=NULL;
|
struct ast_variable *var=NULL;
|
||||||
struct ast_config *cfg=NULL;
|
struct ast_config *cfg=NULL;
|
||||||
struct ast_category *cat=NULL;
|
struct ast_category *cat=NULL;
|
||||||
|
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
|
||||||
SQLULEN colsize;
|
SQLULEN colsize;
|
||||||
SQLSMALLINT colcount=0;
|
SQLSMALLINT colcount=0;
|
||||||
SQLSMALLINT datatype;
|
SQLSMALLINT datatype;
|
||||||
|
@ -341,7 +343,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
|
||||||
va_copy(aq, ap);
|
va_copy(aq, ap);
|
||||||
|
|
||||||
|
|
||||||
obj = ast_odbc_request_obj(database, 0);
|
obj = ast_odbc_request_obj2(database, connected_flag);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
ast_string_field_free_memory(&cps);
|
ast_string_field_free_memory(&cps);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -479,6 +481,7 @@ static int update_odbc(const char *database, const char *table, const char *keyf
|
||||||
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
|
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
|
||||||
struct odbc_cache_tables *tableptr;
|
struct odbc_cache_tables *tableptr;
|
||||||
struct odbc_cache_columns *column;
|
struct odbc_cache_columns *column;
|
||||||
|
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -492,7 +495,7 @@ static int update_odbc(const char *database, const char *table, const char *keyf
|
||||||
}
|
}
|
||||||
|
|
||||||
tableptr = ast_odbc_find_table(database, table);
|
tableptr = ast_odbc_find_table(database, table);
|
||||||
if (!(obj = ast_odbc_request_obj(database, 0))) {
|
if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
|
||||||
ast_odbc_release_table(tableptr);
|
ast_odbc_release_table(tableptr);
|
||||||
ast_string_field_free_memory(&cps);
|
ast_string_field_free_memory(&cps);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -716,6 +719,7 @@ static int store_odbc(const char *database, const char *table, va_list ap)
|
||||||
int res;
|
int res;
|
||||||
va_list aq;
|
va_list aq;
|
||||||
struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
|
struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
|
||||||
|
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
|
||||||
|
|
||||||
va_copy(cps.ap, ap);
|
va_copy(cps.ap, ap);
|
||||||
va_copy(aq, ap);
|
va_copy(aq, ap);
|
||||||
|
@ -723,7 +727,7 @@ static int store_odbc(const char *database, const char *table, va_list ap)
|
||||||
if (!table)
|
if (!table)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
obj = ast_odbc_request_obj(database, 0);
|
obj = ast_odbc_request_obj2(database, connected_flag);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -790,6 +794,7 @@ static int destroy_odbc(const char *database, const char *table, const char *key
|
||||||
int res;
|
int res;
|
||||||
va_list aq;
|
va_list aq;
|
||||||
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
|
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
|
||||||
|
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
|
||||||
|
|
||||||
va_copy(cps.ap, ap);
|
va_copy(cps.ap, ap);
|
||||||
va_copy(aq, ap);
|
va_copy(aq, ap);
|
||||||
|
@ -797,7 +802,7 @@ static int destroy_odbc(const char *database, const char *table, const char *key
|
||||||
if (!table)
|
if (!table)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
obj = ast_odbc_request_obj(database, 0);
|
obj = ast_odbc_request_obj2(database, connected_flag);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -883,13 +888,14 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
|
||||||
char last[128] = "";
|
char last[128] = "";
|
||||||
struct config_odbc_obj q;
|
struct config_odbc_obj q;
|
||||||
struct ast_flags loader_flags = { 0 };
|
struct ast_flags loader_flags = { 0 };
|
||||||
|
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
|
||||||
|
|
||||||
memset(&q, 0, sizeof(q));
|
memset(&q, 0, sizeof(q));
|
||||||
|
|
||||||
if (!file || !strcmp (file, "res_config_odbc.conf"))
|
if (!file || !strcmp (file, "res_config_odbc.conf"))
|
||||||
return NULL; /* cant configure myself with myself ! */
|
return NULL; /* cant configure myself with myself ! */
|
||||||
|
|
||||||
obj = ast_odbc_request_obj(database, 0);
|
obj = ast_odbc_request_obj2(database, connected_flag);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,12 @@ struct odbc_class
|
||||||
unsigned int limit; /*!< Maximum number of database handles we will allow */
|
unsigned int limit; /*!< Maximum number of database handles we will allow */
|
||||||
int count; /*!< Running count of pooled connections */
|
int count; /*!< Running count of pooled connections */
|
||||||
unsigned int idlecheck; /*!< Recheck the connection if it is idle for this long (in seconds) */
|
unsigned int idlecheck; /*!< Recheck the connection if it is idle for this long (in seconds) */
|
||||||
|
unsigned int conntimeout; /*!< Maximum time the connection process should take */
|
||||||
|
/*! When a connection fails, cache that failure for how long? */
|
||||||
|
struct timeval negative_connection_cache;
|
||||||
|
/*! When a connection fails, when did that last occur? */
|
||||||
|
struct timeval last_negative_connect;
|
||||||
|
/*! List of handles associated with this class */
|
||||||
struct ao2_container *obj_container;
|
struct ao2_container *obj_container;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -661,7 +667,7 @@ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_c
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
|
int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
|
||||||
{
|
{
|
||||||
int res = 0, i;
|
int res = 0, i;
|
||||||
SQLINTEGER nativeerror=0, numfields=0;
|
SQLINTEGER nativeerror=0, numfields=0;
|
||||||
|
@ -681,9 +687,10 @@ int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
obj->last_used = ast_tvnow();
|
obj->last_used = ast_tvnow();
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +753,8 @@ static int load_odbc_config(void)
|
||||||
struct ast_variable *v;
|
struct ast_variable *v;
|
||||||
char *cat;
|
char *cat;
|
||||||
const char *dsn, *username, *password, *sanitysql;
|
const char *dsn, *username, *password, *sanitysql;
|
||||||
int enabled, pooling, limit, bse, forcecommit, isolation;
|
int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation;
|
||||||
|
struct timeval ncache = { 0, 0 };
|
||||||
unsigned int idlecheck;
|
unsigned int idlecheck;
|
||||||
int preconnect = 0, res = 0;
|
int preconnect = 0, res = 0;
|
||||||
struct ast_flags config_flags = { 0 };
|
struct ast_flags config_flags = { 0 };
|
||||||
|
@ -808,6 +816,22 @@ static int load_odbc_config(void)
|
||||||
sanitysql = v->value;
|
sanitysql = v->value;
|
||||||
} else if (!strcasecmp(v->name, "backslash_is_escape")) {
|
} else if (!strcasecmp(v->name, "backslash_is_escape")) {
|
||||||
bse = ast_true(v->value);
|
bse = ast_true(v->value);
|
||||||
|
} else if (!strcasecmp(v->name, "connect_timeout")) {
|
||||||
|
if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
|
||||||
|
ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
|
||||||
|
conntimeout = 10;
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(v->name, "negative_connection_cache")) {
|
||||||
|
double dncache;
|
||||||
|
if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
|
||||||
|
ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
|
||||||
|
/* 5 minutes sounds like a reasonable default */
|
||||||
|
ncache.tv_sec = 300;
|
||||||
|
ncache.tv_usec = 0;
|
||||||
|
} else {
|
||||||
|
ncache.tv_sec = (int)dncache;
|
||||||
|
ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
|
||||||
|
}
|
||||||
} else if (!strcasecmp(v->name, "forcecommit")) {
|
} else if (!strcasecmp(v->name, "forcecommit")) {
|
||||||
forcecommit = ast_true(v->value);
|
forcecommit = ast_true(v->value);
|
||||||
} else if (!strcasecmp(v->name, "isolation")) {
|
} else if (!strcasecmp(v->name, "isolation")) {
|
||||||
|
@ -851,6 +875,8 @@ static int load_odbc_config(void)
|
||||||
new->forcecommit = forcecommit ? 1 : 0;
|
new->forcecommit = forcecommit ? 1 : 0;
|
||||||
new->isolation = isolation;
|
new->isolation = isolation;
|
||||||
new->idlecheck = idlecheck;
|
new->idlecheck = idlecheck;
|
||||||
|
new->conntimeout = conntimeout;
|
||||||
|
new->negative_connection_cache = ncache;
|
||||||
|
|
||||||
if (cat)
|
if (cat)
|
||||||
ast_copy_string(new->name, cat, sizeof(new->name));
|
ast_copy_string(new->name, cat, sizeof(new->name));
|
||||||
|
@ -923,7 +949,13 @@ static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_c
|
||||||
while ((class = ao2_iterator_next(&aoi))) {
|
while ((class = ao2_iterator_next(&aoi))) {
|
||||||
if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
|
if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
char timestr[80];
|
||||||
|
struct ast_tm tm;
|
||||||
|
|
||||||
|
ast_localtime(&class->last_negative_connect, &tm, NULL);
|
||||||
|
ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
|
||||||
ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
|
ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
|
||||||
|
ast_cli(a->fd, " Last connection attempt: %s\n", timestr);
|
||||||
|
|
||||||
if (class->haspool) {
|
if (class->haspool) {
|
||||||
struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
|
struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
|
||||||
|
@ -1171,6 +1203,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
|
||||||
unsigned char state[10], diagnostic[256];
|
unsigned char state[10], diagnostic[256];
|
||||||
|
|
||||||
if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
|
if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
|
||||||
|
ast_debug(1, "Class not found!\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1183,11 +1216,13 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
|
||||||
if (obj) {
|
if (obj) {
|
||||||
ast_assert(ao2_ref(obj, 0) > 1);
|
ast_assert(ao2_ref(obj, 0) > 1);
|
||||||
}
|
}
|
||||||
|
if (!obj && (class->count < class->limit) &&
|
||||||
if (!obj && (class->count < class->limit)) {
|
(time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
|
||||||
obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
|
obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
class->count--;
|
||||||
ao2_ref(class, -1);
|
ao2_ref(class, -1);
|
||||||
|
ast_debug(3, "Unable to allocate object\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ast_assert(ao2_ref(obj, 0) == 1);
|
ast_assert(ao2_ref(obj, 0) == 1);
|
||||||
|
@ -1228,9 +1263,11 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
|
||||||
} else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
|
} else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
|
||||||
/* Non-pooled connections -- but must use a separate connection handle */
|
/* Non-pooled connections -- but must use a separate connection handle */
|
||||||
if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
|
if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
|
||||||
|
ast_debug(1, "Object not found\n");
|
||||||
obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
|
obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
ao2_ref(class, -1);
|
ao2_ref(class, -1);
|
||||||
|
ast_debug(3, "Unable to allocate object\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ast_mutex_init(&obj->lock);
|
ast_mutex_init(&obj->lock);
|
||||||
|
@ -1271,6 +1308,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
|
||||||
if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
|
if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
|
||||||
ast_assert(ao2_ref(class, 0) > 1);
|
ast_assert(ao2_ref(class, 0) > 1);
|
||||||
ao2_ref(class, -1);
|
ao2_ref(class, -1);
|
||||||
|
ast_debug(3, "Unable to allocate object\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ast_mutex_init(&obj->lock);
|
ast_mutex_init(&obj->lock);
|
||||||
|
@ -1313,10 +1351,16 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
|
if (obj && ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
|
||||||
|
/* Check if this connection qualifies for reconnection, with negative connection cache time */
|
||||||
|
if (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec) {
|
||||||
|
odbc_obj_connect(obj);
|
||||||
|
}
|
||||||
|
} else if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
|
||||||
ast_odbc_sanity_check(obj);
|
ast_odbc_sanity_check(obj);
|
||||||
} else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
|
} else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
|
||||||
odbc_obj_connect(obj);
|
odbc_obj_connect(obj);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_THREADS
|
#ifdef DEBUG_THREADS
|
||||||
if (obj) {
|
if (obj) {
|
||||||
|
@ -1431,11 +1475,12 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
|
||||||
|
|
||||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||||
ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
|
ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
|
||||||
|
obj->parent->last_negative_connect = ast_tvnow();
|
||||||
ast_mutex_unlock(&obj->lock);
|
ast_mutex_unlock(&obj->lock);
|
||||||
return ODBC_FAIL;
|
return ODBC_FAIL;
|
||||||
}
|
}
|
||||||
SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
|
SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) obj->parent->conntimeout, 0);
|
||||||
SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
|
SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) obj->parent->conntimeout, 0);
|
||||||
#ifdef NEEDTRACE
|
#ifdef NEEDTRACE
|
||||||
SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
|
SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
|
||||||
SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
|
SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
|
||||||
|
@ -1448,6 +1493,7 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
|
||||||
|
|
||||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||||
SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
|
SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
|
||||||
|
obj->parent->last_negative_connect = ast_tvnow();
|
||||||
ast_mutex_unlock(&obj->lock);
|
ast_mutex_unlock(&obj->lock);
|
||||||
ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
|
ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
|
||||||
return ODBC_FAIL;
|
return ODBC_FAIL;
|
||||||
|
|
Reference in New Issue