Skip to content

Commit

Permalink
dict-sql: Support multiple values for lookups
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sirainen committed Jan 15, 2017
1 parent f58c640 commit a08f235
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 32 deletions.
41 changes: 37 additions & 4 deletions src/lib-dict/dict-sql-settings.c
Expand Up @@ -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 */
Expand Down
5 changes: 5 additions & 0 deletions src/lib-dict/dict-sql-settings.h
Expand Up @@ -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 {
Expand Down
57 changes: 29 additions & 28 deletions src/lib-dict/dict-sql.c
Expand Up @@ -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 *
Expand Down Expand Up @@ -479,23 +479,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);
Expand Down Expand Up @@ -958,12 +955,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)
Expand Down Expand Up @@ -997,17 +994,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)
Expand Down Expand Up @@ -1038,10 +1037,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);
Expand Down

0 comments on commit a08f235

Please sign in to comment.