<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>