]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dict-sql: Support multiple values for lookups
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 9 Jan 2017 18:32:12 +0000 (20:32 +0200)
committerGitLab <gitlab@git.dovecot.net>
Sun, 15 Jan 2017 00:10:11 +0000 (02:10 +0200)
The value's fields must be comma-separated without spaces, for example:

map {
  value_field = field1,field2
  value_type = string,uint
  ...

Only the first field is used for INSERTs and UPDATEs.

src/lib-dict/dict-sql-settings.c
src/lib-dict/dict-sql-settings.h
src/lib-dict/dict-sql.c

index 8c63d024bc55a455f32733d4b7d472e7673d19da..ba6e247d68f0df0f0843470025cc0e520475bc7f 100644 (file)
@@ -131,20 +131,53 @@ static const char *dict_sql_fields_map(struct setting_parser_ctx *ctx)
        return NULL;
 }
 
+static bool
+dict_sql_value_type_parse(const char *value_type, enum dict_sql_type *type_r)
+{
+       if (strcmp(value_type, "string") == 0)
+               *type_r = DICT_SQL_TYPE_STRING;
+       else if (strcmp(value_type, "hexblob") == 0)
+               *type_r = DICT_SQL_TYPE_HEXBLOB;
+       else if (strcmp(value_type, "uint") == 0)
+               *type_r = DICT_SQL_TYPE_UINT;
+       else
+               return FALSE;
+       return TRUE;
+}
+
 static const char *dict_sql_map_finish(struct setting_parser_ctx *ctx)
 {
+       unsigned int i;
+
        if (ctx->cur_map.pattern == NULL)
                return "Missing setting: pattern";
        if (ctx->cur_map.table == NULL)
                return "Missing setting: table";
        if (ctx->cur_map.value_field == NULL)
                return "Missing setting: value_field";
+
+       ctx->cur_map.value_fields = (const char *const *)
+               p_strsplit_spaces(ctx->pool, ctx->cur_map.value_field, ",");
+       ctx->cur_map.values_count = str_array_length(ctx->cur_map.value_fields);
+
+       enum dict_sql_type *value_types =
+               p_new(ctx->pool, enum dict_sql_type, ctx->cur_map.values_count);
        if (ctx->cur_map.value_type != NULL) {
-               if (strcmp(ctx->cur_map.value_type, "string") != 0 &&
-                   strcmp(ctx->cur_map.value_type, "hexblob") != 0 &&
-                   strcmp(ctx->cur_map.value_type, "uint") != 0)
-                       return "Invalid value in value_type";
+               const char *const *types =
+                       t_strsplit_spaces(ctx->cur_map.value_type, ",");
+               if (str_array_length(types) != ctx->cur_map.values_count)
+                       return "Number of fields in value_fields doesn't match value_type";
+               for (i = 0; i < ctx->cur_map.values_count; i++) {
+                       if (!dict_sql_value_type_parse(types[i], &value_types[i]))
+                               return "Invalid value in value_type";
+               }
+       } else {
+               for (i = 0; i < ctx->cur_map.values_count; i++) {
+                       value_types[i] = ctx->cur_map.value_hexblob ?
+                               DICT_SQL_TYPE_HEXBLOB : DICT_SQL_TYPE_STRING;
+               }
        }
+       ctx->cur_map.value_types = value_types;
 
        if (ctx->cur_map.username_field == NULL) {
                /* not all queries require this */
index dd9537237bbf29eb119edbfc1e016fbf33b5becd..5ee0977fe46557bd352edaa1132fcc085cf3d46c 100644 (file)
@@ -23,6 +23,11 @@ struct dict_sql_map {
        bool value_hexblob;
 
        ARRAY(struct dict_sql_field) sql_fields;
+
+       /* generated: */
+       unsigned int values_count;
+       const char *const *value_fields;
+       const enum dict_sql_type *value_types;
 };
 
 struct dict_sql_settings {
index 54a91c9d824b1dfaac13c3d33b913e1fb6cf11cf..4ea53e648c5b95d5f6b081ffe2780febc6ff4fce 100644 (file)
@@ -395,26 +395,26 @@ sql_dict_result_unescape(enum dict_sql_type type, pool_t pool,
        return str_c(str);
 }
 
-static enum dict_sql_type 
-sql_dict_map_type(const struct dict_sql_map *map)
-{
-       if (map->value_type != NULL) {
-               if (strcmp(map->value_type, "string") == 0)
-                       return DICT_SQL_TYPE_STRING;
-               if (strcmp(map->value_type, "hexblob") == 0)
-                       return DICT_SQL_TYPE_HEXBLOB;
-               if (strcmp(map->value_type, "uint") == 0)
-                       return DICT_SQL_TYPE_UINT;
-               i_unreached(); /* should have checked already at parsing */
-       }
-       return map->value_hexblob ? DICT_SQL_TYPE_HEXBLOB : DICT_SQL_TYPE_STRING;
-}
-
 static const char *
 sql_dict_result_unescape_value(const struct dict_sql_map *map, pool_t pool,
                               struct sql_result *result)
 {
-       return sql_dict_result_unescape(sql_dict_map_type(map), pool, result, 0);
+       return sql_dict_result_unescape(map->value_types[0], pool, result, 0);
+}
+
+static const char *const *
+sql_dict_result_unescape_values(const struct dict_sql_map *map, pool_t pool,
+                               struct sql_result *result)
+{
+       const char **values;
+       unsigned int i;
+
+       values = p_new(pool, const char *, map->values_count + 1);
+       for (i = 0; i < map->values_count; i++) {
+               values[i] = sql_dict_result_unescape(map->value_types[i],
+                                                    pool, result, i);
+       }
+       return values;
 }
 
 static const char *
@@ -467,23 +467,20 @@ sql_dict_lookup_async_callback(struct sql_result *sql_result,
                               struct sql_dict_lookup_context *ctx)
 {
        struct dict_lookup_result result;
-       const char *values[2] = { NULL, NULL };
 
        i_zero(&result);
        result.ret = sql_result_next_row(sql_result);
        if (result.ret < 0)
                result.error = sql_result_get_error(sql_result);
        else if (result.ret > 0) {
-               result.value = sql_dict_result_unescape_value(ctx->map,
+               result.values = sql_dict_result_unescape_values(ctx->map,
                        pool_datastack_create(), sql_result);
+               result.value = result.values[0];
                if (result.value == NULL) {
                        /* NULL value returned. we'll treat this as
                           "not found", which is probably what is usually
                           wanted. */
                        result.ret = 0;
-               } else {
-                       values[0] = result.value;
-                       result.values = values;
                }
        }
        ctx->callback(&result, ctx->context);
@@ -945,12 +942,12 @@ static int sql_dict_set_query(struct sql_dict_transaction_context *ctx,
                        str_append_c(prefix, ',');
                        str_append_c(suffix, ',');
                }
-               str_append(prefix, fields[i].map->value_field);
+               str_append(prefix, t_strcut(fields[i].map->value_field, ','));
                if (build->inc)
                        str_append(suffix, fields[i].value);
                else {
                        enum dict_sql_type value_type =
-                               sql_dict_map_type(fields[i].map);
+                               fields[i].map->value_types[0];
                        if (sql_dict_value_escape(suffix, dict, fields[i].map,
                                value_type, "value", fields[i].value,
                                "", error_r) < 0)
@@ -984,17 +981,19 @@ static int sql_dict_set_query(struct sql_dict_transaction_context *ctx,
 
        str_append(prefix, " ON DUPLICATE KEY UPDATE ");
        for (i = 0; i < field_count; i++) {
+               const char *first_value_field =
+                       t_strcut(fields[i].map->value_field, ',');
                if (i > 0)
                        str_append_c(prefix, ',');
-               str_append(prefix, fields[i].map->value_field);
+               str_append(prefix, first_value_field);
                str_append_c(prefix, '=');
                if (build->inc) {
                        str_printfa(prefix, "%s+%s",
-                                   fields[i].map->value_field,
+                                   first_value_field,
                                    fields[i].value);
                } else {
                        enum dict_sql_type value_type =
-                               sql_dict_map_type(fields[i].map);
+                               fields[i].map->value_types[0];
                        if (sql_dict_value_escape(prefix, dict, fields[i].map,
                                value_type, "value", fields[i].value,
                                "", error_r) < 0)
@@ -1025,10 +1024,12 @@ sql_dict_update_query(struct sql_dict_transaction_context *ctx,
        sql_dict_transaction_add_timestamp(ctx, query);
        str_append(query, " SET ");
        for (i = 0; i < field_count; i++) {
+               const char *first_value_field =
+                       t_strcut(fields[i].map->value_field, ',');
                if (i > 0)
                        str_append_c(query, ',');
-               str_printfa(query, "%s=%s", fields[i].map->value_field,
-                           fields[i].map->value_field);
+               str_printfa(query, "%s=%s", first_value_field,
+                           first_value_field);
                if (fields[i].value[0] != '-')
                        str_append_c(query, '+');
                str_append(query, fields[i].value);