Add High Resolution Times to CDRs for Asterisk
[asterisk/asterisk.git] / addons / res_config_mysql.c
index 2cd1fa4..fc0d94e 100644 (file)
@@ -47,7 +47,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/threadstorage.h"
 
-#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
+#define RES_CONFIG_MYSQL_CONF "res_config_mysql.conf"
+#define RES_CONFIG_MYSQL_CONF_OLD "res_mysql.conf"
 #define        READHANDLE      0
 #define        WRITEHANDLE     1
 
@@ -206,7 +207,7 @@ static struct tables *find_table(const char *database, const char *tablename)
        }
 
        if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
-               ast_log(LOG_ERROR, "Failed to query database columns: %s\n", mysql_error(&dbh->handle));
+               ast_log(LOG_ERROR, "Failed to query database '%s', table '%s' columns: %s\n", database, tablename, mysql_error(&dbh->handle));
                release_database(dbh);
                AST_LIST_UNLOCK(&mysql_tables);
                return NULL;
@@ -236,7 +237,7 @@ static struct tables *find_table(const char *database, const char *tablename)
                        }
 
                        if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + strlen(fdflt) + 3))) {
-                               ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", tablename, fname);
+                               ast_log(LOG_ERROR, "Unable to allocate column element %s for %s\n", fname, tablename);
                                destroy_table(table);
                                release_database(dbh);
                                AST_LIST_UNLOCK(&mysql_tables);
@@ -244,7 +245,7 @@ static struct tables *find_table(const char *database, const char *tablename)
                        }
 
                        if ((flen = strchr(ftype, '('))) {
-                               sscanf(flen, "(%d)", &column->len);
+                               sscanf(flen, "(%30d)", &column->len);
                        } else {
                                /* Columns like dates, times, and timestamps don't have a length */
                                column->len = -1;
@@ -305,7 +306,7 @@ static struct ast_variable *realtime_mysql(const char *database, const char *tab
        struct ast_variable *var=NULL, *prev=NULL;
 
        if (!(dbh = find_database(database, 0))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: %s\n", database);
+               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: %s (check res_mysql.conf)\n", database);
                return NULL;
        }
 
@@ -366,12 +367,13 @@ static struct ast_variable *realtime_mysql(const char *database, const char *tab
 
                while ((row = mysql_fetch_row(result))) {
                        for (i = 0; i < numFields; i++) {
-                               if (ast_strlen_zero(row[i]))
-                                       continue;
+                               /* Encode NULL values separately from blank values, for the Realtime API */
+                               if (row[i] == NULL) {
+                                       row[i] = "";
+                               } else if (ast_strlen_zero(row[i])) {
+                                       row[i] = " ";
+                               }
                                for (stringp = ast_strdupa(row[i]), chunk = strsep(&stringp, ";"); chunk; chunk = strsep(&stringp, ";")) {
-                                       if (!chunk || ast_strlen_zero(ast_strip(chunk))) {
-                                               continue;
-                                       }
                                        if (prev) {
                                                if ((prev->next = ast_variable_new(fields[i].name, chunk, ""))) {
                                                        prev = prev->next;
@@ -411,7 +413,7 @@ static struct ast_config *realtime_multi_mysql(const char *database, const char
        struct ast_category *cat = NULL;
 
        if (!(dbh = find_database(database, 0))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s'\n", database);
+               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
                return NULL;
        }
 
@@ -530,7 +532,7 @@ static int update_mysql(const char *database, const char *tablename, const char
        struct columns *column = NULL;
 
        if (!(dbh = find_database(database, 1))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s'.\n", database);
+               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
                return -1;
        }
                
@@ -547,7 +549,7 @@ static int update_mysql(const char *database, const char *tablename, const char
        }
 
        if (!(column = find_column(table, keyfield))) {
-               ast_log(LOG_ERROR, "MySQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", keyfield, tablename);
+               ast_log(LOG_ERROR, "MySQL RealTime: Updating on column '%s', but that column does not exist within the table '%s' (db '%s')!\n", keyfield, tablename, database);
                release_table(table);
                release_database(dbh);
                return -1;
@@ -557,7 +559,7 @@ static int update_mysql(const char *database, const char *tablename, const char
        newparam = va_arg(ap, const char *);
        newval = va_arg(ap, const char *);
        if (!newparam || !newval)  {
-               ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
+               ast_log(LOG_WARNING, "MySQL RealTime: Realtime update requires at least 1 parameter and 1 value to update.\n");
                release_table(table);
                release_database(dbh);
                return -1;
@@ -565,7 +567,7 @@ static int update_mysql(const char *database, const char *tablename, const char
 
        /* Check that the column exists in the table */
        if (!(column = find_column(table, newparam))) {
-               ast_log(LOG_ERROR, "MySQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
+               ast_log(LOG_ERROR, "MySQL RealTime: Updating column '%s', but that column does not exist within the table '%s' (first pair MUST exist)!\n", newparam, tablename);
                release_table(table);
                release_database(dbh);
                return -1;
@@ -615,7 +617,7 @@ static int update_mysql(const char *database, const char *tablename, const char
 
        /* Execution. */
        if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
+               ast_log(LOG_WARNING, "MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
                release_table(table);
                release_database(dbh);
                return -1;
@@ -735,7 +737,7 @@ static int update2_mysql(const char *database, const char *tablename, va_list ap
 
        /* Execution. */
        if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
+               ast_log(LOG_WARNING, "MySQL RealTime: Failed to update database: %s\n", mysql_error(&dbh->handle));
                release_table(table);
                release_database(dbh);
                return -1;
@@ -766,7 +768,7 @@ static int store_mysql(const char *database, const char *table, va_list ap)
        const char *newparam, *newval;
 
        if (!(dbh = find_database(database, 1))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s'.\n", database);
+               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
                return -1;
        }
 
@@ -814,7 +816,7 @@ static int store_mysql(const char *database, const char *table, va_list ap)
 
        /* Execution. */
        if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
+               ast_log(LOG_WARNING, "MySQL RealTime: Failed to insert into database: %s\n", mysql_error(&dbh->handle));
                release_database(dbh);
                return -1;
        }
@@ -843,7 +845,7 @@ static int destroy_mysql(const char *database, const char *table, const char *ke
        const char *newparam, *newval;
 
        if (!(dbh = find_database(database, 1))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s'.\n", database);
+               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
                return -1;
        }
 
@@ -883,7 +885,7 @@ static int destroy_mysql(const char *database, const char *table, const char *ke
 
        /* Execution. */
        if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
+               ast_log(LOG_WARNING, "MySQL RealTime: Failed to delete from database: %s\n", mysql_error(&dbh->handle));
                release_database(dbh);
                return -1;
        }
@@ -922,11 +924,11 @@ static struct ast_config *config_mysql(const char *database, const char *table,
        }
 
        if (!(dbh = find_database(database, 0))) {
-               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s'\n", database);
+               ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database);
                return NULL;
        }
 
-       ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=1 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
+       ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
 
        ast_debug(1, "MySQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
 
@@ -1070,7 +1072,7 @@ static int modify_mysql(const char *database, const char *tablename, struct colu
 
                /* Execution. */
                if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) {
-                       ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database: %s\n", mysql_error(&dbh->handle));
+                       ast_log(LOG_WARNING, "MySQL RealTime: Failed to modify database: %s\n", mysql_error(&dbh->handle));
                        ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql));
                        res = -1;
                }
@@ -1225,7 +1227,7 @@ static int require_mysql(const char *database, const char *tablename, va_list ap
                                        } else {
                                                res = -1;
                                        }
-                               } else if ((strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9)) && type != RQ_DATETIME) {
+                               } else if ((strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) && type != RQ_DATETIME) {
                                        if (table->database->requirements == RQ_WARN) {
                                                ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
                                                res = -1;
@@ -1413,6 +1415,11 @@ static int parse_config(int reload)
        struct mysql_conn *cur;
 
        if ((config = ast_config_load(RES_CONFIG_MYSQL_CONF, config_flags)) == CONFIG_STATUS_FILEMISSING) {
+               /* Support old config file name */
+               config = ast_config_load(RES_CONFIG_MYSQL_CONF_OLD, config_flags);
+       }
+
+       if (config == CONFIG_STATUS_FILEMISSING) {
                return 0;
        } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
                return 0;
@@ -1557,6 +1564,7 @@ reconnect_tryagain:
                        ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s (err %d). Check debug for more info.\n", conn->name, !ast_strlen_zero(conn->host) ? conn->host : conn->sock, mysql_errno(&conn->handle));
                        ast_debug(1, "MySQL RealTime: Cannot Connect (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle));
                        conn->connected = 0;
+                       conn->connect_time = 0;
                        return 0;
                }
        } else {
@@ -1564,13 +1572,16 @@ reconnect_tryagain:
                 * So the postman pings twice. */
                if (mysql_ping(&conn->handle) != 0 && (usleep(1) + 2 > 0) && mysql_ping(&conn->handle) != 0) {
                        conn->connected = 0;
+                       conn->connect_time = 0;
                        ast_log(LOG_ERROR, "MySQL RealTime: Ping failed (%d).  Trying an explicit reconnect.\n", mysql_errno(&conn->handle));
                        ast_debug(1, "MySQL RealTime: Server Error (%d): %s\n", mysql_errno(&conn->handle), mysql_error(&conn->handle));
                        goto reconnect_tryagain;
                }
 
-               conn->connected = 1;
-               conn->connect_time = time(NULL);
+               if (!conn->connected) {
+                       conn->connected = 1;
+                       conn->connect_time = time(NULL);
+               }
 
                if (mysql_select_db(&conn->handle, conn->name) != 0) {
                        ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected (%u) - %s.\n", conn->name, mysql_errno(&conn->handle), mysql_error(&conn->handle));
@@ -1654,7 +1665,7 @@ static char *handle_cli_realtime_mysql_cache(struct ast_cli_entry *e, int cmd, s
                        AST_LIST_TRAVERSE(&cur->columns, col, list) {
                                ast_cli(a->fd, "%-20.20s %-20.20s %3d\n", col->name, col->type, col->len);
                        }
-                       ast_mutex_unlock(&cur->lock);
+                       release_table(cur);
                } else {
                        ast_cli(a->fd, "No such table '%s'\n", a->argv[3]);
                }