<p>Joshua C. Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/10965">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Kevin Harwell: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Joshua C. Colp: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_odbc: Add basic query logging.<br><br>When Asterisk is connected and used with a database the response<br>time of the database can cause problems in Asterisk if it is long.<br>Normally the only way to see this problem would be to retrieve a<br>backtrace from Asterisk and examine where things are blocked, or<br>examine the database to see if there is any indication of a<br>problem.<br><br>This change adds some basic query logging to make it easier to<br>investigate such a problem. When logging is enabled res_odbc will<br>now keep track of the number of queries executed, as well as the<br>query that has taken the longest time to execute. There is also<br>an option which will cause a WARNING message to be output if a<br>query takes longer than a configurable amount of time to execute.<br><br>This makes it easier and clearer for users that their database may<br>be experiencing a problem that could impact Asterisk.<br><br>ASTERISK-28277<br><br>Change-Id: I173cf4928b10754478a6a8c27dfa96ede0f058a6<br>---<br>M apps/app_voicemail.c<br>M cdr/cdr_adaptive_odbc.c<br>M cdr/cdr_odbc.c<br>M cel/cel_odbc.c<br>M configs/samples/res_odbc.conf.sample<br>M funcs/func_odbc.c<br>M include/asterisk/res_odbc.h<br>M res/res_config_odbc.c<br>M res/res_odbc.c<br>9 files changed, 168 insertions(+), 11 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c</span><br><span>index a2f6c36..6a29a20 100644</span><br><span>--- a/apps/app_voicemail.c</span><br><span>+++ b/apps/app_voicemail.c</span><br><span>@@ -3838,7 +3838,7 @@</span><br><span> ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");</span><br><span> return NULL;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, stmt, gps->sql);</span><br><span> if (!SQL_SUCCEEDED(res)) {</span><br><span> ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);</span><br><span> SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span>@@ -4361,7 +4361,7 @@</span><br><span> if (!ast_strlen_zero(data->category)) {</span><br><span> SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_execute_sql(obj, stmt, data->sql);</span><br><span> if (!SQL_SUCCEEDED(res)) {</span><br><span> ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");</span><br><span> SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span>diff --git a/cdr/cdr_adaptive_odbc.c b/cdr/cdr_adaptive_odbc.c</span><br><span>index 321a646..d810f9c 100644</span><br><span>--- a/cdr/cdr_adaptive_odbc.c</span><br><span>+++ b/cdr/cdr_adaptive_odbc.c</span><br><span>@@ -327,7 +327,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, stmt, data);</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data);</span><br><span> SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);</span><br><span>diff --git a/cdr/cdr_odbc.c b/cdr/cdr_odbc.c</span><br><span>index aed7499..455217a 100644</span><br><span>--- a/cdr/cdr_odbc.c</span><br><span>+++ b/cdr/cdr_odbc.c</span><br><span>@@ -155,7 +155,7 @@</span><br><span> SQLBindParameter(stmt, i + 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->sequence, 0, NULL);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ODBC_res = SQLExecDirect(stmt, (unsigned char *)sqlcmd, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ ODBC_res = ast_odbc_execute_sql(obj, stmt, sqlcmd);</span><br><span> </span><br><span> if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> ast_log(LOG_WARNING, "cdr_odbc: Error in ExecDirect: %d, query is: %s\n", ODBC_res, sqlcmd);</span><br><span>diff --git a/cel/cel_odbc.c b/cel/cel_odbc.c</span><br><span>index ec2feab..f6b6321 100644</span><br><span>--- a/cel/cel_odbc.c</span><br><span>+++ b/cel/cel_odbc.c</span><br><span>@@ -325,7 +325,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, stmt, sql);</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);</span><br><span> SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);</span><br><span>diff --git a/configs/samples/res_odbc.conf.sample b/configs/samples/res_odbc.conf.sample</span><br><span>index c69f556..58671fc 100644</span><br><span>--- a/configs/samples/res_odbc.conf.sample</span><br><span>+++ b/configs/samples/res_odbc.conf.sample</span><br><span>@@ -65,6 +65,20 @@</span><br><span> ; information before we attempt another connection? This increases</span><br><span> ; responsiveness, when a database resource is not working.</span><br><span> ;negative_connection_cache => 300</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; Enable query logging. This keeps track of the number of prepared queries</span><br><span style="color: hsl(120, 100%, 40%);">+; and executed queries as well as the query that has taken the longest to</span><br><span style="color: hsl(120, 100%, 40%);">+; execute. This can be useful for determining the latency with a database.</span><br><span style="color: hsl(120, 100%, 40%);">+; The data can be viewed using the "odbc show" CLI command.</span><br><span style="color: hsl(120, 100%, 40%);">+; Note that only successful queries are logged currently.</span><br><span style="color: hsl(120, 100%, 40%);">+;logging => yes</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; Slow query limit. If a query exceeds the given amount of time (in milliseconds)</span><br><span style="color: hsl(120, 100%, 40%);">+; when executing then a WARNING message will be output to indicate that there</span><br><span style="color: hsl(120, 100%, 40%);">+; may be a problem. Note that logging must be set to "yes" for this to occur. By</span><br><span style="color: hsl(120, 100%, 40%);">+; default this is set to 5000 milliseconds (or 5 seconds). If you would like to</span><br><span style="color: hsl(120, 100%, 40%);">+; disable the WARNING message it can be set to "0".</span><br><span style="color: hsl(120, 100%, 40%);">+;slow_query_limit => 5000</span><br><span> </span><br><span> [mysql2]</span><br><span> enabled => no</span><br><span>diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c</span><br><span>index 70f02b8..fc5fe2b 100644</span><br><span>--- a/funcs/func_odbc.c</span><br><span>+++ b/funcs/func_odbc.c</span><br><span>@@ -464,7 +464,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_execute_sql(obj, stmt, sql);</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {</span><br><span> if (res == SQL_ERROR && !silent) {</span><br><span> int i;</span><br><span>diff --git a/include/asterisk/res_odbc.h b/include/asterisk/res_odbc.h</span><br><span>index 4286272..9a38915 100644</span><br><span>--- a/include/asterisk/res_odbc.h</span><br><span>+++ b/include/asterisk/res_odbc.h</span><br><span>@@ -51,6 +51,7 @@</span><br><span> char function[80];</span><br><span> int lineno;</span><br><span> #endif</span><br><span style="color: hsl(120, 100%, 40%);">+ char *sql_text; /*!< The SQL text currently executing */</span><br><span> AST_LIST_ENTRY(odbc_obj) list;</span><br><span> };</span><br><span> </span><br><span>@@ -161,6 +162,22 @@</span><br><span> SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Prepares a SQL query on a statement.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The ODBC object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param stmt The statement</span><br><span style="color: hsl(120, 100%, 40%);">+ * \parma sql The SQL query</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This should be used in place of SQLPrepare</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Execute a nonprepared SQL query.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The ODBC object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param sql The SQL query</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This should be used in place of SQLExecDirect</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Find or create an entry describing the table specified.</span><br><span> * \param database Name of an ODBC class on which to query the table</span><br><span> * \param tablename Tablename to describe</span><br><span>diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c</span><br><span>index 8e54b47..a9c3495 100644</span><br><span>--- a/res/res_config_odbc.c</span><br><span>+++ b/res/res_config_odbc.c</span><br><span>@@ -116,7 +116,7 @@</span><br><span> </span><br><span> ast_debug(1, "Skip: %llu; SQL: %s\n", cps->skip, cps->sql);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, stmt, cps->sql);</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> if (res == SQL_ERROR) {</span><br><span> ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Prepare");</span><br><span>@@ -634,7 +634,7 @@</span><br><span> /* Done with the table metadata */</span><br><span> ast_odbc_release_table(tableptr);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, stmt, ast_str_buffer(sql));</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> if (res == SQL_ERROR) {</span><br><span> ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Prepare");</span><br><span>@@ -872,7 +872,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, sth, q->sql);</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> ast_verb(4, "Error in PREPARE %d\n", res);</span><br><span> SQLFreeHandle(SQL_HANDLE_STMT, sth);</span><br><span>@@ -896,7 +896,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_odbc_prepare(obj, sth, q->sql);</span><br><span> if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span> ast_verb(4, "Error in PREPARE %d\n", res);</span><br><span> SQLFreeHandle(SQL_HANDLE_STMT, sth);</span><br><span>diff --git a/res/res_odbc.c b/res/res_odbc.c</span><br><span>index cd588ee..5b79bd4 100644</span><br><span>--- a/res/res_odbc.c</span><br><span>+++ b/res/res_odbc.c</span><br><span>@@ -91,6 +91,18 @@</span><br><span> ast_cond_t cond;</span><br><span> /*! The total number of current connections */</span><br><span> size_t connection_cnt;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Whether logging is enabled on this class or not */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int logging;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The number of prepares executed on this class (total from all connections */</span><br><span style="color: hsl(120, 100%, 40%);">+ int prepares_executed;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The number of queries executed on this class (total from all connections) */</span><br><span style="color: hsl(120, 100%, 40%);">+ int queries_executed;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The longest execution time for a query executed on this class */</span><br><span style="color: hsl(120, 100%, 40%);">+ long longest_query_execution_time;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The SQL query that took the longest to execute */</span><br><span style="color: hsl(120, 100%, 40%);">+ char *sql_text;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Slow query limit (in milliseconds) */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int slowquerylimit;</span><br><span> };</span><br><span> </span><br><span> static struct ao2_container *class_container;</span><br><span>@@ -188,6 +200,7 @@</span><br><span> SQLFreeHandle(SQL_HANDLE_ENV, class->env);</span><br><span> ast_mutex_destroy(&class->lock);</span><br><span> ast_cond_destroy(&class->cond);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(class->sql_text);</span><br><span> }</span><br><span> </span><br><span> static void odbc_obj_destructor(void *data)</span><br><span>@@ -367,18 +380,52 @@</span><br><span> </span><br><span> SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;</span><br><span> SQLHSTMT stmt;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> stmt = exec_cb(obj, data);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ long execution_time = ast_tvdiff_ms(ast_tvnow(), start);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->slowquerylimit && execution_time > obj->parent->slowquerylimit) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SQL query '%s' took %ld milliseconds to execute on class '%s', this may indicate a database problem\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text, execution_time, obj->parent->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&obj->parent->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (execution_time > obj->parent->longest_query_execution_time || !obj->parent->sql_text) {</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->parent->longest_query_execution_time = execution_time;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Due to the callback nature of the res_odbc API it's not possible to ensure that</span><br><span style="color: hsl(120, 100%, 40%);">+ * the SQL text is removed from the connection in all cases, so only if it becomes the</span><br><span style="color: hsl(120, 100%, 40%);">+ * new longest executing query do we steal the SQL text. In other cases what will happen</span><br><span style="color: hsl(120, 100%, 40%);">+ * is that the SQL text will be freed if the connection is released back to the class or</span><br><span style="color: hsl(120, 100%, 40%);">+ * if a new query is done on the connection.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(obj->parent->sql_text);</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->parent->sql_text = obj->sql_text;</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&obj->parent->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return stmt;</span><br><span> }</span><br><span> </span><br><span> SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;</span><br><span> int res = 0;</span><br><span> SQLHSTMT stmt;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* This prepare callback may do more than just prepare -- it may also</span><br><span> * bind parameters, bind results, etc. The real key, here, is that</span><br><span> * when we disconnect, all handles become invalid for most databases.</span><br><span>@@ -398,11 +445,59 @@</span><br><span> ast_log(LOG_WARNING, "SQL Execute error %d!\n", res);</span><br><span> SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span> stmt = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ long execution_time = ast_tvdiff_ms(ast_tvnow(), start);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->slowquerylimit && execution_time > obj->parent->slowquerylimit) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SQL query '%s' took %ld milliseconds to execute on class '%s', this may indicate a database problem\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text, execution_time, obj->parent->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&obj->parent->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If this takes the record on longest query execution time, update the parent class</span><br><span style="color: hsl(120, 100%, 40%);">+ * with the information.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (execution_time > obj->parent->longest_query_execution_time || !obj->parent->sql_text) {</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->parent->longest_query_execution_time = execution_time;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(obj->parent->sql_text);</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->parent->sql_text = obj->sql_text;</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&obj->parent->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&obj->parent->queries_executed, +1);</span><br><span> }</span><br><span> </span><br><span> return stmt;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* It is possible for this connection to be reused without being</span><br><span style="color: hsl(120, 100%, 40%);">+ * released back to the class, so we free what may already exist</span><br><span style="color: hsl(120, 100%, 40%);">+ * and place the new SQL in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(obj->sql_text);</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text = ast_strdup(sql);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&obj->parent->prepares_executed, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(obj->sql_text);</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text = ast_strdup(sql);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&obj->parent->queries_executed, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)</span><br><span> {</span><br><span> int res = 0;</span><br><span>@@ -414,6 +509,10 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj->parent->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&obj->parent->queries_executed, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span>@@ -481,7 +580,7 @@</span><br><span> struct ast_variable *v;</span><br><span> char *cat;</span><br><span> const char *dsn, *username, *password, *sanitysql;</span><br><span style="color: hsl(0, 100%, 40%);">- int enabled, bse, conntimeout, forcecommit, isolation, maxconnections;</span><br><span style="color: hsl(120, 100%, 40%);">+ int enabled, bse, conntimeout, forcecommit, isolation, maxconnections, logging, slowquerylimit;</span><br><span> struct timeval ncache = { 0, 0 };</span><br><span> int preconnect = 0, res = 0;</span><br><span> struct ast_flags config_flags = { 0 };</span><br><span>@@ -509,6 +608,8 @@</span><br><span> forcecommit = 0;</span><br><span> isolation = SQL_TXN_READ_COMMITTED;</span><br><span> maxconnections = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ logging = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ slowquerylimit = 5000;</span><br><span> for (v = ast_variable_browse(config, cat); v; v = v->next) {</span><br><span> if (!strcasecmp(v->name, "pooling") ||</span><br><span> !strncasecmp(v->name, "share", 5) ||</span><br><span>@@ -557,6 +658,13 @@</span><br><span> ast_log(LOG_WARNING, "max_connections must be a positive integer\n");</span><br><span> maxconnections = 1;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcasecmp(v->name, "logging")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ logging = ast_true(v->value);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcasecmp(v->name, "slow_query_limit")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sscanf(v->value, "%30d", &slowquerylimit) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "slow_query_limit must be a positive integer\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ slowquerylimit = 5000;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> }</span><br><span> </span><br><span>@@ -583,6 +691,8 @@</span><br><span> new->conntimeout = conntimeout;</span><br><span> new->negative_connection_cache = ncache;</span><br><span> new->maxconnections = maxconnections;</span><br><span style="color: hsl(120, 100%, 40%);">+ new->logging = logging;</span><br><span style="color: hsl(120, 100%, 40%);">+ new->slowquerylimit = slowquerylimit;</span><br><span> </span><br><span> if (cat)</span><br><span> ast_copy_string(new->name, cat, sizeof(new->name));</span><br><span>@@ -665,6 +775,16 @@</span><br><span> ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);</span><br><span> ast_cli(a->fd, " Last connection attempt: %s\n", timestr);</span><br><span> ast_cli(a->fd, " Number of active connections: %zd (out of %d)\n", class->connection_cnt, class->maxconnections);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, " Logging: %s\n", class->logging ? "Enabled" : "Disabled");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (class->logging) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, " Number of prepares executed: %d\n", class->prepares_executed);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, " Number of queries executed: %d\n", class->queries_executed);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&class->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (class->sql_text) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, " Longest running SQL query: %s (%ld milliseconds)\n", class->sql_text, class->longest_query_execution_time);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&class->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> ast_cli(a->fd, "\n");</span><br><span> }</span><br><span> ao2_ref(class, -1);</span><br><span>@@ -713,6 +833,12 @@</span><br><span> */</span><br><span> obj->parent = NULL;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Free the SQL text so that the next user of this connection has</span><br><span style="color: hsl(120, 100%, 40%);">+ * a fresh start.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(obj->sql_text);</span><br><span style="color: hsl(120, 100%, 40%);">+ obj->sql_text = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ast_mutex_lock(&class->lock);</span><br><span> AST_LIST_INSERT_HEAD(&class->connections, obj, list);</span><br><span> ast_cond_signal(&class->cond);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/10965">change 10965</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/10965"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I173cf4928b10754478a6a8c27dfa96ede0f058a6 </div>
<div style="display:none"> Gerrit-Change-Number: 10965 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Joshua C. Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation (1000185) </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua C. Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>