[asterisk-commits] twilson: branch twilson/res_config_sqlite3 r333945 - /team/twilson/res_config...
SVN commits to the Asterisk project
asterisk-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 asterisk-commits
mailing list