Merge "Fix ast_(v)asprintf() malloc failure usage conditions."
[asterisk/asterisk.git] / res / res_config_sqlite3.c
index 86af51c..bbf6db2 100644 (file)
@@ -58,6 +58,8 @@
 /*** DOCUMENTATION
  ***/
 
+static int has_explicit_like_escaping;
+
 static struct ast_config *realtime_sqlite3_load(const char *database, const char *table, const char *configfile, struct ast_config *config, struct ast_flags flags, const char *suggested_include_file, const char *who_asked);
 static struct ast_variable *realtime_sqlite3(const char *database, const char *table, const struct ast_variable *fields);
 static struct ast_config *realtime_sqlite3_multi(const char *database, const char *table, const struct ast_variable *fields);
@@ -101,6 +103,7 @@ struct realtime_sqlite3_db {
        unsigned int exiting:1;
        unsigned int wakeup:1;
        unsigned int batch;
+       int busy_timeout;
 };
 
 struct ao2_container *databases;
@@ -340,7 +343,7 @@ static int db_open(struct realtime_sqlite3_db *db)
                ao2_unlock(db);
                return -1;
        }
-       sqlite3_busy_timeout(db->handle, 1000);
+       sqlite3_busy_timeout(db->handle, db->busy_timeout);
 
        if (db->debug) {
                sqlite3_trace(db->handle, trace_cb, db);
@@ -398,6 +401,7 @@ static struct realtime_sqlite3_db *new_realtime_sqlite3_db(struct ast_config *co
        db->requirements = REALTIME_SQLITE3_REQ_WARN;
        db->batch = 100;
        ast_string_field_set(db, name, cat);
+       db->busy_timeout = 1000;
 
        for (var = ast_variable_browse(config, cat); var; var = var->next) {
                if (!strcasecmp(var->name, "dbfile")) {
@@ -408,6 +412,10 @@ static struct realtime_sqlite3_db *new_realtime_sqlite3_db(struct ast_config *co
                        ast_app_parse_timelen(var->value, (int *) &db->batch, TIMELEN_MILLISECONDS);
                } else if (!strcasecmp(var->name, "debug")) {
                        db->debug = ast_true(var->value);
+               } else if (!strcasecmp(var->name, "busy_timeout")) {
+                       if (ast_parse_arg(var->value, PARSE_INT32|PARSE_DEFAULT, &(db->busy_timeout), 1000) != 0) {
+                               ast_log(LOG_WARNING, "Invalid busy_timeout value '%s' at res_config_sqlite3.conf:%d. Using 1000 instead.\n", var->value, var->lineno);
+                       }
                }
        }
 
@@ -452,6 +460,11 @@ static int update_realtime_sqlite3_db(struct realtime_sqlite3_db *db, struct ast
                db_open(db); /* Also handles setting appropriate debug on new handle */
        }
 
+       if (db->busy_timeout != new->busy_timeout) {
+               db->busy_timeout = new->busy_timeout;
+               sqlite3_busy_timeout(db->handle, db->busy_timeout);
+       }
+
        if (db->batch != new->batch) {
                if (db->batch == 0) {
                        db->batch = new->batch;
@@ -501,7 +514,8 @@ static int append_row_to_cfg(void *arg, int num_columns, char **values, char **c
        struct ast_category *cat;
        int i;
 
-       if (!(cat = ast_category_new("", "", 99999))) {
+       cat = ast_category_new_anonymous();
+       if (!cat) {
                return SQLITE_ABORT;
        }
 
@@ -721,8 +735,8 @@ static int static_realtime_cb(void *arg, int num_columns, char **values, char **
        }
 
        if (!args->cat_name || strcmp(args->cat_name, values[COL_CATEGORY])) {
-               if (!(args->cat = ast_category_new(values[COL_CATEGORY], "", 99999))) {
-                       ast_log(LOG_WARNING, "Unable to allocate category\n");
+               args->cat = ast_category_new_dynamic(values[COL_CATEGORY]);
+               if (!args->cat) {
                        return SQLITE_ABORT;
                }
 
@@ -777,6 +791,8 @@ static struct ast_config *realtime_sqlite3_load(const char *database, const char
        return config;
 }
 
+#define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
+
 /*! \brief Helper function for single and multi-row realtime load functions */
 static int realtime_sqlite3_helper(const char *database, const char *table, const struct ast_variable *fields, int is_multi, void *arg)
 {
@@ -802,6 +818,15 @@ static int realtime_sqlite3_helper(const char *database, const char *table, cons
                        ast_str_append(&sql, 0, " AND %s %s", sqlite3_escape_column_op(field->name),
                                        sqlite3_escape_value(field->value));
                }
+
+               if (has_explicit_like_escaping && IS_SQL_LIKE_CLAUSE(field->name)) {
+                       /*
+                        * The realtime framework is going to pre-escape these
+                        * for us with a backslash. We just need to make sure
+                        * to tell SQLite about it
+                        */
+                       ast_str_append(&sql, 0, " ESCAPE '\\'");
+               }
        }
 
        if (!is_multi) {
@@ -1248,6 +1273,8 @@ static int parse_config(int reload)
        if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
                ast_log(LOG_ERROR, "%s config file '%s'\n",
                        config == CONFIG_STATUS_FILEMISSING ? "Missing" : "Invalid", config_filename);
+               ast_mutex_unlock(&config_lock);
+               return 0;
        } else {
                const char *cat;
                struct realtime_sqlite3_db *db;
@@ -1305,6 +1332,29 @@ static int unload_module(void)
        return 0;
 }
 
+static void discover_sqlite3_caps(void)
+{
+       /*
+        * So we cheat a little bit here. SQLite3 added support for the
+        * 'ESCAPE' keyword in 3.1.0. They added SQLITE_VERSION_NUMBER
+        * in 3.1.2. So if we run into 3.1.0 or 3.1.1 in the wild, we
+        * just treat it like < 3.1.0.
+        *
+        * For reference: 3.1.0, 3.1.1, and 3.1.2 were all released
+        * within 30 days of each other in Jan/Feb 2005, so I don't
+        * imagine we'll be finding something pre-3.1.2 that often in
+        * practice.
+        */
+#if defined(SQLITE_VERSION_NUMBER)
+       has_explicit_like_escaping = 1;
+#else
+       has_explicit_like_escaping = 0;
+#endif
+
+       ast_debug(3, "SQLite3 has 'LIKE ... ESCAPE ...' support? %s\n",
+                       has_explicit_like_escaping ? "Yes" : "No");
+}
+
 /*!
  * \brief Load the module
  *
@@ -1317,19 +1367,21 @@ static int unload_module(void)
  */
 static int load_module(void)
 {
+       discover_sqlite3_caps();
+
        if (!((databases = ao2_container_alloc(DB_BUCKETS, db_hash_fn, db_cmp_fn)))) {
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
 
        if (parse_config(0)) {
                ao2_ref(databases, -1);
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
 
        if (!(ast_config_engine_register(&sqlite3_config_engine))) {
                ast_log(LOG_ERROR, "The config API must have changed, this shouldn't happen.\n");
                ao2_ref(databases, -1);
-               return AST_MODULE_LOAD_FAILURE;
+               return AST_MODULE_LOAD_DECLINE;
        }
 
        return AST_MODULE_LOAD_SUCCESS;