dovecot-1.2: dict-sql: Dictionary iteration fixes.
dovecot at dovecot.org
dovecot at dovecot.org
Sun Oct 12 13:36:58 EEST 2008
details: http://hg.dovecot.org/dovecot-1.2/rev/72b7277aefb3
changeset: 8265:72b7277aefb3
user: Timo Sirainen <tss at iki.fi>
date: Sun Oct 12 13:36:54 2008 +0300
description:
dict-sql: Dictionary iteration fixes.
diffstat:
1 file changed, 118 insertions(+), 31 deletions(-)
src/lib-dict/dict-sql.c | 149 +++++++++++++++++++++++++++++++++++++----------
diffs (298 lines):
diff -r f472f9ad69be -r 72b7277aefb3 src/lib-dict/dict-sql.c
--- a/src/lib-dict/dict-sql.c Sun Oct 12 11:44:33 2008 +0300
+++ b/src/lib-dict/dict-sql.c Sun Oct 12 13:36:54 2008 +0300
@@ -15,6 +15,12 @@
#define DICT_SQL_MAX_UNUSED_CONNECTIONS 10
+enum sql_recurse_type {
+ SQL_DICT_RECURSE_NONE,
+ SQL_DICT_RECURSE_ONE,
+ SQL_DICT_RECURSE_FULL
+};
+
struct sql_dict {
struct dict dict;
@@ -35,7 +41,7 @@ struct sql_dict_iterate_context {
struct sql_result *result;
string_t *key;
const struct dict_sql_map *map;
- unsigned int key_prefix_len, next_map_idx;
+ unsigned int key_prefix_len, pattern_prefix_len, next_map_idx;
};
struct sql_dict_transaction_context {
@@ -86,9 +92,12 @@ static void sql_dict_deinit(struct dict
static bool
dict_sql_map_match(const struct dict_sql_map *map, const char *path,
- ARRAY_TYPE(const_string) *values, bool partial_ok)
-{
+ ARRAY_TYPE(const_string) *values, unsigned int *pat_len_r,
+ unsigned int *path_len_r, bool partial_ok)
+{
+ const char *path_start = path;
const char *pat, *field, *p;
+ unsigned int len;
array_clear(values);
pat = map->pattern;
@@ -99,16 +108,39 @@ dict_sql_map_match(const struct dict_sql
if (*pat == '\0') {
/* pattern ended with this variable,
it'll match the rest of the path */
- array_append(values, &path, 1);
+ len = strlen(path);
+ if (partial_ok) {
+ /* iterating - the last field never
+ matches fully. if there's a trailing
+ '/', drop it. */
+ pat--;
+ if (path[len-1] == '/') {
+ field = t_strndup(path, len-1);
+ array_append(values, &field, 1);
+ } else {
+ array_append(values, &path, 1);
+ }
+ } else {
+ array_append(values, &path, 1);
+ path += len;
+ }
+ *path_len_r = path - path_start;
+ *pat_len_r = pat - map->pattern;
return TRUE;
}
/* pattern matches until the next '/' in path */
p = strchr(path, '/');
- if (p == NULL)
- return FALSE;
- field = t_strdup_until(path, p);
- array_append(values, &field, 1);
- path = p;
+ if (p != NULL) {
+ field = t_strdup_until(path, p);
+ array_append(values, &field, 1);
+ path = p;
+ } else {
+ /* no '/' anymore, but it'll still match a
+ partial */
+ array_append(values, &path, 1);
+ path += strlen(path);
+ pat++;
+ }
} else if (*pat == *path) {
pat++;
path++;
@@ -122,6 +154,8 @@ dict_sql_map_match(const struct dict_sql
return FALSE;
else {
/* partial matches must end with '/' */
+ *path_len_r = path - path_start;
+ *pat_len_r = pat - map->pattern;
return pat == map->pattern || pat[-1] == '/';
}
}
@@ -131,14 +165,15 @@ sql_dict_find_map(struct sql_dict *dict,
ARRAY_TYPE(const_string) *values)
{
const struct dict_sql_map *maps;
- unsigned int i, idx, count;
+ unsigned int i, idx, count, len;
t_array_init(values, dict->set->max_field_count);
maps = array_get(&dict->set->maps, &count);
for (i = 0; i < count; i++) {
/* start matching from the previously successful match */
idx = (dict->prev_map_match_idx + i) % count;
- if (dict_sql_map_match(&maps[idx], path, values, FALSE)) {
+ if (dict_sql_map_match(&maps[idx], path, values,
+ &len, &len, FALSE)) {
dict->prev_map_match_idx = idx;
return &maps[idx];
}
@@ -149,10 +184,11 @@ static void
static void
sql_dict_where_build(struct sql_dict *dict, const struct dict_sql_map *map,
const ARRAY_TYPE(const_string) *values_arr,
- const char *key, string_t *query)
+ const char *key, enum sql_recurse_type recurse_type,
+ string_t *query)
{
const char *const *sql_fields, *const *values;
- unsigned int i, count, count2;
+ unsigned int i, count, count2, exact_count;
bool priv = *key == DICT_PATH_PRIVATE[0];
sql_fields = array_get(&map->sql_fields, &count);
@@ -165,12 +201,42 @@ sql_dict_where_build(struct sql_dict *di
return;
}
- str_append(query, "WHERE");
- for (i = 0; i < count2; i++) {
+ str_append(query, " WHERE");
+ exact_count = count == count2 && recurse_type != SQL_DICT_RECURSE_NONE ?
+ count2-1 : count2;
+ for (i = 0; i < exact_count; i++) {
if (i > 0)
str_append(query, " AND");
str_printfa(query, " %s = '%s'", sql_fields[i],
sql_escape_string(dict->db, values[i]));
+ }
+ switch (recurse_type) {
+ case SQL_DICT_RECURSE_NONE:
+ break;
+ case SQL_DICT_RECURSE_ONE:
+ if (i > 0)
+ str_append(query, " AND");
+ if (i < count2) {
+ str_printfa(query, " %s LIKE '%s/%%' AND "
+ "%s NOT LIKE '%s/%%/%%'",
+ sql_fields[i],
+ sql_escape_string(dict->db, values[i]),
+ sql_fields[i],
+ sql_escape_string(dict->db, values[i]));
+ } else {
+ str_printfa(query, " %s LIKE '%%' AND "
+ "%s NOT LIKE '%%/%%'",
+ sql_fields[i], sql_fields[i]);
+ }
+ break;
+ case SQL_DICT_RECURSE_FULL:
+ if (i < count2) {
+ if (i > 0)
+ str_append(query, " AND");
+ str_printfa(query, " %s LIKE '%s/%%'", sql_fields[i],
+ sql_escape_string(dict->db, values[i]));
+ }
+ break;
}
if (priv) {
if (count2 > 0)
@@ -199,9 +265,10 @@ static int sql_dict_lookup(struct dict *
T_BEGIN {
string_t *query = t_str_new(256);
- str_printfa(query, "SELECT %s FROM %s ",
+ str_printfa(query, "SELECT %s FROM %s",
map->value_field, map->table);
- sql_dict_where_build(dict, map, &values, key, query);
+ sql_dict_where_build(dict, map, &values, key,
+ SQL_DICT_RECURSE_NONE, query);
result = sql_query_s(dict->db, str_c(query));
} T_END;
@@ -227,14 +294,17 @@ sql_dict_iterate_find_next_map(struct sq
{
struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict;
const struct dict_sql_map *maps;
- unsigned int i, count;
+ unsigned int i, count, pat_len, path_len;
t_array_init(values, dict->set->max_field_count);
maps = array_get(&dict->set->maps, &count);
for (i = ctx->next_map_idx; i < count; i++) {
- if (dict_sql_map_match(&maps[i], ctx->path, values, TRUE) &&
+ if (dict_sql_map_match(&maps[i], ctx->path,
+ values, &pat_len, &path_len, TRUE) &&
((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0 ||
- array_count(values)+1 == array_count(&maps[i].sql_fields))) {
+ array_count(values)+1 >= array_count(&maps[i].sql_fields))) {
+ ctx->key_prefix_len = path_len;
+ ctx->pattern_prefix_len = pat_len;
ctx->next_map_idx = i + 1;
return &maps[i];
}
@@ -248,6 +318,7 @@ static bool sql_dict_iterate_next_query(
const struct dict_sql_map *map;
ARRAY_TYPE(const_string) values;
const char *const *sql_fields;
+ enum sql_recurse_type recurse_type;
unsigned int i, count;
map = sql_dict_iterate_find_next_map(ctx, &values);
@@ -260,20 +331,31 @@ static bool sql_dict_iterate_next_query(
str_printfa(query, "SELECT %s", map->value_field);
/* get all missing fields */
sql_fields = array_get(&map->sql_fields, &count);
- for (i = array_count(&values); i < count; i++)
+ i = array_count(&values);
+ if (i == count) {
+ /* we always want to know the last field since we're
+ iterating its children */
+ i_assert(i > 0);
+ i--;
+ }
+ for (; i < count; i++)
str_printfa(query, ",%s", sql_fields[i]);
- str_printfa(query, " FROM %s ", map->table);
- sql_dict_where_build(dict, map, &values, ctx->path, query);
+ str_printfa(query, " FROM %s", map->table);
+
+ recurse_type = (ctx->flags & DICT_ITERATE_FLAG_RECURSE) == 0 ?
+ SQL_DICT_RECURSE_ONE : SQL_DICT_RECURSE_FULL;
+ sql_dict_where_build(dict, map, &values, ctx->path,
+ recurse_type, query);
if ((ctx->flags & DICT_ITERATE_FLAG_SORT_BY_KEY) != 0) {
- str_append(query, "ORDER BY ");
+ str_append(query, " ORDER BY ");
for (i = array_count(&values); i < count; i++) {
str_printfa(query, "%s", sql_fields[i]);
if (i < count-1)
str_append_c(query, ',');
}
} else if ((ctx->flags & DICT_ITERATE_FLAG_SORT_BY_VALUE) != 0)
- str_printfa(query, "ORDER BY %s", map->value_field);
+ str_printfa(query, " ORDER BY %s", map->value_field);
ctx->result = sql_query_s(dict->db, str_c(query));
} T_END;
@@ -292,8 +374,7 @@ sql_dict_iterate_init(struct dict *_dict
ctx->path = i_strdup(path);
ctx->flags = flags;
ctx->key = str_new(default_pool, 256);
- str_append(ctx->key, path);
- ctx->key_prefix_len = str_len(ctx->key);
+ str_append(ctx->key, ctx->path);
if (!sql_dict_iterate_next_query(ctx)) {
i_error("sql dict iterate: Invalid/unmapped path: %s", path);
@@ -328,9 +409,13 @@ static int sql_dict_iterate(struct dict_
/* convert fetched row to dict key */
str_truncate(ctx->key, ctx->key_prefix_len);
+ if (ctx->key_prefix_len > 0 &&
+ str_c(ctx->key)[ctx->key_prefix_len-1] != '/')
+ str_append_c(ctx->key, '/');
+
count = sql_result_get_fields_count(ctx->result);
i = 1;
- for (p = ctx->map->pattern + ctx->key_prefix_len; *p != '\0'; p++) {
+ for (p = ctx->map->pattern + ctx->pattern_prefix_len; *p != '\0'; p++) {
if (*p != '$')
str_append_c(ctx->key, *p);
else {
@@ -351,7 +436,8 @@ static void sql_dict_iterate_deinit(stru
struct sql_dict_iterate_context *ctx =
(struct sql_dict_iterate_context *)_ctx;
- sql_result_free(ctx->result);
+ if (ctx->result != NULL)
+ sql_result_free(ctx->result);
str_free(&ctx->key);
i_free(ctx->path);
i_free(ctx);
@@ -494,8 +580,9 @@ static void sql_dict_unset(struct dict_t
T_BEGIN {
string_t *query = t_str_new(256);
- str_printfa(query, "DELETE FROM %s ", map->table);
- sql_dict_where_build(dict, map, &values, key, query);
+ str_printfa(query, "DELETE FROM %s", map->table);
+ sql_dict_where_build(dict, map, &values, key,
+ SQL_DICT_RECURSE_NONE, query);
sql_update(ctx->sql_ctx, str_c(query));
} T_END;
}
More information about the dovecot-cvs
mailing list