[asterisk-commits] twilson: branch twilson/res_config_sqlite3 r334291 - in /team/twilson/res_con...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Sep 1 21:54:49 CDT 2011


Author: twilson
Date: Thu Sep  1 21:54:45 2011
New Revision: 334291

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=334291
Log:
Add multi-database support and stuff

Also added helper functions, handling of requirements including the ability
to warn or create table/add columns when necessary. Still need to add the
batch processing.

Modified:
    team/twilson/res_config_sqlite3/configs/res_config_sqlite3.conf.sample
    team/twilson/res_config_sqlite3/res/res_config_sqlite3.c

Modified: team/twilson/res_config_sqlite3/configs/res_config_sqlite3.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/twilson/res_config_sqlite3/configs/res_config_sqlite3.conf.sample?view=diff&rev=334291&r1=334290&r2=334291
==============================================================================
--- team/twilson/res_config_sqlite3/configs/res_config_sqlite3.conf.sample (original)
+++ team/twilson/res_config_sqlite3/configs/res_config_sqlite3.conf.sample Thu Sep  1 21:54:45 2011
@@ -1,4 +1,27 @@
-[general]
+; Define a realtime database name to use in extconfig.conf
+;
+;[asterisk]
+;dbfile => /var/lib/asterisk/realtime.sqlite3
+;
+; debug - Turn on debugging information
+;debug=yes
+;
+; requirements - At startup, each realtime family will make requirements
+;   on the backend.  There are several strategies for handling requirements:
+;     warn        - Warn if the required column does not exist.
+;     createclose - Create columns as close to the requirements as possible.
+;     createchar  - Create char columns only
+;
+;requirements=warn
+;
+; batch - SQLite 3 write performance can be greatly improved by wrapping
+;   multiple writes in transactions. This option specifies the duration in
+;   milliseconds of auto-generated transactions. Any changes made during an
+;   unfinished transaction will be immediately available to the same database
+;   connection, but any external connections could see a delay up to the value
+;   of this setting. It is also possible that if asterisk crashes, any changes
+;   made during this time could be lost. Due to the nearly 100x performance
+;   benefit, the default is 100 ms. Set to 0 to disable batching.
+;
+;batch=1000
 
-; The database file. Defaults to the astdb database file: astdb.sqlite3
-;dbfile => /var/lib/asterisk/sqlite.db

Modified: team/twilson/res_config_sqlite3/res/res_config_sqlite3.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/res_config_sqlite3/res/res_config_sqlite3.c?view=diff&rev=334291&r1=334290&r2=334291
==============================================================================
--- team/twilson/res_config_sqlite3/res/res_config_sqlite3.c (original)
+++ team/twilson/res_config_sqlite3/res/res_config_sqlite3.c Thu Sep  1 21:54:45 2011
@@ -72,15 +72,159 @@
 	.unload_func = realtime_sqlite3_unload,
 };
 
-static struct {
+enum {
+	REALTIME_SQLITE3_REQ_WARN,
+	REALTIME_SQLITE3_REQ_CLOSE,
+	REALTIME_SQLITE3_REQ_CHAR,
+};
+
+struct realtime_sqlite3_db {
 	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);
 		AST_STRING_FIELD(filename);
 	);
-} realtime_sqlite3_config;
+	sqlite3 *handle;
+	unsigned int requirements:2;
+	unsigned int dirty:1;
+	unsigned int debug:1;
+	unsigned int batch;
+};
+
+struct ao2_container *databases;
+#define DB_BUCKETS 7
 
 AST_MUTEX_DEFINE_STATIC(config_lock);
-AST_MUTEX_DEFINE_STATIC(db_lock);
-static sqlite3 *db;
+
+static int db_hash_fn(const void *obj, const int flags)
+{
+	const struct realtime_sqlite3_db *db = obj;
+
+	return ast_str_hash(flags & OBJ_KEY ? (const char *) obj : db->name);
+}
+
+static int db_cmp_fn(void *obj, void *arg, int flags) {
+	struct realtime_sqlite3_db *db = obj, *other = arg;
+	const char *name = arg;
+
+	return !strcasecmp(db->name, flags & OBJ_KEY ? name : other->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void db_destructor(void *obj)
+{
+	struct realtime_sqlite3_db *db = obj;
+
+	ast_string_field_free_memory(db);
+	if (db->handle) {
+		sqlite3_close(db->handle);
+	}
+}
+
+static struct realtime_sqlite3_db *find_database(const char *database)
+{
+	return ao2_find(databases, database, OBJ_KEY);
+}
+
+static void unref_db(struct realtime_sqlite3_db **db)
+{
+	ao2_ref(*db, -1);
+	*db = NULL;
+}
+
+static int mark_dirty_cb(void *obj, void *arg, int flags)
+{
+	struct realtime_sqlite3_db *db = obj;
+	db->dirty = 1;
+	return CMP_MATCH;
+}
+
+static void mark_all_databases_dirty(void)
+{
+	ao2_callback(databases, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, mark_dirty_cb, NULL);
+}
+
+static int is_dirty_cb(void *obj, void *arg, int flags)
+{
+	struct realtime_sqlite3_db *db = obj;
+	return db->dirty ? CMP_MATCH : 0;
+}
+
+static void unlink_dirty_databases(void)
+{
+	ao2_callback(databases, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, is_dirty_cb, NULL);
+}
+
+static int str_to_requirements(const char *data)
+{
+	if (!strcasecmp(data, "createclose")) {
+		return REALTIME_SQLITE3_REQ_CLOSE;
+	} else if (!strcasecmp(data, "createchar")) {
+		return REALTIME_SQLITE3_REQ_CHAR;
+	}
+	/* default */
+	return REALTIME_SQLITE3_REQ_WARN;
+}
+
+/* Since this is called while a query is executing, we should already hold the db lock */
+static void trace_cb(void *arg, const char *sql)
+{
+	struct realtime_sqlite3_db *db = arg;
+	ast_debug(3, "DB: %s SQL: %s\n", db->name, sql);
+}
+
+static struct realtime_sqlite3_db *new_realtime_sqlite3_db(struct ast_config *config, const char *cat)
+{
+	struct ast_variable *var;
+	struct realtime_sqlite3_db *db;
+
+	if (!(db = ao2_alloc(sizeof(*db), db_destructor))) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(db, 64)) {
+		unref_db(&db);
+		return NULL;
+	}
+
+	/* Set defaults */
+	db->requirements = REALTIME_SQLITE3_REQ_WARN;
+	db->batch = 100;
+	ast_string_field_set(db, name, cat);
+
+	for (var = ast_variable_browse(config, cat); var; var = var->next) {
+		if (!strcasecmp(var->name, "dbfile")) {
+			ast_string_field_set(db, filename, var->value);
+		} else if (!strcasecmp(var->name, "requirements")) {
+			db->requirements = str_to_requirements(var->value);
+		} else if (!strcasecmp(var->name, "batch")) {
+			db->batch = atoi(var->value);
+		} else if (!strcasecmp(var->name, "debug")) {
+			db->debug = ast_true(var->value);
+		}
+	}
+
+	if (ast_strlen_zero(db->filename)) {
+		ast_log(LOG_WARNING, "Must specify dbfile in res_config_sqlite3.conf\n");
+		unref_db(&db);
+		return NULL;
+	}
+
+	if (sqlite3_open(db->filename, &db->handle) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Could not open %s: %s\n", db->filename, sqlite3_errmsg(db->handle));
+		unref_db(&db);
+		return NULL;
+	}
+
+	if (db->debug) {
+		sqlite3_trace(db->handle, trace_cb, db);
+	}
+
+	return db;
+}
+
+static int update_realtime_sqlite3_db(struct ast_config *config, const char *cat)
+{
+	return 0;
+}
 
 static int row_to_varlist(void *arg, int num_columns, char **values, char **columns)
 {
@@ -186,13 +330,62 @@
 	return 0;
 }
 
+/*! Exeute an SQL statement
+ *
+ * \retval -1 ERROR
+ * \retval > -1 Number of rows changed
+ */
+static int realtime_sqlite3_execute_handle(struct realtime_sqlite3_db *db, const char *sql, int (*callback)(void*, int, char **, char **), void *arg)
+{
+	int res = 0;
+	char *errmsg;
+
+	ao2_lock(db);
+	if (sqlite3_exec(db->handle, sql, callback, arg, &errmsg) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", sql, errmsg);
+		sqlite3_free(errmsg);
+		res = -1;
+	} else {
+		res = sqlite3_changes(db->handle);
+	}
+	ao2_unlock(db);
+
+	return res;
+}
+
+/*! Exeute an SQL statement
+ *
+ * \retval -1 ERROR
+ * \retval > -1 Number of rows changed
+ */
+static int realtime_sqlite3_execute(const char *database, const char *sql, int (*callback)(void*, int, char **, char **), void *arg)
+{
+	struct realtime_sqlite3_db *db;
+	int res;
+
+	if (!(db = find_database(database))) {
+		ast_log(LOG_WARNING, "Could not find database: %s\n", database);
+		return -1;
+	}
+
+	res = realtime_sqlite3_execute_handle(db, sql, callback, arg);
+	ao2_ref(db, -1);
+
+	return res;
+}
+
 /*!
  * \return ast_config on success, NULL on failure
  */
 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)
 {
-	char *sql, *errmsg;
+	char *sql;
 	struct cfg_entry_args args;
+
+	if (ast_strlen_zero(table)) {
+		ast_log(LOG_WARNING, "Must have a table to query!\n");
+		return NULL;
+	}
 
 	if (!(sql = sqlite3_mprintf("SELECT category, var_name, var_val FROM %Q WHERE filename = %Q AND commented = 0 ORDER BY cat_metric ASC, var_metric ASC", table, configfile))) {
 		ast_log(LOG_WARNING, "Couldn't allocate query\n");
@@ -205,23 +398,17 @@
 	args.flags = flags;
 	args.who_asked = who_asked;
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, sql, static_realtime_cb, &args, &errmsg)) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", sql, errmsg);
-		sqlite3_free(errmsg);
-	}
-	ast_mutex_unlock(&db_lock);
+	realtime_sqlite3_execute(database, sql, static_realtime_cb, &args);
 
 	sqlite3_free(sql);
 
 	return config;
 }
 
-static int realtime_sqlite3_helper(const char *table, va_list ap, int is_multi, void *arg)
+static int realtime_sqlite3_helper(const char *database, const char *table, va_list ap, int is_multi, void *arg)
 {
 	struct ast_str *sql;
 	const char *param, *value;
-	char *errmsg;
 	int first = 1;
 
 	if (ast_strlen_zero(table)) {
@@ -246,12 +433,10 @@
 		ast_str_append(&sql, 0, "%s", " LIMIT 1");
 	}
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, ast_str_buffer(sql), is_multi ? append_row_to_cfg : row_to_varlist, arg, &errmsg)) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", ast_str_buffer(sql), errmsg);
-		sqlite3_free(errmsg);
-	}
-	ast_mutex_unlock(&db_lock);
+	if (realtime_sqlite3_execute(database, ast_str_buffer(sql), is_multi ? append_row_to_cfg : row_to_varlist, arg) < 0) {
+		ast_free(sql);
+		return -1;
+	}
 
 	ast_free(sql);
 
@@ -265,7 +450,7 @@
 {
 	struct ast_variable *result_row = NULL;
 
-	realtime_sqlite3_helper(table, ap, 0, &result_row);
+	realtime_sqlite3_helper(database, table, ap, 0, &result_row);
 
 	return result_row;
 }
@@ -281,7 +466,7 @@
 		return NULL;
 	}
 
-	if (realtime_sqlite3_helper(table, ap, 1, cfg)) {
+	if (realtime_sqlite3_helper(database, table, ap, 1, cfg)) {
 		ast_config_destroy(cfg);
 		return NULL;
 	}
@@ -296,9 +481,12 @@
 {
 	struct ast_str *sql;
 	const char *key, *value;
-	char *errmsg;
 	int tmp = 1;
 
+	if (ast_strlen_zero(table)) {
+		ast_log(LOG_WARNING, "Must have a table to query!\n");
+		return -1;
+	}
 
 	if (!(sql = ast_str_create(128))) {
 		return -1;
@@ -315,16 +503,8 @@
 
 	ast_str_append(&sql, 0, " WHERE %s%s '%s'", keyfield, strchr(keyfield, ' ') ? "" : " =", entity);
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, ast_str_buffer(sql), NULL, NULL, &errmsg) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", ast_str_buffer(sql), errmsg);
-		sqlite3_free(errmsg);
-		ast_mutex_unlock(&db_lock);
-		return -1;
-	}	
-
-	tmp = sqlite3_changes(db);
-	ast_mutex_unlock(&db_lock);
+	tmp = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL);
+	ast_free(sql);
 
 	return tmp;
 }
@@ -334,56 +514,50 @@
  */
 static int realtime_sqlite3_update2(const char *database, const char *table, va_list ap)
 {
-	struct ast_str *begin_sql;
-	struct ast_str *end_sql;
+	struct ast_str *sql;
+	struct ast_str *where_clause;
 	const char *key, *value;
-	char *errmsg;
 	int tmp = 1;
 
-
-	if (!(begin_sql = ast_str_create(128))) {
-		return -1;
-	}
-
-	if (!(end_sql = ast_str_create(128))) {
-		ast_free(begin_sql);
+	if (ast_strlen_zero(table)) {
+		ast_log(LOG_WARNING, "Must have a table to query!\n");
+		return -1;
+	}
+
+	if (!(sql = ast_str_create(128))) {
+		return -1;
+	}
+
+	if (!(where_clause = ast_str_create(128))) {
+		ast_free(sql);
 		return -1;
 	}
 
 	while ((key = va_arg(ap, const char *)) && (value = va_arg(ap, const char *))) {
 		if (tmp) {
-			ast_str_append(&end_sql, 0, " WHERE %s%s '%s'", key, strchr(key, ' ') ? "" : " =", value);
+			ast_str_append(&where_clause, 0, " WHERE %s%s '%s'", key, strchr(key, ' ') ? "" : " =", value);
 			tmp = 0;
 		} else {
-			ast_str_append(&end_sql, 0, " AND %s%s '%s'", key, strchr(key, ' ') ? "" : " =", value);
+			ast_str_append(&where_clause, 0, " AND %s%s '%s'", key, strchr(key, ' ') ? "" : " =", value);
 		}
 	}
 
 	tmp = 1;
 	while ((key = va_arg(ap, const char *)) && (value = va_arg(ap, const char *))) {
 		if (tmp) {
-			ast_str_set(&begin_sql, 0, "UPDATE %s SET `%s` = '%s'", table, key, value);
+			ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", table, key, value);
 			tmp = 0;
 		} else {
-			ast_str_append(&begin_sql, 0, ", `%s` = '%s'", key, value);
-		}
-	}
-
-	ast_str_append(&begin_sql, 0, "%s", ast_str_buffer(end_sql));
-
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, ast_str_buffer(begin_sql), NULL, NULL, &errmsg) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", ast_str_buffer(begin_sql), errmsg);
-		sqlite3_free(errmsg);
-		ast_mutex_unlock(&db_lock);
-		return -1;
-	}	
-
-	tmp = sqlite3_changes(db);
-	ast_mutex_unlock(&db_lock);
-
-	ast_free(begin_sql);
-	ast_free(end_sql);
+			ast_str_append(&sql, 0, ", `%s` = '%s'", key, value);
+		}
+	}
+
+	ast_str_append(&sql, 0, "%s", ast_str_buffer(where_clause));
+
+	tmp = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL);
+
+	ast_free(sql);
+	ast_free(where_clause);
 
 	return tmp;
 }
@@ -395,8 +569,12 @@
 {
 	struct ast_str *sql, *values;
 	const char *column, *value;
-	char *errmsg;
 	int tmp = 1;
+
+	if (ast_strlen_zero(table)) {
+		ast_log(LOG_WARNING, "Must have a table to query!\n");
+		return -1;
+	}
 
 	if (!(sql = ast_str_create(128))) {
 		return -1;
@@ -420,16 +598,7 @@
 
 	ast_str_append(&sql, 0, "%s)", ast_str_buffer(values));
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, ast_str_buffer(sql), NULL, NULL, &errmsg) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", ast_str_buffer(sql), errmsg);
-		sqlite3_free(errmsg);
-		ast_mutex_unlock(&db_lock);
-		return -1;
-	}	
-
-	tmp = sqlite3_changes(db);
-	ast_mutex_unlock(&db_lock);
+	tmp = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL);
 
 	ast_free(sql);
 	ast_free(values);
@@ -444,7 +613,6 @@
 {
 	struct ast_str *sql;
 	const char *param, *value;
-	char *errmsg;
 	int tmp = 1;
 
 	if (ast_strlen_zero(table)) {
@@ -465,14 +633,7 @@
 		}
 	}
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, ast_str_buffer(sql), NULL, NULL, &errmsg)) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", ast_str_buffer(sql), errmsg);
-		sqlite3_free(errmsg);
-	}
-
-	tmp = sqlite3_changes(db);
-	ast_mutex_unlock(&db_lock);
+	tmp = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL);
 
 	ast_free(sql);
 
@@ -511,27 +672,58 @@
 	return "TEXT";
 }
 
-static int create_sqlite3_column(const char *table, const char *column, int type, size_t sz)
-{
-	char *sql, *errmsg;
+static int handle_missing_table(struct realtime_sqlite3_db *db, const char *table, va_list ap)
+{
+	const char *column;
+	int type, tmp = 1;
+	size_t sz;
+	struct ast_str *sql;
+
+	if (!(sql = ast_str_create(128))) {
+		return -1;
+	}
+
+	while ((column = va_arg(ap, typeof(column))) && (type = va_arg(ap, typeof(type))) && (sz = va_arg(ap, typeof(sz)))) {
+		if (tmp) {
+			ast_str_set(&sql, 0, "CREATE TABLE IF NOT EXISTS %s (%s %s", table, column, get_sqlite_column_type(type));
+			tmp = 0;
+		} else {
+			ast_str_append(&sql, 0, ", %s %s", column, get_sqlite_column_type(type));
+		}
+	}
+
+	ast_str_append(&sql, 0, ")");
+
+	tmp = realtime_sqlite3_execute_handle(db, ast_str_buffer(sql), NULL, NULL) < 0 ? -1 : 0;
+	ast_free(sql);
+
+	return tmp;
+}
+
+static int handle_missing_column(struct realtime_sqlite3_db *db, const char *table, const char *column, int type, size_t sz)
+{
+	char *sql;
 	const char *sqltype = get_sqlite_column_type(type);
+	int res;
+
+	if (db->requirements == REALTIME_SQLITE3_REQ_WARN) {
+		ast_log(LOG_WARNING, "Missing column '%s' of type '%s' in %s.%s\n", column, sqltype, db->name, table);
+		return -1;
+	} else if (db->requirements == REALTIME_SQLITE3_REQ_CHAR) {
+		sqltype = "TEXT";
+	}
 
 	if (!(sql = sqlite3_mprintf("ALTER TABLE %q ADD COLUMN %Q %s", table, column, sqltype))) {
 		return -1;
 	}
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Could not execute '%s': %s\n", sql, errmsg);
-		sqlite3_free(errmsg);
-		sqlite3_free(sql);
-		return -1;
-	}
-	ast_mutex_unlock(&db_lock);
+	if (!(res = realtime_sqlite3_execute_handle(db, sql, NULL, NULL) < 0 ? -1 : 0)) {
+		ast_log(LOG_NOTICE, "Creating column '%s' type %s for table %s\n", column, sqltype, table);
+	}
 
 	sqlite3_free(sql);
-	ast_log(LOG_NOTICE, "Creating column '%s', type %s for table %s\n", column, sqltype, table);
-	return 0;
+
+	return res;
 }
 
 static int str_hash_fn(const void *obj, const int flags)
@@ -567,6 +759,13 @@
  */
 static int realtime_sqlite3_require(const char *database, const char *table, va_list ap)
 {
+	const char *column;
+	char *sql;
+	int type;
+	int res;
+	size_t sz;
+	struct ao2_container *columns;
+	struct realtime_sqlite3_db *db;
 
 	/* SQLite3 columns are dynamically typed, with type affinity. Built-in functions will
 	 * return the results as char * anyway. The only field that that cannot contain text
@@ -574,36 +773,47 @@
 	 * the purposes here we really only care whether the column exists and not what its
 	 * type or length is. */
 
-	const char *column;
-	int type;
-	size_t sz;
-	char *sql, *errmsg;
-	struct ao2_container *columns;
+	if (ast_strlen_zero(table)) {
+		ast_log(LOG_WARNING, "Must have a table to query!\n");
+		return -1;
+	}
+
+	if (!(db = find_database(database))) {
+		return -1;
+	}
 
 	if (!(columns = ao2_container_alloc(31, str_hash_fn, str_cmp_fn))) {
+		unref_db(&db);
 	   return -1;
 	}	   
 
 	if (!(sql = sqlite3_mprintf("PRAGMA table_info(%q)", table))) {
+		unref_db(&db);
 		ao2_ref(columns, -1);
 		return -1;
 	}
 
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_exec(db, sql, add_column_name, columns, &errmsg) != SQLITE_OK) {
-		ast_mutex_unlock(&db_lock);
+	if ((res = realtime_sqlite3_execute_handle(db, sql, add_column_name, columns)) < 0) {
+		unref_db(&db);
 		ao2_ref(columns, -1);
 		sqlite3_free(sql);
 		return -1;
-	}
-	ast_mutex_unlock(&db_lock);
+	} else if (res == 0) {
+		/* Table does not exist */
+		sqlite3_free(sql);
+		res = handle_missing_table(db, table, ap);
+		ao2_ref(columns, -1);
+		unref_db(&db);
+		return res;
+	}
 
 	sqlite3_free(sql);
 
 	while ((column = va_arg(ap, typeof(column))) && (type = va_arg(ap, typeof(type))) && (sz = va_arg(ap, typeof(sz)))) {
 		char *found;
 		if (!(found = ao2_find(columns, column, OBJ_POINTER | OBJ_UNLINK))) {
-			if (create_sqlite3_column(table, column, type, sz)) {
+			if (handle_missing_column(db, table, column, type, sz)) {
+				unref_db(&db);
 				ao2_ref(columns, -1);
 				return -1;
 			}
@@ -612,8 +822,8 @@
 		}
 	}
 
-	/* Should unlink the column names causing them to be destroyed */
 	ao2_ref(columns, -1);
+	unref_db(&db);
 
 	return 0;
 }
@@ -631,7 +841,6 @@
 static int parse_config(void)
 {
 	struct ast_config *config;
-	struct ast_variable *var;
 	struct ast_flags config_flags = { CONFIG_FLAG_NOREALTIME | CONFIG_FLAG_FILEUNCHANGED };
 	static const char *config_filename = "res_config_sqlite3.conf";
 
@@ -643,22 +852,33 @@
 	}
 
 	ast_mutex_lock(&config_lock);
-	ast_string_field_set(&realtime_sqlite3_config, filename, "");
 
 	if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
-		ast_log(LOG_ERROR, "%s config file '%s', using default values\n",
+		ast_log(LOG_ERROR, "%s config file '%s'\n",
 			config == CONFIG_STATUS_FILEMISSING ? "Missing" : "Invalid", config_filename);
 	} else {
-		for (var = ast_variable_browse(config, "general"); var; var = var->next) {
-			if (!strcasecmp(var->name, "dbfile")) {
-				ast_string_field_set(&realtime_sqlite3_config, filename, var->value);
+		const char *cat;
+		struct realtime_sqlite3_db *db;
+
+		mark_all_databases_dirty();
+		for (cat = ast_category_browse(config, NULL); cat; cat = ast_category_browse(config, cat)) {
+			if (!strcasecmp(cat, "general")) {
+				continue;
 			}
-		}
-	}
-
-	if (ast_strlen_zero(realtime_sqlite3_config.filename)) { 
-		ast_string_field_build(&realtime_sqlite3_config, filename, "%s.sqlite3", ast_config_AST_DB);
-	}
+			if (!(db = find_database(cat))) {
+				if (!(db = new_realtime_sqlite3_db(config, cat))) {
+					continue;
+				}
+				ao2_link(databases, db);
+				unref_db(&db);
+			} else {
+				update_realtime_sqlite3_db(config, cat);
+				unref_db(&db);
+			}
+		}
+		unlink_dirty_databases();
+	}
+
 	ast_mutex_unlock(&config_lock);
 
 	ast_config_destroy(config);
@@ -666,67 +886,37 @@
 	return 0;
 }
 
-static int db_open(void)
-{
-	int res = 0;
-
-	ast_mutex_lock(&db_lock);
-	if (sqlite3_open(realtime_sqlite3_config.filename, &db) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n",
-			realtime_sqlite3_config.filename, sqlite3_errmsg(db));
-		sqlite3_close(db);
-		res = -1;
-	}
-	ast_mutex_unlock(&db_lock);
-
-	return res;
-}
-
 static int reload(void)
 {
 	parse_config();
-
 	return 0;
 }
 
 static int unload_module(void)
 {
 	ast_mutex_lock(&config_lock);
-	ast_string_field_free_memory(&realtime_sqlite3_config);
+	ao2_ref(databases, -1);
+	databases = NULL;
 	ast_mutex_unlock(&config_lock);
-	ast_mutex_lock(&db_lock);
-	sqlite3_close(db);
-	ast_mutex_unlock(&db_lock);
 	return 0;
 }
 
-static void trace_cb(void *unused, const char *sql)
-{
-	ast_debug(3, "Executing: %s\n", sql);
-}
-
 static int load_module(void)
 {
-	if (ast_string_field_init(&realtime_sqlite3_config, 32)) {
-		ast_log(LOG_ERROR, "Could not initialize config stringfields!\n");
+	if (!((databases = ao2_container_alloc(DB_BUCKETS, db_hash_fn, db_cmp_fn)))) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
 	if (parse_config()) {
+		ao2_ref(databases, -1);
 		return AST_MODULE_LOAD_FAILURE;
 	}
-
-	if (db_open()) {
-		return AST_MODULE_LOAD_FAILURE;
-	}
-
-	sqlite3_trace(db, trace_cb, NULL);
 
 	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_SUCCESS;
 }




More information about the asterisk-commits mailing list