[svn-commits] twilson: branch twilson/res_config_sqlite3 r333945 - /team/twilson/res_config...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Aug 30 14:02:19 CDT 2011


Author: twilson
Date: Tue Aug 30 14:02:15 2011
New Revision: 333945

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=333945
Log:
Work in progress

Adds realtime store/update/update2/destroy/load support.

TODO:
add require and realtime pbx support
Add option to wrap transactions around things for much better performance
Make some helper funcs to get rid of some copy/paste code

I haven't bothered looking at any caching, etc. as premature optimization
is the blah, blah, blah.

Modified:
    team/twilson/res_config_sqlite3/res/res_config_sqlite3.c

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=333945&r1=333944&r2=333945
==============================================================================
--- team/twilson/res_config_sqlite3/res/res_config_sqlite3.c (original)
+++ team/twilson/res_config_sqlite3/res/res_config_sqlite3.c Tue Aug 30 14:02:15 2011
@@ -38,95 +38,366 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include <sqlite3.h>
+
 #include "asterisk/module.h"
 #include "asterisk/config.h"
+#include "asterisk/paths.h"
+
 
 /*** DOCUMENTATION
  ***/
 
-static struct ast_config *sqlite3_realtime_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 *sqlite3_realtime(const char *database, const char *table, va_list ap);
-static struct ast_config *sqlite3_realtime_multi(const char *database, const char *table, va_list ap);
-static int sqlite3_realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
-static int sqlite3_realtime_update2(const char *database, const char *table, va_list ap);
-static int sqlite3_realtime_store(const char *database, const char *table, va_list ap);
-static int sqlite3_realtime_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
-static int sqlite3_realtime_require(const char *database, const char *table, va_list ap);
-static int sqlite3_realtime_unload(const char *database, const char *table);
+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, va_list ap);
+static struct ast_config *realtime_sqlite3_multi(const char *database, const char *table, va_list ap);
+static int realtime_sqlite3_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+static int realtime_sqlite3_update2(const char *database, const char *table, va_list ap);
+static int realtime_sqlite3_store(const char *database, const char *table, va_list ap);
+static int realtime_sqlite3_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
+static int realtime_sqlite3_require(const char *database, const char *table, va_list ap);
+static int realtime_sqlite3_unload(const char *database, const char *table);
 
 struct ast_config_engine sqlite3_config_engine = {
-	.load_func = sqlite3_realtime_load,
-	.realtime_func = sqlite3_realtime,
-	.realtime_multi_func = sqlite3_realtime_multi,
-	.update_func = sqlite3_realtime_update,
-	.update2_func = sqlite3_realtime_update2,
-	.store_func = sqlite3_realtime_store,
-	.destroy_func = sqlite3_realtime_destroy,
-	.require_func = sqlite3_realtime_require,
-	.unload_func = sqlite3_realtime_unload,
+	.name = "sqlite3",
+	.load_func = realtime_sqlite3_load,
+	.realtime_func = realtime_sqlite3,
+	.realtime_multi_func = realtime_sqlite3_multi,
+	.update_func = realtime_sqlite3_update,
+	.update2_func = realtime_sqlite3_update2,
+	.store_func = realtime_sqlite3_store,
+	.destroy_func = realtime_sqlite3_destroy,
+	.require_func = realtime_sqlite3_require,
+	.unload_func = realtime_sqlite3_unload,
 };
 
+static struct {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(filename);
+		AST_STRING_FIELD(config_table);
+		AST_STRING_FIELD(cdr_table);
+	);
+} realtime_sqlite3_config;
+
+AST_MUTEX_DEFINE_STATIC(config_lock);
+AST_MUTEX_DEFINE_STATIC(db_lock);
+static sqlite3 *db;
+
 /*!
  * \return ast_config on success, NULL on failure
  */
-static struct ast_config *sqlite3_realtime_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_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)
+{
+	/* Currently no handling of realtime pbx stuff */
 	return NULL;
 }
 
+static int row_to_varlist(void *arg, int num_columns, char **values, char **columns)
+{
+	struct ast_variable **head = arg;
+	int i;
+
+	/* reverse order, but who cares? */
+	for (i = 0; i < num_columns; i++) {
+		struct ast_variable *new;
+		new = ast_variable_new(columns[i], values[i], "");
+		new->next = *head;
+		*head = new;
+	}
+
+	return 0;
+}
+
+static int append_row_to_cfg(void *arg, int num_columns, char **values, char **columns)
+{
+	struct ast_config *cfg = arg;
+	struct ast_category *cat;
+	int i;
+
+	if (!(cat = ast_category_new("", "", 99999))) {
+		return SQLITE_ABORT;
+	}
+
+	for (i = 0; i < num_columns; i++) {
+		struct ast_variable *var;
+		if (!(var = ast_variable_new(columns[i], values[i], ""))) {
+			ast_log(LOG_ERROR, "Could not create new variable for '%s: %s', throwing away list\n", columns[i], values[i]);
+			continue;
+		}
+		ast_variable_append(cat, var);
+	}
+	ast_category_append(cfg, cat);
+
+	return 0;
+}
+
+static int realtime_sqlite3_helper(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)) {
+		ast_log(LOG_WARNING, "Must have a table to query!\n");
+		return -1;
+	}
+
+	if (!(sql = ast_str_create(128))) {
+		return -1;
+	}
+
+	while ((param = va_arg(ap, const char *)) && (value = va_arg(ap, const char *))) {
+		if (first) {
+			ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s \"%s\"", table, param, strchr(param, ' ') ? "" : " =", value);
+			first = 0;
+		} else {
+			ast_str_append(&sql, 0, " AND %s%s \"%s\"", param, strchr(param, ' ') ? "" : " =", value);
+		}
+	}
+
+	if (!is_multi) {
+		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);
+
+	ast_free(sql);
+
+	return 0;
+}
+
 /*!
  * \return ast_variable list for single result on success, NULL on empty/failure
  */
-static struct ast_variable *sqlite3_realtime(const char *database, const char *table, va_list ap)
-{
-	return NULL;
+static struct ast_variable *realtime_sqlite3(const char *database, const char *table, va_list ap)
+{
+	struct ast_variable *result_row = NULL;
+
+	realtime_sqlite3_helper(table, ap, 0, &result_row);
+
+	return result_row;
 }
 
 /*!
  * \return ast_config containing possibly many results on success, NULL on empty/failure
  */
-static struct ast_config *sqlite3_realtime_multi(const char *database, const char *table, va_list ap)
-{
-	return NULL;
+static struct ast_config *realtime_sqlite3_multi(const char *database, const char *table, va_list ap)
+{
+	struct ast_config *cfg;
+
+	if (!(cfg = ast_config_new())) {
+		return NULL;
+	}
+
+	if (realtime_sqlite3_helper(table, ap, 1, cfg)) {
+		ast_config_destroy(cfg);
+		return NULL;
+	}
+
+	return cfg;
 }
 
 /*!
  * \return Number of rows affected or -1 on error
  */
-static int sqlite3_realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap)
-{
-	return 0;
+static int realtime_sqlite3_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap)
+{
+	struct ast_str *sql;
+	const char *key, *value;
+	char *errmsg;
+	int tmp = 1;
+
+
+	if (!(sql = ast_str_create(128))) {
+		return -1;
+	}
+
+	while ((key = va_arg(ap, const char *)) && (value = va_arg(ap, const char *))) {
+		if (tmp) {
+			ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", table, key, value);
+			tmp = 0;
+		} else {
+			ast_str_append(&sql, 0, ", `%s` = '%s'", key, value);
+		}
+	}
+
+	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);
+
+	return tmp;
 }
 
 /*!
  * \return Number of rows affected or -1 on error
  */
-static int sqlite3_realtime_update2(const char *database, const char *table, va_list ap)
-{
-	return 0;
+static int realtime_sqlite3_update2(const char *database, const char *table, va_list ap)
+{
+	struct ast_str *begin_sql;
+	struct ast_str *end_sql;
+	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);
+		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);
+			tmp = 0;
+		} else {
+			ast_str_append(&end_sql, 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);
+			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);
+
+	return tmp;
 }
 
 /*!
  * \return Number of rows affected or -1 on error
  */
-static int sqlite3_realtime_store(const char *database, const char *table, va_list ap)
-{
-	return 0;
+static int realtime_sqlite3_store(const char *database, const char *table, va_list ap)
+{
+	struct ast_str *sql, *values;
+	const char *column, *value;
+	char *errmsg;
+	int tmp = 1;
+
+	if (!(sql = ast_str_create(128))) {
+		return -1;
+	}
+
+	if (!(values = ast_str_create(128))) {
+		ast_free(sql);
+		return -1;
+	}
+
+	while ((column = va_arg(ap, const char *)) && (value = va_arg(ap, const char *))) {
+		if (tmp) {
+			ast_str_set(&sql, 0, "INSERT INTO %s (`%s`", table, column);
+			ast_str_set(&values, 0, ") VALUES ('%s'", value);
+			tmp = 0;
+		} else {
+			ast_str_append(&sql, 0, ", `%s`", column);
+			ast_str_append(&values, 0, ", '%s'", value);
+		}
+	}
+
+	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);
+
+	ast_free(sql);
+	ast_free(values);
+
+	return tmp;
 }
 
 /*!
  * \return Number of rows affected or -1 on error
  */
-static int sqlite3_realtime_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap)
-{
-	return 0;
+static int realtime_sqlite3_destroy(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap)
+{
+	struct ast_str *sql;
+	const char *param, *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;
+	}
+
+	while ((param = va_arg(ap, const char *)) && (value = va_arg(ap, const char *))) {
+		if (tmp) {
+			ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s%s \"%s\"", table, param, strchr(param, ' ') ? "" : " =", value);
+			tmp = 0;
+		} else {
+			ast_str_append(&sql, 0, " AND %s%s \"%s\"", param, strchr(param, ' ') ? "" : " =", value);
+		}
+	}
+
+	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);
+
+	ast_free(sql);
+
+	return tmp;
 }
 
 /*!
  * \retval 0 Required fields met specified standards
  * \retval -1 One or more fields was missing or insufficient
  */
-static int sqlite3_realtime_require(const char *database, const char *table, va_list ap)
+static int realtime_sqlite3_require(const char *database, const char *table, va_list ap)
 {
 	return 0;
 }
@@ -135,19 +406,122 @@
  * \retval 0 If any cache was purged
  * \retval -1 If no cache was found
  */
-static int sqlite3_realtime_unload(const char *database, const char *table)
-{
+static int realtime_sqlite3_unload(const char *database, const char *table)
+{
+	/* We currently do no caching */
+	return -1;
+}
+
+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";
+
+	config = ast_config_load(config_filename, config_flags);
+
+	if (config == CONFIG_STATUS_FILEUNCHANGED) {
+		ast_debug(1, "%s was unchanged, skipping parsing\n", config_filename);
+		return 0;
+	}
+
+	ast_mutex_lock(&config_lock);
+	ast_string_field_set(&realtime_sqlite3_config, filename, "");
+	ast_string_field_set(&realtime_sqlite3_config, config_table, "");
+	ast_string_field_set(&realtime_sqlite3_config, cdr_table, "");
+
+	if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
+		ast_log(LOG_ERROR, "%s config file '%s', using default values\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);
+			} else if (!strcasecmp(var->name, "config_table")) {
+				ast_string_field_set(&realtime_sqlite3_config, config_table, var->value);
+			}
+		}
+	}
+
+	if (ast_strlen_zero(realtime_sqlite3_config.filename)) { 
+		ast_string_field_build(&realtime_sqlite3_config, filename, "%s.sqlite3", ast_config_AST_DB);
+	}
+	ast_mutex_unlock(&config_lock);
+
+	ast_config_destroy(config);
+
+	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)
 {
-	return 0;
+	ast_mutex_lock(&config_lock);
+	ast_string_field_free_memory(&realtime_sqlite3_config);
+	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");
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+	if (parse_config()) {
+		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");
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SQLite 3 realtime config engine");
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SQLite 3 realtime config engine",
+	.load = load_module,
+	.unload = unload_module,
+	.reload = reload,
+	.load_pri = AST_MODPRI_REALTIME_DRIVER,
+);




More information about the svn-commits mailing list