struct sql_dict_inc_row *inc_row;
struct sql_dict_prev *prev_inc;
- struct sql_dict_prev *prev_set;
+ ARRAY(struct sql_dict_prev) prev_set;
dict_transaction_commit_callback_t *async_callback;
void *async_context;
static void sql_dict_transaction_free(struct sql_dict_transaction_context *ctx)
{
i_assert(ctx->prev_inc == NULL);
- i_assert(ctx->prev_set == NULL);
+ i_assert(!array_is_created(&ctx->prev_set));
pool_unref(&ctx->inc_row_pool);
i_free(ctx->error);
if (ctx->prev_inc != NULL)
sql_dict_prev_inc_flush(ctx);
- if (ctx->prev_set != NULL)
+ if (array_is_created(&ctx->prev_set))
sql_dict_prev_set_flush(ctx);
/* note that the above calls might still set ctx->error */
return 0;
}
-static void sql_dict_set_real(struct dict_transaction_context *_ctx,
- const char *key, const char *value)
+static void sql_dict_set_real(struct dict_transaction_context *_ctx)
{
struct sql_dict_transaction_context *ctx =
(struct sql_dict_transaction_context *)_ctx;
struct sql_dict *dict = (struct sql_dict *)_ctx->dict;
- const struct dict_sql_map *map;
+ const struct sql_dict_prev *prev_sets;
+ unsigned int count;
struct sql_statement *stmt;
ARRAY_TYPE(const_string) values;
struct dict_sql_build_query build;
- struct dict_sql_build_query_field field;
+ struct dict_sql_build_query_field *field;
const char *error;
if (ctx->error != NULL)
return;
- map = sql_dict_find_map(dict, key, &values);
- if (map == NULL) {
- ctx->error = i_strdup_printf(
- "dict-sql: Invalid/unmapped key: %s", key);
- return;
- }
+ prev_sets = array_get(&ctx->prev_set, &count);
+ i_assert(count > 0);
- field.map = map;
- field.value = value;
+ if (sql_dict_find_map(dict, prev_sets[0].key, &values) == NULL)
+ i_unreached(); /* this was already checked */
i_zero(&build);
build.dict = dict;
- t_array_init(&build.fields, 1);
- array_push_back(&build.fields, &field);
build.extra_values = &values;
- build.key1 = key[0];
+ build.key1 = prev_sets[0].key[0];
+
+ t_array_init(&build.fields, count);
+ for (unsigned int i = 0; i < count; i++) {
+ i_assert(build.key1 == prev_sets[i].key[0]);
+ field = array_append_space(&build.fields);
+ field->map = prev_sets[i].map;
+ field->value = prev_sets[i].value.str;
+ }
if (sql_dict_set_query(ctx, &build, &stmt, &error) < 0) {
- ctx->error = i_strdup_printf("dict-sql: Failed to set %s=%s: %s",
- key, value, error);
+ ctx->error = i_strdup_printf(
+ "dict-sql: Failed to set %u fields (first %s): %s",
+ count, prev_sets[0].key, error);
} else {
sql_update_stmt(ctx->sql_ctx, &stmt);
}
if (ctx->prev_inc != NULL)
sql_dict_prev_inc_flush(ctx);
- if (ctx->prev_set != NULL)
+ if (array_is_created(&ctx->prev_set))
sql_dict_prev_set_flush(ctx);
map = sql_dict_find_map(dict, key, &values);
static void sql_dict_prev_set_flush(struct sql_dict_transaction_context *ctx)
{
- i_assert(ctx->prev_set != NULL);
+ struct sql_dict_prev *prev_set;
+
+ i_assert(array_is_created(&ctx->prev_set));
- sql_dict_set_real(&ctx->ctx, ctx->prev_set->key,
- ctx->prev_set->value.str);
- i_free(ctx->prev_set->value.str);
- i_free(ctx->prev_set->key);
- i_free(ctx->prev_set);
+ sql_dict_set_real(&ctx->ctx);
+ array_foreach_modifiable(&ctx->prev_set, prev_set) {
+ i_free(prev_set->value.str);
+ i_free(prev_set->key);
+ }
+ array_free(&ctx->prev_set);
}
static void sql_dict_prev_inc_flush(struct sql_dict_transaction_context *ctx)
return;
}
- if (ctx->prev_set != NULL &&
- !sql_dict_maps_are_mergeable(dict, ctx->prev_set,
+ if (array_is_created(&ctx->prev_set) &&
+ !sql_dict_maps_are_mergeable(dict, array_front(&ctx->prev_set),
map, key, &values)) {
/* couldn't merge to the previous set - flush it */
sql_dict_prev_set_flush(ctx);
}
- if (ctx->prev_set == NULL) {
- /* see if we can merge this increment SQL query with the
- next one */
- ctx->prev_set = i_new(struct sql_dict_prev, 1);
- ctx->prev_set->map = map;
- ctx->prev_set->key = i_strdup(key);
- ctx->prev_set->value.str = i_strdup(value);
- return;
- }
-
- /* merge with prev_set */
- {
- struct dict_sql_build_query build;
- struct dict_sql_build_query_field *field;
- struct sql_statement *stmt;
- const char *error;
-
- i_zero(&build);
- build.dict = dict;
- t_array_init(&build.fields, 1);
- build.extra_values = &values;
- build.key1 = key[0];
-
- field = array_append_space(&build.fields);
- field->map = ctx->prev_set->map;
- field->value = ctx->prev_set->value.str;
- field = array_append_space(&build.fields);
- field->map = map;
- field->value = value;
-
- if (sql_dict_set_query(ctx, &build, &stmt, &error) < 0) {
- ctx->error = i_strdup_printf(
- "dict-sql: Failed to set %s: %s", key, error);
- } else {
- sql_update_stmt(ctx->sql_ctx, &stmt);
- }
- i_free(ctx->prev_set->value.str);
- i_free(ctx->prev_set->key);
- i_free(ctx->prev_set);
- }
+ if (!array_is_created(&ctx->prev_set))
+ i_array_init(&ctx->prev_set, 4);
+ /* Either this is the first set, or this can be merged with the
+ previous set. */
+ struct sql_dict_prev *prev_set = array_append_space(&ctx->prev_set);
+ prev_set->map = map;
+ prev_set->key = i_strdup(key);
+ prev_set->value.str = i_strdup(value);
}
static void sql_dict_atomic_inc(struct dict_transaction_context *_ctx,
if (ctx->error != NULL)
return;
- if (ctx->prev_set != NULL)
+ if (array_is_created(&ctx->prev_set))
sql_dict_prev_set_flush(ctx);
map = sql_dict_find_map(dict, key, &values);
const char *error;
struct test_driver_result res = {
.affected_rows = 1,
- .nqueries = 2,
+ .nqueries = 3,
.queries = (const char *[]){
"INSERT INTO counters (value,class,name) VALUES (128,'global','counter') ON DUPLICATE KEY UPDATE value=128",
"INSERT INTO quota (bytes,count,username) VALUES (128,1,'testuser') ON DUPLICATE KEY UPDATE bytes=128,count=1",
+ "INSERT INTO quota (bytes,count,folders,username) VALUES (128,1,123,'testuser') ON DUPLICATE KEY UPDATE bytes=128,count=1,folders=123",
NULL},
.result = NULL,
};
test_set_expected(dict, &res);
+ /* 1 field */
struct dict_transaction_context *ctx = dict_transaction_begin(dict);
dict_set(ctx, "shared/counters/global/counter", "128");
test_assert(dict_transaction_commit(&ctx, &error) == 1);
if (error != NULL)
i_error("dict_transaction_commit failed: %s", error);
error = NULL;
+
+ /* 2 fields */
+ ctx = dict_transaction_begin(dict);
+ dict_set(ctx, "priv/quota/bytes", "128");
+ dict_set(ctx, "priv/quota/count", "1");
+ test_assert(dict_transaction_commit(&ctx, &error) == 1);
+ if (error != NULL)
+ i_error("dict_transaction_commit failed: %s", error);
+ error = NULL;
+
+ /* 3 fields */
ctx = dict_transaction_begin(dict);
dict_set(ctx, "priv/quota/bytes", "128");
dict_set(ctx, "priv/quota/count", "1");
+ dict_set(ctx, "priv/quota/folders", "123");
test_assert(dict_transaction_commit(&ctx, &error) == 1);
if (error != NULL)
i_error("dict_transaction_commit failed: %s", error);