From: Timo Sirainen Date: Thu, 19 May 2022 14:42:23 +0000 (+0200) Subject: dict-sql: Add support for dict_op_settings.expire_secs X-Git-Tag: 2.4.0~3965 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=13f62c28badefa30d7fd0cc8d9c8e090351233c5;p=thirdparty%2Fdovecot%2Fcore.git dict-sql: Add support for dict_op_settings.expire_secs Add a new map { expire_field } to specify the expiration timestamp field in sql table. This field is automatically updated when dict_op_settings.expire_secs is used. Also when lookup/iteration sees an expired row, it's skipped over. --- diff --git a/src/lib-dict-backend/dict-sql-settings.c b/src/lib-dict-backend/dict-sql-settings.c index 66203a0bfa..7ca0373e1c 100644 --- a/src/lib-dict-backend/dict-sql-settings.c +++ b/src/lib-dict-backend/dict-sql-settings.c @@ -38,6 +38,7 @@ static const struct setting_def dict_sql_map_setting_defs[] = { DEF_STR(username_field), DEF_STR(value_field), DEF_STR(value_type), + DEF_STR(expire_field), DEF_BOOL(value_hexblob), { 0, NULL, 0 } @@ -160,6 +161,9 @@ static const char *dict_sql_map_finish(struct setting_parser_ctx *ctx) if (ctx->cur_map.value_field == NULL) return "Missing setting: value_field"; + if (ctx->cur_map.expire_field != NULL && + ctx->cur_map.expire_field[0] == '\0') + ctx->cur_map.expire_field = NULL; 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); diff --git a/src/lib-dict-backend/dict-sql-settings.h b/src/lib-dict-backend/dict-sql-settings.h index 1d0821b472..04b1b0f718 100644 --- a/src/lib-dict-backend/dict-sql-settings.h +++ b/src/lib-dict-backend/dict-sql-settings.h @@ -22,6 +22,7 @@ struct dict_sql_map { const char *username_field; const char *value_field; const char *value_type; + const char *expire_field; bool value_hexblob; /* SQL field names, one for each $ variable in the pattern */ diff --git a/src/lib-dict-backend/dict-sql.c b/src/lib-dict-backend/dict-sql.c index 0424ccf224..02a5318002 100644 --- a/src/lib-dict-backend/dict-sql.c +++ b/src/lib-dict-backend/dict-sql.c @@ -2,6 +2,7 @@ #include "lib.h" #include "array.h" +#include "ioloop.h" #include "istream.h" #include "hex-binary.h" #include "hash.h" @@ -468,8 +469,10 @@ sql_lookup_get_query(struct sql_dict *dict, string_t *query = t_str_new(256); ARRAY_TYPE(sql_dict_param) params; t_array_init(¶ms, 4); - str_printfa(query, "SELECT %s FROM %s", - map->value_field, map->table); + str_append(query, "SELECT "); + if (map->expire_field != NULL) + str_printfa(query, "%s,", map->expire_field); + str_printfa(query, "%s FROM %s", map->value_field, map->table); if (sql_dict_where_build(set->username, map, &pattern_values, key[0] == DICT_PATH_PRIVATE[0], SQL_DICT_RECURSE_NONE, query, @@ -513,12 +516,17 @@ sql_dict_result_unescape_values(const struct dict_sql_map *map, pool_t pool, struct sql_result *result) { const char **values; - unsigned int i; + unsigned int i, first_sql_idx = 0; values = p_new(pool, const char *, map->values_count + 1); + if (map->expire_field != NULL) { + /* don't include expire_field in results */ + first_sql_idx++; + } for (i = 0; i < map->values_count; i++) { values[i] = sql_dict_result_unescape(map->value_types[i], - pool, result, i); + pool, result, + first_sql_idx + i); } return values; } @@ -535,6 +543,27 @@ sql_dict_result_unescape_field(const struct dict_sql_map *map, pool_t pool, result, result_idx); } +static int +sql_dict_result_next_row(const struct dict_sql_map *map, + struct sql_result *result) +{ + int ret; + + while ((ret = sql_result_next_row(result)) == SQL_RESULT_NEXT_OK && + map->expire_field != NULL) { + const char *expire_value = + sql_result_get_field_value(result, 0); + time_t expire_timestamp; + + if (expire_value == NULL || + str_to_time(expire_value, &expire_timestamp) < 0 || + expire_timestamp > ioloop_time) + break; + /* expired - jump to the next row */ + } + return ret; +} + static int sql_dict_lookup(struct dict *_dict, const struct dict_op_settings *set, pool_t pool, const char *key, const char *const **values_r, const char **error_r) @@ -549,7 +578,7 @@ static int sql_dict_lookup(struct dict *_dict, const struct dict_op_settings *se return -1; result = sql_statement_query_s(&stmt); - ret = sql_result_next_row(result); + ret = sql_dict_result_next_row(map, result); if (ret < 0) { *error_r = t_strdup_printf("dict sql lookup failed: %s", sql_result_get_error(result)); @@ -574,7 +603,7 @@ sql_dict_lookup_async_callback(struct sql_result *sql_result, struct dict_lookup_result result; i_zero(&result); - result.ret = sql_result_next_row(sql_result); + result.ret = sql_dict_result_next_row(ctx->map, sql_result); if (result.ret < 0) result.error = sql_result_get_error(sql_result); else if (result.ret > 0) { @@ -681,6 +710,8 @@ sql_dict_iterate_build_next_query(struct sql_dict_iterate_context *ctx, string_t *query = t_str_new(256); str_append(query, "SELECT "); + if (map->expire_field != NULL) + str_printfa(query, "%s,", map->expire_field); if ((ctx->flags & DICT_ITERATE_FLAG_NO_VALUE) == 0) str_printfa(query, "%s,", map->value_field); @@ -824,7 +855,7 @@ static bool sql_dict_iterate(struct dict_iterate_context *_ctx, return FALSE; } - ret = sql_result_next_row(ctx->result); + ret = sql_dict_result_next_row(ctx->map, ctx->result); while (ret == SQL_RESULT_NEXT_MORE) { if ((ctx->flags & DICT_ITERATE_FLAG_ASYNC) == 0) sql_result_more_s(&ctx->result); @@ -839,7 +870,7 @@ static bool sql_dict_iterate(struct dict_iterate_context *_ctx, return FALSE; } } - ret = sql_result_next_row(ctx->result); + ret = sql_dict_result_next_row(ctx->map, ctx->result); } if (ret == 0) { /* see if there are more results in the next map. @@ -1072,10 +1103,15 @@ static int sql_dict_set_query(struct sql_dict_transaction_context *ctx, const char *const *pattern_values; unsigned int i, field_count, count, count2; string_t *prefix, *suffix; + time_t expire_timestamp = 0; fields = array_get(&build->fields, &field_count); i_assert(field_count > 0); + if (fields[0].map->expire_field != NULL && + ctx->ctx.set.expire_secs > 0) + expire_timestamp = ioloop_time + ctx->ctx.set.expire_secs; + t_array_init(¶ms, 4); prefix = t_str_new(64); suffix = t_str_new(256); @@ -1108,6 +1144,13 @@ static int sql_dict_set_query(struct sql_dict_transaction_context *ctx, param->value_type = DICT_SQL_TYPE_STRING; param->value_str = ctx->ctx.set.username; } + if (expire_timestamp != 0) { + struct sql_dict_param *param = array_append_space(¶ms); + str_printfa(prefix, ",%s", fields[0].map->expire_field); + str_append(suffix, ",?"); + param->value_type = DICT_SQL_TYPE_UINT; + param->value_int64 = expire_timestamp; + } /* add the variable fields that were parsed from the path */ pattern_fields = array_get(&fields[0].map->pattern_fields, &count); @@ -1165,6 +1208,12 @@ static int sql_dict_set_query(struct sql_dict_transaction_context *ctx, "", ¶ms, error_r) < 0) return -1; } + if (expire_timestamp != 0) { + str_printfa(prefix, ",%s=?", fields[0].map->expire_field); + struct sql_dict_param *param = array_append_space(¶ms); + param->value_type = DICT_SQL_TYPE_UINT; + param->value_int64 = expire_timestamp; + } *stmt_r = sql_dict_transaction_stmt_init(ctx, str_c(prefix), ¶ms); return 0; } @@ -1517,7 +1566,7 @@ static void sql_dict_atomic_inc(struct dict_transaction_context *_ctx, static struct dict sql_dict = { .name = "sql", - + .flags = DICT_DRIVER_FLAG_SUPPORT_EXPIRE_SECS, .v = { .init = sql_dict_init, .deinit = sql_dict_deinit,