mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 10:33:13 +00:00
Move the table cache routines to res_odbc, so they can be used from other
places (app_voicemail, for example). (Related to bug #11678) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@121683 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -142,4 +142,35 @@ SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struc
|
|||||||
*/
|
*/
|
||||||
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data);
|
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Find or create an entry describing the table specified.
|
||||||
|
* \param database Name of an ODBC class on which to query the table
|
||||||
|
* \param table Tablename to describe
|
||||||
|
* \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
|
||||||
|
* rdlock'ed, to ensure that it will be retained in memory.
|
||||||
|
*/
|
||||||
|
struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Find a column entry within a cached table structure
|
||||||
|
* \param table Cached table structure, as returned from ast_odbc_find_table()
|
||||||
|
* \param colname The column name requested
|
||||||
|
* \retval A structure describing the column type, or NULL, if the column is not found.
|
||||||
|
*/
|
||||||
|
struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Remove a cache entry from memory
|
||||||
|
* \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused)
|
||||||
|
* \param table 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.
|
||||||
|
*/
|
||||||
|
int ast_odbc_clear_cache(const char *database, const char *tablename);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Release a table returned from ast_odbc_find_table
|
||||||
|
*/
|
||||||
|
#define ast_odbc_release_table(ptr) if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }
|
||||||
|
|
||||||
#endif /* _ASTERISK_RES_ODBC_H */
|
#endif /* _ASTERISK_RES_ODBC_H */
|
||||||
|
@@ -54,153 +54,6 @@ struct custom_prepare_struct {
|
|||||||
unsigned long long skip;
|
unsigned long long skip;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!\brief The structures referenced are in include/asterisk/res_odbc.h */
|
|
||||||
static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
|
|
||||||
|
|
||||||
static void destroy_table_cache(struct odbc_cache_tables *table) {
|
|
||||||
struct odbc_cache_columns *col;
|
|
||||||
ast_debug(1, "Destroying table cache for %s\n", table->table);
|
|
||||||
AST_RWLIST_WRLOCK(&table->columns);
|
|
||||||
while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
|
|
||||||
ast_free(col);
|
|
||||||
}
|
|
||||||
AST_RWLIST_UNLOCK(&table->columns);
|
|
||||||
AST_RWLIST_HEAD_DESTROY(&table->columns);
|
|
||||||
ast_free(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define release_table(ptr) if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Find or create an entry describing the table specified.
|
|
||||||
* \param obj An active ODBC handle on which to query the table
|
|
||||||
* \param table Tablename to describe
|
|
||||||
* \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
|
|
||||||
* rdlock'ed, to ensure that it will be retained in memory.
|
|
||||||
*/
|
|
||||||
static struct odbc_cache_tables *find_table(const char *database, const char *tablename)
|
|
||||||
{
|
|
||||||
struct odbc_cache_tables *tableptr;
|
|
||||||
struct odbc_cache_columns *entry;
|
|
||||||
char columnname[80];
|
|
||||||
SQLLEN sqlptr;
|
|
||||||
SQLHSTMT stmt = NULL;
|
|
||||||
int res = 0, error = 0, try = 0;
|
|
||||||
struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
|
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&odbc_tables);
|
|
||||||
AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
|
|
||||||
if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tableptr) {
|
|
||||||
AST_RWLIST_RDLOCK(&tableptr->columns);
|
|
||||||
AST_RWLIST_UNLOCK(&odbc_tables);
|
|
||||||
return tableptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table structure not already cached; build it now. */
|
|
||||||
do {
|
|
||||||
retry:
|
|
||||||
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
|
|
||||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
|
||||||
if (try == 0) {
|
|
||||||
try = 1;
|
|
||||||
ast_odbc_sanity_check(obj);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
|
|
||||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
|
||||||
if (try == 0) {
|
|
||||||
try = 1;
|
|
||||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
||||||
ast_odbc_sanity_check(obj);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
|
|
||||||
ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
tableptr->connection = (char *)tableptr + sizeof(*tableptr);
|
|
||||||
tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
|
|
||||||
strcpy(tableptr->connection, database); /* SAFE */
|
|
||||||
strcpy(tableptr->table, tablename); /* SAFE */
|
|
||||||
AST_RWLIST_HEAD_INIT(&(tableptr->columns));
|
|
||||||
|
|
||||||
while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
|
|
||||||
SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
|
|
||||||
|
|
||||||
if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
|
|
||||||
ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
|
|
||||||
error = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
entry->name = (char *)entry + sizeof(*entry);
|
|
||||||
strcpy(entry->name, columnname);
|
|
||||||
|
|
||||||
SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
|
|
||||||
SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
|
|
||||||
SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
|
|
||||||
SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
|
|
||||||
SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
|
|
||||||
SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
|
|
||||||
|
|
||||||
/* Specification states that the octenlen should be the maximum number of bytes
|
|
||||||
* returned in a char or binary column, but it seems that some drivers just set
|
|
||||||
* it to NULL. (Bad Postgres! No biscuit!) */
|
|
||||||
if (entry->octetlen == 0) {
|
|
||||||
entry->octetlen = entry->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_verb(10, "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);
|
|
||||||
/* Insert column info into column list */
|
|
||||||
AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
|
|
||||||
}
|
|
||||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
|
||||||
|
|
||||||
AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
|
|
||||||
AST_RWLIST_RDLOCK(&(tableptr->columns));
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
AST_RWLIST_UNLOCK(&odbc_tables);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
destroy_table_cache(tableptr);
|
|
||||||
tableptr = NULL;
|
|
||||||
}
|
|
||||||
if (obj) {
|
|
||||||
ast_odbc_release_obj(obj);
|
|
||||||
}
|
|
||||||
return tableptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct odbc_cache_columns *find_column(struct odbc_cache_tables *table, const char *colname)
|
|
||||||
{
|
|
||||||
struct odbc_cache_columns *col;
|
|
||||||
AST_RWLIST_TRAVERSE(&table->columns, col, list) {
|
|
||||||
if (strcasecmp(col->name, colname) == 0) {
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
|
static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
|
||||||
{
|
{
|
||||||
int res, x = 1, count = 0;
|
int res, x = 1, count = 0;
|
||||||
@@ -552,38 +405,38 @@ static int update_odbc(const char *database, const char *table, const char *keyf
|
|||||||
int res, count = 0;
|
int res, count = 0;
|
||||||
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 odbc_cache_tables *tableptr = find_table(database, table);
|
struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
|
||||||
struct odbc_cache_columns *column;
|
struct odbc_cache_columns *column;
|
||||||
|
|
||||||
va_copy(cps.ap, ap);
|
va_copy(cps.ap, ap);
|
||||||
va_copy(aq, ap);
|
va_copy(aq, ap);
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
release_table(tableptr);
|
ast_odbc_release_table(tableptr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = ast_odbc_request_obj(database, 0);
|
obj = ast_odbc_request_obj(database, 0);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
release_table(tableptr);
|
ast_odbc_release_table(tableptr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
newparam = va_arg(aq, const char *);
|
newparam = va_arg(aq, const char *);
|
||||||
if (!newparam) {
|
if (!newparam) {
|
||||||
ast_odbc_release_obj(obj);
|
ast_odbc_release_obj(obj);
|
||||||
release_table(tableptr);
|
ast_odbc_release_table(tableptr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
newval = va_arg(aq, const char *);
|
newval = va_arg(aq, const char *);
|
||||||
|
|
||||||
if (tableptr && !(column = find_column(tableptr, newparam))) {
|
if (tableptr && !(column = ast_odbc_find_column(tableptr, newparam))) {
|
||||||
ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", newparam, table, database);
|
ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", newparam, table, database);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
|
snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
|
||||||
while((newparam = va_arg(aq, const char *))) {
|
while((newparam = va_arg(aq, const char *))) {
|
||||||
if ((tableptr && (column = find_column(tableptr, newparam))) || count > 63) {
|
if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
|
||||||
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
|
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
|
||||||
newval = va_arg(aq, const char *);
|
newval = va_arg(aq, const char *);
|
||||||
} else { /* the column does not exist in the table OR we've exceeded the space in our flag field */
|
} else { /* the column does not exist in the table OR we've exceeded the space in our flag field */
|
||||||
@@ -593,7 +446,7 @@ static int update_odbc(const char *database, const char *table, const char *keyf
|
|||||||
}
|
}
|
||||||
va_end(aq);
|
va_end(aq);
|
||||||
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
|
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
|
||||||
release_table(tableptr);
|
ast_odbc_release_table(tableptr);
|
||||||
|
|
||||||
stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
|
stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
|
||||||
|
|
||||||
@@ -883,7 +736,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
|
|||||||
|
|
||||||
static int require_odbc(const char *database, const char *table, va_list ap)
|
static int require_odbc(const char *database, const char *table, va_list ap)
|
||||||
{
|
{
|
||||||
struct odbc_cache_tables *tableptr = find_table(database, table);
|
struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
|
||||||
struct odbc_cache_columns *col;
|
struct odbc_cache_columns *col;
|
||||||
char *elm;
|
char *elm;
|
||||||
int type, size;
|
int type, size;
|
||||||
@@ -1034,23 +887,6 @@ static int require_odbc(const char *database, const char *table, va_list ap)
|
|||||||
#undef warn_length
|
#undef warn_length
|
||||||
#undef warn_type
|
#undef warn_type
|
||||||
|
|
||||||
static int unload_odbc(const char *database, const char *tablename)
|
|
||||||
{
|
|
||||||
struct odbc_cache_tables *tableptr;
|
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&odbc_tables);
|
|
||||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
|
|
||||||
if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
|
|
||||||
AST_LIST_REMOVE_CURRENT(list);
|
|
||||||
destroy_table_cache(tableptr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AST_RWLIST_TRAVERSE_SAFE_END
|
|
||||||
AST_RWLIST_UNLOCK(&odbc_tables);
|
|
||||||
return tableptr ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ast_config_engine odbc_engine = {
|
static struct ast_config_engine odbc_engine = {
|
||||||
.name = "odbc",
|
.name = "odbc",
|
||||||
.load_func = config_odbc,
|
.load_func = config_odbc,
|
||||||
@@ -1060,22 +896,13 @@ static struct ast_config_engine odbc_engine = {
|
|||||||
.destroy_func = destroy_odbc,
|
.destroy_func = destroy_odbc,
|
||||||
.update_func = update_odbc,
|
.update_func = update_odbc,
|
||||||
.require_func = require_odbc,
|
.require_func = require_odbc,
|
||||||
.unload_func = unload_odbc,
|
.unload_func = ast_odbc_clear_cache,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int unload_module (void)
|
static int unload_module (void)
|
||||||
{
|
{
|
||||||
struct odbc_cache_tables *table;
|
|
||||||
|
|
||||||
ast_config_engine_deregister(&odbc_engine);
|
ast_config_engine_deregister(&odbc_engine);
|
||||||
|
|
||||||
/* Empty the cache */
|
|
||||||
AST_RWLIST_WRLOCK(&odbc_tables);
|
|
||||||
while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
|
|
||||||
destroy_table_cache(table);
|
|
||||||
}
|
|
||||||
AST_RWLIST_UNLOCK(&odbc_tables);
|
|
||||||
|
|
||||||
ast_verb(1, "res_config_odbc unloaded.\n");
|
ast_verb(1, "res_config_odbc unloaded.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1089,15 +916,6 @@ static int load_module (void)
|
|||||||
|
|
||||||
static int reload_module(void)
|
static int reload_module(void)
|
||||||
{
|
{
|
||||||
struct odbc_cache_tables *table;
|
|
||||||
|
|
||||||
/* Empty the cache; it will get rebuilt the next time the tables are needed. */
|
|
||||||
AST_RWLIST_WRLOCK(&odbc_tables);
|
|
||||||
while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
|
|
||||||
destroy_table_cache(table);
|
|
||||||
}
|
|
||||||
AST_RWLIST_UNLOCK(&odbc_tables);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
169
res/res_odbc.c
169
res/res_odbc.c
@@ -69,6 +69,8 @@ struct odbc_class
|
|||||||
|
|
||||||
struct ao2_container *class_container;
|
struct ao2_container *class_container;
|
||||||
|
|
||||||
|
static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
|
||||||
|
|
||||||
static odbc_status odbc_obj_connect(struct odbc_obj *obj);
|
static odbc_status odbc_obj_connect(struct odbc_obj *obj);
|
||||||
static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
|
static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
|
||||||
static int odbc_register_class(struct odbc_class *class, int connect);
|
static int odbc_register_class(struct odbc_class *class, int connect);
|
||||||
@@ -102,6 +104,165 @@ static void odbc_obj_destructor(void *data)
|
|||||||
ao2_ref(obj->parent, -1);
|
ao2_ref(obj->parent, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void destroy_table_cache(struct odbc_cache_tables *table) {
|
||||||
|
struct odbc_cache_columns *col;
|
||||||
|
ast_debug(1, "Destroying table cache for %s\n", table->table);
|
||||||
|
AST_RWLIST_WRLOCK(&table->columns);
|
||||||
|
while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
|
||||||
|
ast_free(col);
|
||||||
|
}
|
||||||
|
AST_RWLIST_UNLOCK(&table->columns);
|
||||||
|
AST_RWLIST_HEAD_DESTROY(&table->columns);
|
||||||
|
ast_free(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Find or create an entry describing the table specified.
|
||||||
|
* \param obj An active ODBC handle on which to query the table
|
||||||
|
* \param table Tablename to describe
|
||||||
|
* \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
|
||||||
|
* rdlock'ed, to ensure that it will be retained in memory.
|
||||||
|
*/
|
||||||
|
struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename)
|
||||||
|
{
|
||||||
|
struct odbc_cache_tables *tableptr;
|
||||||
|
struct odbc_cache_columns *entry;
|
||||||
|
char columnname[80];
|
||||||
|
SQLLEN sqlptr;
|
||||||
|
SQLHSTMT stmt = NULL;
|
||||||
|
int res = 0, error = 0, try = 0;
|
||||||
|
struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
|
||||||
|
|
||||||
|
AST_RWLIST_RDLOCK(&odbc_tables);
|
||||||
|
AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
|
||||||
|
if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tableptr) {
|
||||||
|
AST_RWLIST_RDLOCK(&tableptr->columns);
|
||||||
|
AST_RWLIST_UNLOCK(&odbc_tables);
|
||||||
|
return tableptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table structure not already cached; build it now. */
|
||||||
|
do {
|
||||||
|
retry:
|
||||||
|
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
|
||||||
|
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||||
|
if (try == 0) {
|
||||||
|
try = 1;
|
||||||
|
ast_odbc_sanity_check(obj);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
|
||||||
|
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||||
|
if (try == 0) {
|
||||||
|
try = 1;
|
||||||
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||||
|
ast_odbc_sanity_check(obj);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
|
||||||
|
ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableptr->connection = (char *)tableptr + sizeof(*tableptr);
|
||||||
|
tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
|
||||||
|
strcpy(tableptr->connection, database); /* SAFE */
|
||||||
|
strcpy(tableptr->table, tablename); /* SAFE */
|
||||||
|
AST_RWLIST_HEAD_INIT(&(tableptr->columns));
|
||||||
|
|
||||||
|
while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
|
||||||
|
SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
|
||||||
|
|
||||||
|
if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
|
||||||
|
ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
|
||||||
|
error = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entry->name = (char *)entry + sizeof(*entry);
|
||||||
|
strcpy(entry->name, columnname);
|
||||||
|
|
||||||
|
SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
|
||||||
|
SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
|
||||||
|
SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
|
||||||
|
SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
|
||||||
|
SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
|
||||||
|
SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
|
||||||
|
|
||||||
|
/* Specification states that the octenlen should be the maximum number of bytes
|
||||||
|
* returned in a char or binary column, but it seems that some drivers just set
|
||||||
|
* it to NULL. (Bad Postgres! No biscuit!) */
|
||||||
|
if (entry->octetlen == 0) {
|
||||||
|
entry->octetlen = entry->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_verb(10, "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);
|
||||||
|
/* Insert column info into column list */
|
||||||
|
AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
|
||||||
|
}
|
||||||
|
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||||
|
|
||||||
|
AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
|
||||||
|
AST_RWLIST_RDLOCK(&(tableptr->columns));
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
AST_RWLIST_UNLOCK(&odbc_tables);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
destroy_table_cache(tableptr);
|
||||||
|
tableptr = NULL;
|
||||||
|
}
|
||||||
|
if (obj) {
|
||||||
|
ast_odbc_release_obj(obj);
|
||||||
|
}
|
||||||
|
return tableptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
|
||||||
|
{
|
||||||
|
struct odbc_cache_columns *col;
|
||||||
|
AST_RWLIST_TRAVERSE(&table->columns, col, list) {
|
||||||
|
if (strcasecmp(col->name, colname) == 0) {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ast_odbc_clear_cache(const char *database, const char *tablename)
|
||||||
|
{
|
||||||
|
struct odbc_cache_tables *tableptr;
|
||||||
|
|
||||||
|
AST_RWLIST_WRLOCK(&odbc_tables);
|
||||||
|
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
|
||||||
|
if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
|
||||||
|
AST_LIST_REMOVE_CURRENT(list);
|
||||||
|
destroy_table_cache(tableptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AST_RWLIST_TRAVERSE_SAFE_END
|
||||||
|
AST_RWLIST_UNLOCK(&odbc_tables);
|
||||||
|
return tableptr ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
|
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
|
||||||
{
|
{
|
||||||
int attempt;
|
int attempt;
|
||||||
@@ -636,6 +797,7 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
|
|||||||
|
|
||||||
static int reload(void)
|
static int reload(void)
|
||||||
{
|
{
|
||||||
|
struct odbc_cache_tables *table;
|
||||||
struct odbc_class *class;
|
struct odbc_class *class;
|
||||||
struct odbc_obj *current;
|
struct odbc_obj *current;
|
||||||
struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
|
struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
|
||||||
@@ -694,6 +856,13 @@ static int reload(void)
|
|||||||
ao2_ref(class, -1); /* C-ref-- (by iterator) */
|
ao2_ref(class, -1); /* C-ref-- (by iterator) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Empty the cache; it will get rebuilt the next time the tables are needed. */
|
||||||
|
AST_RWLIST_WRLOCK(&odbc_tables);
|
||||||
|
while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
|
||||||
|
destroy_table_cache(table);
|
||||||
|
}
|
||||||
|
AST_RWLIST_UNLOCK(&odbc_tables);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user