Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3737:d67092398377 HEAD
Added dictionary API and implementation for SQL. It's meant for looking and
modifying key=value pairs. In future we may need to use this for many
different things, but for now it's only used by quota-dict plugin.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 10 Dec 2005 20:58:59 +0200 |
parents | 3dd0ab18d8da |
children | 732b62dc1976 |
files | configure.in src/Makefile.am src/lib-dict/.cvsignore src/lib-dict/Makefile.am src/lib-dict/dict-private.h src/lib-dict/dict-sql.c src/lib-dict/dict-sql.h src/lib-dict/dict.c src/lib-dict/dict.h |
diffstat | 9 files changed, 548 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.in Sat Dec 10 20:57:11 2005 +0200 +++ b/configure.in Sat Dec 10 20:58:59 2005 +0200 @@ -1477,6 +1477,7 @@ src/lib-sql/Makefile src/lib-auth/Makefile src/lib-charset/Makefile +src/lib-dict/Makefile src/lib-imap/Makefile src/lib-index/Makefile src/lib-mail/Makefile
--- a/src/Makefile.am Sat Dec 10 20:57:11 2005 +0200 +++ b/src/Makefile.am Sat Dec 10 20:58:59 2005 +0200 @@ -8,6 +8,7 @@ SUBDIRS = \ lib \ + lib-dict \ lib-sql \ lib-ntlm \ lib-settings \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/.cvsignore Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,8 @@ +*.la +*.lo +*.o +.deps +.libs +Makefile +Makefile.in +so_locations
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/Makefile.am Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,14 @@ +noinst_LIBRARIES = libdict.a + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-sql \ + $(SQL_CFLAGS) + +libdict_a_SOURCES = \ + dict.c \ + dict-sql.c + +noinst_HEADERS = \ + dict.h \ + dict-private.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict-private.h Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,43 @@ +#ifndef __DICT_PRIVATE_H +#define __DICT_PRIVATE_H + +#include "dict.h" + +struct dict_vfuncs { + struct dict *(*init)(struct dict *dict_class, const char *uri); + void (*deinit)(struct dict *dict); + + char *(*lookup)(struct dict *dict, pool_t pool, const char *key); + + struct dict_iterate_context * + (*iterate_init)(struct dict *dict, const char *path, + int recurse); + int (*iterate)(struct dict_iterate_context *ctx, + const char **key_r, const char **value_r); + void (*iterate_deinit)(struct dict_iterate_context *ctx); + + struct dict_transaction_context *(*transaction_init)(struct dict *dict); + int (*transaction_commit)(struct dict_transaction_context *ctx); + void (*transaction_rollback)(struct dict_transaction_context *ctx); + + void (*set)(struct dict_transaction_context *ctx, + const char *key, const char *value); + void (*atomic_inc)(struct dict_transaction_context *ctx, + const char *key, long long diff); +}; + +struct dict { + const char *name; + + struct dict_vfuncs v; +}; + +struct dict_iterate_context { + struct dict *dict; +}; + +struct dict_transaction_context { + struct dict *dict; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict-sql.c Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,285 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "lib.h" +#include "istream.h" +#include "str.h" +#include "strescape.h" +#include "sql-api-private.h" +#include "dict-private.h" +#include "dict-sql.h" + +#include <unistd.h> +#include <fcntl.h> + +struct sql_dict { + struct dict dict; + + pool_t pool; + struct sql_db *db; + + const char *connect_string; + const char *table, *select_field, *where_field; +}; + +struct sql_dict_iterate_context { + struct dict_iterate_context ctx; + + struct sql_result *result; +}; + +static int sql_dict_read_config(struct sql_dict *dict, const char *path) +{ + struct istream *input; + const char *line, *value; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) { + i_error("open(%s) failed: %m", path); + return -1; + } + + input = i_stream_create_file(fd, default_pool, (size_t)-1, FALSE); + while ((line = i_stream_read_next_line(input)) != NULL) { + value = strchr(line, '='); + if (value == NULL) + continue; + + t_push(); + line = t_strdup_until(line, value); + value++; + + if (strcmp(line, "connect") == 0) + dict->connect_string = p_strdup(dict->pool, value); + else if (strcmp(line, "table") == 0) + dict->table = p_strdup(dict->pool, value); + else if (strcmp(line, "select_field") == 0) + dict->select_field = p_strdup(dict->pool, value); + else if (strcmp(line, "where_field") == 0) + dict->where_field = p_strdup(dict->pool, value); + + t_pop(); + } + i_stream_unref(input); + (void)close(fd); + return 0; +} + +static struct dict *sql_dict_init(struct dict *dict_class, const char *uri) +{ + struct sql_dict *dict; + pool_t pool; + + pool = pool_alloconly_create("sql dict", 1024); + dict = p_new(pool, struct sql_dict, 1); + dict->pool = pool; + dict->dict = *dict_class; + + if (sql_dict_read_config(dict, uri) <= 0) { + pool_unref(pool); + return NULL; + } + + t_push(); + dict->db = sql_init(dict_class->name, dict->connect_string); + t_pop(); + return &dict->dict; +} + +static void sql_dict_deinit(struct dict *_dict) +{ + struct sql_dict *dict = (struct sql_dict *)_dict; + + sql_deinit(dict->db); +} + +static char *sql_dict_lookup(struct dict *_dict, pool_t pool, const char *key) +{ + struct sql_dict *dict = (struct sql_dict *)_dict; + struct sql_result *result; + const char *query; + char *ret; + + t_push(); + query = t_strdup_printf("SELECT %s FROM %s WHERE %s = '%s'", + dict->select_field, dict->table, + dict->where_field, str_escape(key)); + result = sql_query_s(dict->db, query); + t_pop(); + + if (sql_result_next_row(result) <= 0) + ret = NULL; + else + ret = p_strdup(pool, sql_result_get_field_value(result, 0)); + + sql_result_free(result); + return ret; +} + +static struct dict_iterate_context * +sql_dict_iterate_init(struct dict *_dict, const char *path, int recurse) +{ + struct sql_dict *dict = (struct sql_dict *)_dict; + struct sql_dict_iterate_context *ctx; + string_t *query; + + ctx = i_new(struct sql_dict_iterate_context, 1); + ctx->ctx.dict = _dict; + + t_push(); + query = t_str_new(256); + str_printfa(query, "SELECT %s, %s FROM %s WHERE %s LIKE '%s/%%'", + dict->where_field, dict->select_field, + dict->table, dict->where_field, str_escape(path)); + if (!recurse) { + str_printfa(query, " AND %s NOT LIKE '%s/%%/%%'", + dict->where_field, str_escape(path)); + } + ctx->result = sql_query_s(dict->db, str_c(query)); + t_pop(); + + return &ctx->ctx; +} + +static int sql_dict_iterate(struct dict_iterate_context *_ctx, + const char **key_r, const char **value_r) +{ + struct sql_dict_iterate_context *ctx = + (struct sql_dict_iterate_context *)_ctx; + int ret; + + if ((ret = sql_result_next_row(ctx->result)) <= 0) + return ret; + + *key_r = sql_result_get_field_value(ctx->result, 0); + *value_r = sql_result_get_field_value(ctx->result, 1); + return 1; +} + +static void sql_dict_iterate_deinit(struct dict_iterate_context *_ctx) +{ + struct sql_dict_iterate_context *ctx = + (struct sql_dict_iterate_context *)_ctx; + + sql_result_free(ctx->result); +} + +struct sql_dict_transaction_context { + struct dict_transaction_context ctx; + + struct sql_transaction_context *sql_ctx; +}; + +static struct dict_transaction_context * +sql_dict_transaction_init(struct dict *_dict) +{ + struct sql_dict *dict = (struct sql_dict *)_dict; + struct sql_dict_transaction_context *ctx; + + ctx = i_new(struct sql_dict_transaction_context, 1); + ctx->ctx.dict = _dict; + ctx->sql_ctx = sql_transaction_begin(dict->db); + + return &ctx->ctx; +} + +static int sql_dict_transaction_commit(struct dict_transaction_context *_ctx) +{ + struct sql_dict_transaction_context *ctx = + (struct sql_dict_transaction_context *)_ctx; + const char *error; + int ret; + + ret = sql_transaction_commit_s(ctx->sql_ctx, &error); + if (ret < 0) + i_error("sql dict: commit failed: %s", error); + i_free(ctx); + return ret; +} + +static void sql_dict_transaction_rollback(struct dict_transaction_context *_ctx) +{ + struct sql_dict_transaction_context *ctx = + (struct sql_dict_transaction_context *)_ctx; + + sql_transaction_rollback(ctx->sql_ctx); + i_free(ctx); +} + +static void sql_dict_set(struct dict_transaction_context *_ctx, + const char *key, const char *value) +{ + struct sql_dict_transaction_context *ctx = + (struct sql_dict_transaction_context *)_ctx; + struct sql_dict *dict = (struct sql_dict *)_ctx->dict; + const char *query; + + t_push(); + query = t_strdup_printf("UPDATE %s SET %s = '%s' WHERE %s = '%s'", + dict->table, dict->select_field, str_escape(value), + dict->where_field, str_escape(key)); + sql_update(ctx->sql_ctx, query); + t_pop(); +} + +static void sql_dict_atomic_inc(struct dict_transaction_context *_ctx, + const char *key, long long diff) +{ + struct sql_dict_transaction_context *ctx = + (struct sql_dict_transaction_context *)_ctx; + struct sql_dict *dict = (struct sql_dict *)_ctx->dict; + const char *query; + + t_push(); + query = t_strdup_printf("UPDATE %s SET %s = %s + %lld WHERE %s = '%s'", + dict->table, dict->select_field, + dict->select_field, diff, + dict->where_field, str_escape(key)); + sql_update(ctx->sql_ctx, query); + t_pop(); +} + +static struct dict sql_dict = { + MEMBER(name) "sql", + + { + sql_dict_init, + sql_dict_deinit, + sql_dict_lookup, + sql_dict_iterate_init, + sql_dict_iterate, + sql_dict_iterate_deinit, + sql_dict_transaction_init, + sql_dict_transaction_commit, + sql_dict_transaction_rollback, + sql_dict_set, + sql_dict_atomic_inc + } +}; + +static struct dict *dict_sql_classes; + +void dict_sql_register(void) +{ + int i, count; + + /* @UNSAFE */ + for (count = 0; sql_db_drivers[count] != NULL; count++) ; + dict_sql_classes = i_new(struct dict, count); + + for (i = 0; i < count; i++) { + dict_sql_classes[i] = sql_dict; + dict_sql_classes[i].name = sql_db_drivers[i]->name; + + dict_class_register(&dict_sql_classes[i]); + } +} + +void dict_sql_unregister(void) +{ + int i; + + for (i = 0; sql_db_drivers[i] != NULL; i++) + dict_class_unregister(&dict_sql_classes[i]); + i_free(dict_sql_classes); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict-sql.h Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,7 @@ +#ifndef __DICT_SQL_H +#define __DICT_SQL_H + +void dict_sql_register(void); +void dict_sql_unregister(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict.c Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,143 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "lib.h" +#include "array.h" +#include "dict-sql.h" +#include "dict-private.h" + +static array_t ARRAY_DEFINE(dict_classes, struct dict *); +static int dict_count = 0; + +static void dict_class_register_all(void) +{ + dict_sql_register(); +} + +static void dict_class_unregister_all(void) +{ + dict_sql_unregister(); +} + +static struct dict *dict_class_lookup(const char *name) +{ + struct dict *const *dicts; + unsigned int i, count; + + dicts = array_get(&dict_classes, &count); + for (i = 0; i < count; i++) { + if (strcmp(dicts[i]->name, name) == 0) + return dicts[i]; + } + return NULL; +} + +void dict_class_register(struct dict *dict_class) +{ + if (!array_is_created(&dict_classes)) + ARRAY_CREATE(&dict_classes, default_pool, struct dict *, 8); + + if (dict_class_lookup(dict_class->name) != NULL) { + i_fatal("dict_class_register(%s): Already registered", + dict_class->name); + } + array_append(&dict_classes, &dict_class, 1); +} + +void dict_class_unregister(struct dict *dict_class) +{ + struct dict *const *dicts; + unsigned int i, count; + + dicts = array_get(&dict_classes, &count); + for (i = 0; i < count; i++) { + if (dicts[i] == dict_class) { + array_delete(&dict_classes, i, 1); + break; + } + } + + i_assert(i < count); + + if (array_count(&dict_classes) == 0) + array_free(&dict_classes); +} + +struct dict *dict_init(const char *uri) +{ + struct dict *dict; + const char *p; + + if (dict_count++ == 0) + dict_class_register_all(); + + p = strchr(uri, ':'); + if (p == NULL) { + i_error("URI is missing ':': %s", uri); + return NULL; + } + + t_push(); + dict = dict_class_lookup(t_strdup_until(uri, p)); + t_pop(); + if (dict == NULL) + return NULL; + + return dict->v.init(dict, p+1); +} + +void dict_deinit(struct dict *dict) +{ + dict->v.deinit(dict); + + if (--dict_count == 0) + dict_class_unregister_all(); +} + +char *dict_lookup(struct dict *dict, pool_t pool, const char *key) +{ + return dict->v.lookup(dict, pool, key); +} + +struct dict_iterate_context * +dict_iterate_init(struct dict *dict, const char *path, int recurse) +{ + return dict->v.iterate_init(dict, path, recurse); +} + +int dict_iterate(struct dict_iterate_context *ctx, + const char **key_r, const char **value_r) +{ + return ctx->dict->v.iterate(ctx, key_r, value_r); +} + +void dict_iterate_deinit(struct dict_iterate_context *ctx) +{ + ctx->dict->v.iterate_deinit(ctx); +} + +struct dict_transaction_context *dict_transaction_begin(struct dict *dict) +{ + return dict->v.transaction_init(dict); +} + +int dict_transaction_commit(struct dict_transaction_context *ctx) +{ + return ctx->dict->v.transaction_commit(ctx); +} + +void dict_transaction_rollback(struct dict_transaction_context *ctx) +{ + ctx->dict->v.transaction_rollback(ctx); +} + +void dict_set(struct dict_transaction_context *ctx, + const char *key, const char *value) +{ + ctx->dict->v.set(ctx, key, value); +} + +void dict_atomic_inc(struct dict_transaction_context *ctx, + const char *key, long long diff) +{ + ctx->dict->v.atomic_inc(ctx, key, diff); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict.h Sat Dec 10 20:58:59 2005 +0200 @@ -0,0 +1,46 @@ +#ifndef __DICT_H +#define __DICT_H + +#define DICT_PATH_PRIVATE "priv/" +#define DICT_PATH_SHARED "shared/" + +struct dict; + +void dict_class_register(struct dict *dict_class); +void dict_class_unregister(struct dict *dict_class); + +/* Open dictionary with given URI (type:data). + If URI is invalid, returns NULL. */ +struct dict *dict_init(const char *uri); +/* Close dictionary. */ +void dict_deinit(struct dict *dict); + +/* Return value for key, or NULL if not found. */ +char *dict_lookup(struct dict *dict, pool_t pool, const char *key); + +/* Iterate through all values in a path. If recurse is FALSE, keys in + the given path are returned, but not their children. */ +struct dict_iterate_context * +dict_iterate_init(struct dict *dict, const char *path, int recurse); +/* Returns -1 = error, 0 = finished, 1 = key/value set */ +int dict_iterate(struct dict_iterate_context *ctx, + const char **key_r, const char **value_r); +void dict_iterate_deinit(struct dict_iterate_context *ctx); + +/* Start a new dictionary transaction. */ +struct dict_transaction_context *dict_transaction_begin(struct dict *dict); +/* Commit the transaction. Returns 0 if ok, -1 if failed. */ +int dict_transaction_commit(struct dict_transaction_context *ctx); +/* Rollback all changes made in transaction. */ +void dict_transaction_rollback(struct dict_transaction_context *ctx); + +/* Set key=value in dictionary. */ +void dict_set(struct dict_transaction_context *ctx, + const char *key, const char *value); +/* Increase/decrease a numeric value in dictionary. Note that the value is + changed when transaction is being committed, so you can't know beforehand + what the value will become. */ +void dict_atomic_inc(struct dict_transaction_context *ctx, + const char *key, long long diff); + +#endif