dovecot-1.2: dict sql: Added configuration for mapping dict path...
dovecot at dovecot.org
dovecot at dovecot.org
Wed Aug 27 12:10:26 EEST 2008
details: http://hg.dovecot.org/dovecot-1.2/rev/2d3e942a4cde
changeset: 8113:2d3e942a4cde
user: Timo Sirainen <tss at iki.fi>
date: Wed Aug 27 12:10:21 2008 +0300
description:
dict sql: Added configuration for mapping dict paths to SQL fields.
diffstat:
7 files changed, 590 insertions(+), 261 deletions(-)
doc/dovecot-dict-sql-example.conf | 39 ++
src/dict/Makefile.am | 1
src/lib-dict/Makefile.am | 7
src/lib-dict/dict-sql-settings.c | 215 ++++++++++++++
src/lib-dict/dict-sql-settings.h | 24 +
src/lib-dict/dict-sql.c | 551 +++++++++++++++++++------------------
src/lib-dict/dict.c | 14
diffs (truncated from 1100 to 300 lines):
diff -r d630b13ccd60 -r 2d3e942a4cde doc/dovecot-dict-sql-example.conf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/dovecot-dict-sql-example.conf Wed Aug 27 12:10:21 2008 +0300
@@ -0,0 +1,39 @@
+#connect = host=localhost dbname=mails user=testuser password=pass
+
+# CREATE TABLE quota (
+# username varchar(100) not null,
+# bytes bigint not null default 0,
+# messages integer not null default 0,
+# primary key (username)
+# );
+
+map {
+ pattern = priv/quota/storage
+ table = quota
+ username_field = username
+ value_field = bytes
+}
+map {
+ pattern = priv/quota/messages
+ table = quota
+ username_field = username
+ value_field = messages
+}
+
+# CREATE TABLE expires (
+# username varchar(100) not null,
+# mailbox varchar(255) not null,
+# expire_stamp integer not null,
+# primary key (username, mailbox)
+# );
+
+map {
+ pattern = shared/expire/$user/$mailbox
+ table = expires
+ value_field = expire_stamp
+
+ fields {
+ username = $user
+ mailbox = $mailbox
+ }
+}
diff -r d630b13ccd60 -r 2d3e942a4cde src/dict/Makefile.am
--- a/src/dict/Makefile.am Wed Aug 27 12:08:14 2008 +0300
+++ b/src/dict/Makefile.am Wed Aug 27 12:10:21 2008 +0300
@@ -15,6 +15,7 @@ libs = \
../lib-dict/libdict_backend.a \
../lib-dict/libdict.a \
../lib-sql/libsql.a \
+ ../lib-settings/libsettings.a \
../lib/liblib.a
dict_LDADD = \
diff -r d630b13ccd60 -r 2d3e942a4cde src/lib-dict/Makefile.am
--- a/src/lib-dict/Makefile.am Wed Aug 27 12:08:14 2008 +0300
+++ b/src/lib-dict/Makefile.am Wed Aug 27 12:10:21 2008 +0300
@@ -5,6 +5,7 @@ AM_CPPFLAGS = \
AM_CPPFLAGS = \
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-sql \
+ -I$(top_srcdir)/src/lib-settings \
-DPKG_RUNDIR=\""$(rundir)"\" \
$(SQL_CFLAGS)
@@ -14,7 +15,8 @@ base_sources = \
backend_sources = \
dict-db.c \
- dict-sql.c
+ dict-sql.c \
+ dict-sql-settings.c
libdict_a_SOURCES = \
$(base_sources)
@@ -27,7 +29,8 @@ headers = \
dict.h \
dict-client.h \
dict-private.h \
- dict-sql.h
+ dict-sql.h \
+ dict-sql-settings.h
if INSTALL_HEADERS
pkginc_libdir=$(pkgincludedir)/src/lib-dict
diff -r d630b13ccd60 -r 2d3e942a4cde src/lib-dict/dict-sql-settings.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict/dict-sql-settings.c Wed Aug 27 12:10:21 2008 +0300
@@ -0,0 +1,215 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "settings.h"
+#include "dict-sql-settings.h"
+
+#include <ctype.h>
+
+enum section_type {
+ SECTION_ROOT = 0,
+ SECTION_MAP,
+ SECTION_FIELDS
+};
+
+struct dict_sql_map_field {
+ const char *sql_field, *variable;
+};
+
+struct setting_parser_ctx {
+ pool_t pool;
+ struct dict_sql_settings *set;
+ enum section_type type;
+
+ struct dict_sql_map cur_map;
+ ARRAY_DEFINE(cur_fields, struct dict_sql_map_field);
+};
+
+#define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map)
+
+static struct setting_def dict_sql_map_setting_defs[] = {
+ DEF_STR(pattern),
+ DEF_STR(table),
+ DEF_STR(username_field),
+ DEF_STR(value_field),
+
+ { 0, NULL, 0 }
+};
+
+static const char *pattern_read_name(const char **pattern)
+{
+ const char *p = *pattern, *name;
+
+ if (*p == '{') {
+ /* ${name} */
+ name = ++p;
+ p = strchr(p, '}');
+ if (p == NULL) {
+ /* error, but allow anyway */
+ *pattern += strlen(*pattern);
+ return "";
+ }
+ *pattern = p + 1;
+ } else {
+ /* $name - ends at the first non-alnum_ character */
+ name = p;
+ for (; *p != '\0'; p++) {
+ if (!i_isalnum(*p) && *p != '_')
+ break;
+ }
+ *pattern = p;
+ }
+ name = t_strdup_until(name, p);
+ return name;
+}
+
+static const char *dict_sql_fields_map(struct setting_parser_ctx *ctx)
+{
+ struct dict_sql_map_field *fields;
+ string_t *pattern;
+ const char *p, *name;
+ unsigned int i, count;
+
+ p_array_init(&ctx->cur_map.sql_fields, ctx->pool, count);
+
+ /* go through the variables in the pattern, replace them with plain
+ '$' character and add its sql field */
+ pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1);
+ fields = array_get_modifiable(&ctx->cur_fields, &count);
+ for (p = ctx->cur_map.pattern; *p != '\0';) {
+ if (*p != '$') {
+ str_append_c(pattern, *p);
+ p++;
+ continue;
+ }
+ p++;
+ str_append_c(pattern, '$');
+
+ name = pattern_read_name(&p);
+ for (i = 0; i < count; i++) {
+ if (fields[i].variable != NULL &&
+ strcmp(fields[i].variable, name) == 0)
+ break;
+ }
+ if (i == count) {
+ return t_strconcat("Missing SQL field for variable: ",
+ name, NULL);
+ }
+
+ /* mark this field as used */
+ fields[i].variable = NULL;
+ array_append(&ctx->cur_map.sql_fields,
+ &fields[i].sql_field, 1);
+ }
+
+ /* make sure there aren't any unused fields */
+ for (i = 0; i < count; i++) {
+ if (fields[i].variable != NULL) {
+ return t_strconcat("Unused variable: ",
+ fields[i].variable, NULL);
+ }
+ }
+
+ if (ctx->set->max_field_count < count)
+ ctx->set->max_field_count = count;
+ ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern));
+ return NULL;
+}
+
+static const char *dict_sql_map_finish(struct setting_parser_ctx *ctx)
+{
+ if (!array_is_created(&ctx->cur_map.sql_fields)) {
+ /* no fields besides value. allocate the array anyway. */
+ p_array_init(&ctx->cur_map.sql_fields, ctx->pool, 1);
+ if (strchr(ctx->cur_map.pattern, '$') != NULL)
+ return "Missing fields for pattern variables";
+ }
+ array_append(&ctx->set->maps, &ctx->cur_map, 1);
+ memset(&ctx->cur_map, 0, sizeof(ctx->cur_map));
+ return NULL;
+}
+
+static const char *
+parse_setting(const char *key, const char *value,
+ struct setting_parser_ctx *ctx)
+{
+ struct dict_sql_map_field *field;
+
+ switch (ctx->type) {
+ case SECTION_ROOT:
+ if (strcmp(key, "connect") == 0) {
+ ctx->set->connect = p_strdup(ctx->pool, value);
+ return NULL;
+ }
+ break;
+ case SECTION_MAP:
+ return parse_setting_from_defs(ctx->pool,
+ dict_sql_map_setting_defs,
+ &ctx->cur_map, key, value);
+ case SECTION_FIELDS:
+ if (*value != '$') {
+ return t_strconcat("Value is missing '$' for field: ",
+ key, NULL);
+ }
+ field = array_append_space(&ctx->cur_fields);
+ field->sql_field = p_strdup(ctx->pool, key);
+ field->variable = p_strdup(ctx->pool, value + 1);
+ return NULL;
+ }
+ return t_strconcat("Unknown setting: ", key, NULL);
+}
+
+static bool
+parse_section(const char *type, const char *name ATTR_UNUSED,
+ struct setting_parser_ctx *ctx, const char **error_r)
+{
+ switch (ctx->type) {
+ case SECTION_ROOT:
+ if (type == NULL)
+ return FALSE;
+ if (strcmp(type, "map") == 0) {
+ array_clear(&ctx->cur_fields);
+ ctx->type = SECTION_MAP;
+ return TRUE;
+ }
+ break;
+ case SECTION_MAP:
+ if (type == NULL) {
+ ctx->type = SECTION_ROOT;
+ *error_r = dict_sql_map_finish(ctx);
+ return FALSE;
+ }
+ if (strcmp(type, "fields") == 0) {
+ ctx->type = SECTION_FIELDS;
+ return TRUE;
+ }
+ break;
+ case SECTION_FIELDS:
+ if (type == NULL) {
+ ctx->type = SECTION_MAP;
+ *error_r = dict_sql_fields_map(ctx);
+ return FALSE;
+ }
+ break;
+ }
+ *error_r = t_strconcat("Unknown section: ", type, NULL);
+ return FALSE;
+}
+
+struct dict_sql_settings *dict_sql_settings_read(pool_t pool, const char *path)
+{
+ struct setting_parser_ctx ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pool = pool;
+ ctx.set = p_new(pool, struct dict_sql_settings, 1);
+ t_array_init(&ctx.cur_fields, 16);
+ p_array_init(&ctx.set->maps, pool, 8);
+
+ if (!settings_read(path, NULL, parse_setting, parse_section, &ctx))
More information about the dovecot-cvs
mailing list