res_config_odbc.c: Fix buffer size limitation creating invalid SQL.

Creating ODBC SQL queries resulted in queries too large to fit into the
supplied buffer.  The resulting truncated buffer contained an invalid SQL
query.

* Made SQL query generation code use a thread storage buffer that can
increase in size as needed.

* Fixed bad multi-line warning messages.

ASTERISK-26263 #close
Reported by: Jeppe Ryskov Larsen

Change-Id: I23f3cdd43c2dac80bed3ded4dd77d18cb17f21ae
This commit is contained in:
Richard Mudgett
2016-09-12 18:00:22 -05:00
parent 95cf4f8d31
commit 2820b13393

View File

@@ -47,6 +47,9 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/stringfields.h" #include "asterisk/stringfields.h"
/*! Initial SQL query buffer size to allocate. */
#define SQL_BUF_SIZE 1024
AST_THREADSTORAGE(sql_buf); AST_THREADSTORAGE(sql_buf);
AST_THREADSTORAGE(rowdata_buf); AST_THREADSTORAGE(rowdata_buf);
@@ -114,7 +117,7 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS); res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql); ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", cps->sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt); SQLFreeHandle (SQL_HANDLE_STMT, stmt);
return NULL; return NULL;
} }
@@ -161,13 +164,13 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
* *
* \retval var on success * \retval var on success
* \retval NULL on failure * \retval NULL on failure
*/ */
static struct ast_variable *realtime_odbc(const char *database, const char *table, const struct ast_variable *fields) static struct ast_variable *realtime_odbc(const char *database, const char *table, const struct ast_variable *fields)
{ {
struct odbc_obj *obj; struct odbc_obj *obj;
SQLHSTMT stmt; SQLHSTMT stmt;
char sql[1024];
char coltitle[256]; char coltitle[256];
struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128); struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
char *op; char *op;
const struct ast_variable *field = fields; const struct ast_variable *field = fields;
@@ -183,29 +186,30 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
SQLSMALLINT decimaldigits; SQLSMALLINT decimaldigits;
SQLSMALLINT nullable; SQLSMALLINT nullable;
SQLLEN indicator; SQLLEN indicator;
struct custom_prepare_struct cps = { .sql = sql, .fields = fields, }; struct custom_prepare_struct cps = { .fields = fields, };
struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
if (!table || !field) { if (!table || !field || !sql || !rowdata) {
return NULL; return NULL;
} }
obj = ast_odbc_request_obj2(database, connected_flag); 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);
return NULL; return NULL;
} }
op = !strchr(field->name, ' ') ? " =" : ""; op = !strchr(field->name, ' ') ? " =" : "";
snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op, ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : ""); strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
while ((field = field->next)) { while ((field = field->next)) {
op = !strchr(field->name, ' ') ? " =" : ""; op = !strchr(field->name, ' ') ? " =" : "";
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", field->name, op, ast_str_append(&sql, 0, " AND %s%s ?%s", field->name, op,
strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : ""); strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
} }
cps.sql = ast_str_buffer(sql);
if (ast_string_field_init(&cps, 256)) { if (ast_string_field_init(&cps, 256)) {
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return NULL; return NULL;
@@ -220,7 +224,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
res = SQLNumResultCols(stmt, &colcount); res = SQLNumResultCols(stmt, &colcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Column Count error! [%s]\n", ast_str_buffer(sql));
SQLFreeHandle (SQL_HANDLE_STMT, stmt); SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return NULL; return NULL;
@@ -233,7 +237,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
return NULL; return NULL;
} }
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
SQLFreeHandle (SQL_HANDLE_STMT, stmt); SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return NULL; return NULL;
@@ -244,7 +248,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
&datatype, &colsize, &decimaldigits, &nullable); &datatype, &colsize, &decimaldigits, &nullable);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
if (var) if (var)
ast_variables_destroy(var); ast_variables_destroy(var);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
@@ -273,7 +277,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
} }
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
if (var) if (var)
ast_variables_destroy(var); ast_variables_destroy(var);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
@@ -317,13 +321,13 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
* *
* \retval var on success * \retval var on success
* \retval NULL on failure * \retval NULL on failure
*/ */
static struct ast_config *realtime_multi_odbc(const char *database, const char *table, const struct ast_variable *fields) static struct ast_config *realtime_multi_odbc(const char *database, const char *table, const struct ast_variable *fields)
{ {
struct odbc_obj *obj; struct odbc_obj *obj;
SQLHSTMT stmt; SQLHSTMT stmt;
char sql[1024];
char coltitle[256]; char coltitle[256];
struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128); struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
const char *initfield; const char *initfield;
char *op; char *op;
@@ -343,9 +347,9 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
SQLSMALLINT decimaldigits; SQLSMALLINT decimaldigits;
SQLSMALLINT nullable; SQLSMALLINT nullable;
SQLLEN indicator; SQLLEN indicator;
struct custom_prepare_struct cps = { .sql = sql, .fields = fields, }; struct custom_prepare_struct cps = { .fields = fields, };
if (!table || !field) { if (!table || !field || !sql || !rowdata) {
return NULL; return NULL;
} }
@@ -360,15 +364,16 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
} }
op = !strchr(field->name, ' ') ? " =" : ""; op = !strchr(field->name, ' ') ? " =" : "";
snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op, ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : ""); strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
while ((field = field->next)) { while ((field = field->next)) {
op = !strchr(field->name, ' ') ? " =" : ""; op = !strchr(field->name, ' ') ? " =" : "";
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", field->name, op, ast_str_append(&sql, 0, " AND %s%s ?%s", field->name, op,
strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : ""); strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
} }
ast_str_append(&sql, 0, " ORDER BY %s", initfield);
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); cps.sql = ast_str_buffer(sql);
if (ast_string_field_init(&cps, 256)) { if (ast_string_field_init(&cps, 256)) {
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
@@ -384,7 +389,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
res = SQLNumResultCols(stmt, &colcount); res = SQLNumResultCols(stmt, &colcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Column Count error! [%s]\n", ast_str_buffer(sql));
SQLFreeHandle(SQL_HANDLE_STMT, stmt); SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return NULL; return NULL;
@@ -401,7 +406,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
while ((res=SQLFetch(stmt)) != SQL_NO_DATA) { while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
var = NULL; var = NULL;
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
continue; continue;
} }
cat = ast_category_new("","",99999); cat = ast_category_new("","",99999);
@@ -415,7 +420,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
&datatype, &colsize, &decimaldigits, &nullable); &datatype, &colsize, &decimaldigits, &nullable);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Describe Column error! [%s]\n", ast_str_buffer(sql));
ast_category_destroy(cat); ast_category_destroy(cat);
goto next_sql_fetch; goto next_sql_fetch;
} }
@@ -440,7 +445,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
} }
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Get Data error! [%s]\n", ast_str_buffer(sql));
ast_category_destroy(cat); ast_category_destroy(cat);
goto next_sql_fetch; goto next_sql_fetch;
} }
@@ -482,21 +487,21 @@ next_sql_fetch:;
* *
* \retval number of rows affected * \retval number of rows affected
* \retval -1 on failure * \retval -1 on failure
*/ */
static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields) static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
{ {
struct odbc_obj *obj; struct odbc_obj *obj;
SQLHSTMT stmt; SQLHSTMT stmt;
char sql[256];
SQLLEN rowcount=0; SQLLEN rowcount=0;
struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
const struct ast_variable *field = fields; const struct ast_variable *field = fields;
int res, count = 0, paramcount = 0; int res, count = 0, paramcount = 0;
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup, .fields = fields, }; struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
struct odbc_cache_tables *tableptr; struct odbc_cache_tables *tableptr;
struct odbc_cache_columns *column = NULL; struct odbc_cache_columns *column = NULL;
struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
if (!table || !field || !keyfield) { if (!table || !field || !keyfield || !sql) {
return -1; return -1;
} }
@@ -510,19 +515,19 @@ static int update_odbc(const char *database, const char *table, const char *keyf
ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", keyfield, table, database); ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", keyfield, table, database);
} }
snprintf(sql, sizeof(sql), "UPDATE %s SET ", table); ast_str_set(&sql, 0, "UPDATE %s SET ", table);
while (field) { while (field) {
if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count >= 64) { if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count >= 64) {
if (paramcount++) { if (paramcount++) {
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", "); ast_str_append(&sql, 0, ", ");
} }
/* NULL test for non-text columns */ /* NULL test for non-text columns */
if (count < 64 && ast_strlen_zero(field->value) && column->nullable && !is_text(column)) { if (count < 64 && ast_strlen_zero(field->value) && column->nullable && !is_text(column)) {
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", field->name); ast_str_append(&sql, 0, "%s=NULL", field->name);
cps.skip |= (1LL << count); cps.skip |= (1LL << count);
} else { } else {
/* Value is not an empty string, or column is of text type, or we couldn't fit any more into cps.skip (count >= 64 ?!). */ /* Value is not an empty string, or column is of text type, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", field->name); ast_str_append(&sql, 0, "%s=?", field->name);
} }
} else { /* the column does not exist in the table */ } else { /* the column does not exist in the table */
cps.skip |= (1LL << count); cps.skip |= (1LL << count);
@@ -530,9 +535,11 @@ static int update_odbc(const char *database, const char *table, const char *keyf
++count; ++count;
field = field->next; field = field->next;
} }
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield); ast_str_append(&sql, 0, " WHERE %s=?", keyfield);
ast_odbc_release_table(tableptr); ast_odbc_release_table(tableptr);
cps.sql = ast_str_buffer(sql);
if (ast_string_field_init(&cps, 256)) { if (ast_string_field_init(&cps, 256)) {
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return -1; return -1;
@@ -550,7 +557,7 @@ static int update_odbc(const char *database, const char *table, const char *keyf
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
return -1; return -1;
} }
@@ -573,17 +580,15 @@ static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
int res, x = 1, first = 1; int res, x = 1, first = 1;
struct update2_prepare_struct *ups = data; struct update2_prepare_struct *ups = data;
const struct ast_variable *field; const struct ast_variable *field;
struct ast_str *sql = ast_str_thread_get(&sql_buf, 16); struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
SQLHSTMT stmt; SQLHSTMT stmt;
struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table); struct odbc_cache_tables *tableptr;
if (!sql) { if (!sql) {
if (tableptr) {
ast_odbc_release_table(tableptr);
}
return NULL; return NULL;
} }
tableptr = ast_odbc_find_table(ups->database, ups->table);
if (!tableptr) { if (!tableptr) {
ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database); ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database);
return NULL; return NULL;
@@ -628,7 +633,7 @@ static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS); res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql)); ast_log(LOG_WARNING, "SQL Prepare failed! [%s]\n", ast_str_buffer(sql));
SQLFreeHandle(SQL_HANDLE_STMT, stmt); SQLFreeHandle(SQL_HANDLE_STMT, stmt);
return NULL; return NULL;
} }
@@ -674,8 +679,9 @@ static int update2_odbc(const char *database, const char *table, const struct as
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
/* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */ /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
sql = ast_str_thread_get(&sql_buf, 16); sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql)); ast_assert(sql != NULL);
ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
return -1; return -1;
} }
@@ -698,36 +704,47 @@ static int update2_odbc(const char *database, const char *table, const struct as
* *
* \retval number of rows affected * \retval number of rows affected
* \retval -1 on failure * \retval -1 on failure
*/ */
static int store_odbc(const char *database, const char *table, const struct ast_variable *fields) static int store_odbc(const char *database, const char *table, const struct ast_variable *fields)
{ {
struct odbc_obj *obj; struct odbc_obj *obj;
SQLHSTMT stmt; SQLHSTMT stmt;
char sql[256];
char keys[256];
char vals[256];
SQLLEN rowcount=0; SQLLEN rowcount=0;
const struct ast_variable *field = fields; const struct ast_variable *field = fields;
struct ast_str *keys;
struct ast_str *vals;
struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
int res; int res;
struct custom_prepare_struct cps = { .sql = sql, .extra = NULL, .fields = fields, }; struct custom_prepare_struct cps = { .fields = fields, };
struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
if (!table || !field) { keys = ast_str_create(SQL_BUF_SIZE / 2);
vals = ast_str_create(SQL_BUF_SIZE / 4);
if (!table || !field || !keys || !vals || !sql) {
ast_free(vals);
ast_free(keys);
return -1; return -1;
} }
obj = ast_odbc_request_obj2(database, connected_flag); obj = ast_odbc_request_obj2(database, connected_flag);
if (!obj) { if (!obj) {
ast_free(vals);
ast_free(keys);
return -1; return -1;
} }
snprintf(keys, sizeof(keys), "%s", field->name); ast_str_set(&keys, 0, "%s", field->name);
ast_copy_string(vals, "?", sizeof(vals)); ast_str_set(&vals, 0, "?");
while ((field = field->next)) { while ((field = field->next)) {
snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", field->name); ast_str_append(&keys, 0, ", %s", field->name);
snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?"); ast_str_append(&vals, 0, ", ?");
} }
snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals); ast_str_set(&sql, 0, "INSERT INTO %s (%s) VALUES (%s)",
table, ast_str_buffer(keys), ast_str_buffer(vals));
ast_free(vals);
ast_free(keys);
cps.sql = ast_str_buffer(sql);
if (ast_string_field_init(&cps, 256)) { if (ast_string_field_init(&cps, 256)) {
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
@@ -746,7 +763,7 @@ static int store_odbc(const char *database, const char *table, const struct ast_
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
return -1; return -1;
} }
@@ -770,19 +787,19 @@ static int store_odbc(const char *database, const char *table, const struct ast_
* *
* \retval number of rows affected * \retval number of rows affected
* \retval -1 on failure * \retval -1 on failure
*/ */
static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields) static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
{ {
struct odbc_obj *obj; struct odbc_obj *obj;
SQLHSTMT stmt; SQLHSTMT stmt;
char sql[256];
SQLLEN rowcount=0; SQLLEN rowcount=0;
struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
const struct ast_variable *field; const struct ast_variable *field;
int res; int res;
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup, .fields = fields, }; struct custom_prepare_struct cps = { .extra = lookup, .fields = fields, };
struct ast_flags connected_flag = { RES_ODBC_CONNECTED }; struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
if (!table) { if (!table || !sql) {
return -1; return -1;
} }
@@ -791,12 +808,13 @@ static int destroy_odbc(const char *database, const char *table, const char *key
return -1; return -1;
} }
snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table); ast_str_set(&sql, 0, "DELETE FROM %s WHERE ", table);
for (field = fields; field; field = field->next) { for (field = fields; field; field = field->next) {
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", field->name); ast_str_append(&sql, 0, "%s=? AND ", field->name);
} }
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield); ast_str_append(&sql, 0, "%s=?", keyfield);
cps.sql = ast_str_buffer(sql);
if (ast_string_field_init(&cps, 256)) { if (ast_string_field_init(&cps, 256)) {
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
@@ -815,7 +833,7 @@ static int destroy_odbc(const char *database, const char *table, const char *key
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL Row Count error! [%s]\n", ast_str_buffer(sql));
return -1; return -1;
} }
@@ -893,9 +911,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
struct ast_category *cur_cat; struct ast_category *cur_cat;
int res = 0; int res = 0;
struct odbc_obj *obj; struct odbc_obj *obj;
char sqlbuf[1024] = ""; struct ast_str *sql = ast_str_thread_get(&sql_buf, SQL_BUF_SIZE);
char *sql = sqlbuf;
size_t sqlleft = sizeof(sqlbuf);
unsigned int last_cat_metric = 0; unsigned int last_cat_metric = 0;
SQLSMALLINT rowcount = 0; SQLSMALLINT rowcount = 0;
SQLHSTMT stmt; SQLHSTMT stmt;
@@ -906,21 +922,21 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
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") || !sql) {
return NULL; /* cant configure myself with myself ! */ return NULL; /* cant configure myself with myself ! */
}
obj = ast_odbc_request_obj2(database, connected_flag); obj = ast_odbc_request_obj2(database, connected_flag);
if (!obj) if (!obj)
return NULL; return NULL;
q.sql = sqlbuf; ast_str_set(&sql, 0, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'",
table, file);
ast_build_string(&sql, &sqlleft, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'", table, file); q.sql = ast_str_buffer(sql);
stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q); stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q);
if (!stmt) { if (!stmt) {
ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return NULL; return NULL;
} }
@@ -928,7 +944,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
res = SQLNumResultCols(stmt, &rowcount); res = SQLNumResultCols(stmt, &rowcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
SQLFreeHandle(SQL_HANDLE_STMT, stmt); SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
return NULL; return NULL;
@@ -950,12 +966,11 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
/* Reset stuff to a fresh state for the actual query which will retrieve all configuration */ /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */
SQLFreeHandle(SQL_HANDLE_STMT, stmt); SQLFreeHandle(SQL_HANDLE_STMT, stmt);
sql = sqlbuf;
sqlleft = sizeof(sqlbuf);
ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table); ast_str_set(&sql, 0, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file); ast_str_append(&sql, 0, "WHERE filename='%s' AND commented=0 ", file);
ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); ast_str_append(&sql, 0, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
q.sql = ast_str_buffer(sql);
q.var_val_size += 1; q.var_val_size += 1;
q.var_val = ast_malloc(q.var_val_size); q.var_val = ast_malloc(q.var_val_size);
@@ -966,9 +981,8 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
} }
stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q); stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
if (!stmt) { if (!stmt) {
ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL select error! [%s]\n", ast_str_buffer(sql));
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
ast_free(q.var_val); ast_free(q.var_val);
return NULL; return NULL;
@@ -977,7 +991,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
res = SQLNumResultCols(stmt, &rowcount); res = SQLNumResultCols(stmt, &rowcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql); ast_log(LOG_WARNING, "SQL NumResultCols error! [%s]\n", ast_str_buffer(sql));
SQLFreeHandle(SQL_HANDLE_STMT, stmt); SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj); ast_odbc_release_obj(obj);
ast_free(q.var_val); ast_free(q.var_val);