dovecot-1.2: sql dict: Merge two INSERTs together whenever possi...

dovecot at dovecot.org dovecot at dovecot.org
Sat Jan 17 20:56:34 EET 2009


details:   http://hg.dovecot.org/dovecot-1.2/rev/cc46e5822b96
changeset: 8647:cc46e5822b96
user:      Timo Sirainen <tss at iki.fi>
date:      Sat Jan 17 12:32:28 2009 -0500
description:
sql dict: Merge two INSERTs together whenever possible (for dict quota).

diffstat:

1 file changed, 195 insertions(+), 30 deletions(-)
src/lib-dict/dict-sql.c |  225 ++++++++++++++++++++++++++++++++++++++++-------

diffs (truncated from 309 to 300 lines):

diff -r 7c568270c8df -r cc46e5822b96 src/lib-dict/dict-sql.c
--- a/src/lib-dict/dict-sql.c	Sat Jan 17 12:31:28 2009 -0500
+++ b/src/lib-dict/dict-sql.c	Sat Jan 17 12:32:28 2009 -0500
@@ -49,11 +49,17 @@ struct sql_dict_transaction_context {
 
 	struct sql_transaction_context *sql_ctx;
 
+	const struct dict_sql_map *prev_inc_map;
+	char *prev_inc_key;
+	long long prev_inc_diff;
+
 	unsigned int failed:1;
 	unsigned int changed:1;
 };
 
 static struct sql_pool *dict_sql_pool;
+
+static void sql_dict_prev_inc_flush(struct sql_dict_transaction_context *ctx);
 
 static struct dict *
 sql_dict_init(struct dict *driver, const char *uri,
@@ -463,6 +469,9 @@ static int sql_dict_transaction_commit(s
 	const char *error;
 	int ret;
 
+	if (ctx->prev_inc_map != NULL)
+		sql_dict_prev_inc_flush(ctx);
+
 	if (ctx->failed) {
 		sql_transaction_rollback(&ctx->sql_ctx);
 		ret = -1;
@@ -485,52 +494,89 @@ static void sql_dict_transaction_rollbac
 
 	if (_ctx->changed)
 		sql_transaction_rollback(&ctx->sql_ctx);
+	i_free(ctx->prev_inc_key);
 	i_free(ctx);
 }
 
-static const char *
-sql_dict_set_query(struct sql_dict *dict, const struct dict_sql_map *map,
-		   const ARRAY_TYPE(const_string) *values_arr,
-		   const char *key, const char *value, bool inc)
-{
-	const char *const *sql_fields, *const *values;
-	unsigned int i, count, count2;
+struct dict_sql_build_query_field {
+	const struct dict_sql_map *map;
+	const char *value;
+};
+
+struct dict_sql_build_query {
+	struct sql_dict *dict;
+
+	ARRAY_DEFINE(fields, struct dict_sql_build_query_field);
+	const ARRAY_TYPE(const_string) *extra_values;
+	char key1;
+	bool inc;
+};
+
+static const char *sql_dict_set_query(const struct dict_sql_build_query *build)
+{
+	struct sql_dict *dict = build->dict;
+	const struct dict_sql_build_query_field *fields;
+	const char *const *sql_fields, *const *extra_values;
+	unsigned int i, field_count, count, count2;
 	string_t *prefix, *suffix;
+
+	fields = array_get(&build->fields, &field_count);
+	i_assert(field_count > 0);
 
 	prefix = t_str_new(64);
 	suffix = t_str_new(256);
-	str_printfa(prefix, "INSERT INTO %s (%s", map->table, map->value_field);
+	str_printfa(prefix, "INSERT INTO %s (", fields[0].map->table);
 	str_append(suffix, ") VALUES (");
-	if (inc)
-		str_append(suffix, value);
-	else
-		str_printfa(suffix, "'%s'", sql_escape_string(dict->db, value));
-	if (*key == DICT_PATH_PRIVATE[0]) {
-		str_printfa(prefix, ",%s", map->username_field);
+	for (i = 0; i < field_count; i++) {
+		if (i > 0) {
+			str_append_c(prefix, ',');
+			str_append_c(suffix, ',');
+		}
+		str_append(prefix, fields[i].map->value_field);
+		if (build->inc)
+			str_append(suffix, fields[i].value);
+		else {
+			str_printfa(suffix, "'%s'",
+				sql_escape_string(dict->db, fields[i].value));
+		}
+	}
+	if (build->key1 == DICT_PATH_PRIVATE[0]) {
+		str_printfa(prefix, ",%s", fields[0].map->username_field);
 		str_printfa(suffix, ",'%s'",
 			    sql_escape_string(dict->db, dict->username));
 	}
 
 	/* add the other fields from the key */
-	sql_fields = array_get(&map->sql_fields, &count);
-	values = array_get(values_arr, &count2);
+	sql_fields = array_get(&fields[0].map->sql_fields, &count);
+	extra_values = array_get(build->extra_values, &count2);
 	i_assert(count == count2);
 	for (i = 0; i < count; i++) {
 		str_printfa(prefix, ",%s", sql_fields[i]);
 		str_printfa(suffix, ",'%s'",
-			    sql_escape_string(dict->db, values[i]));
+			    sql_escape_string(dict->db, extra_values[i]));
 	}
 
 	str_append_str(prefix, suffix);
 	str_append_c(prefix, ')');
-	if (dict->has_on_duplicate_key) {
-		str_printfa(prefix, " ON DUPLICATE KEY UPDATE %s =",
-			    map->value_field);
-		if (inc)
-			str_printfa(prefix, "%s+%s", map->value_field, value);
-		else {
-			str_printfa(prefix, "'%s'",
-				    sql_escape_string(dict->db, value));
+	if (!dict->has_on_duplicate_key)
+		return str_c(prefix);
+
+	str_append(prefix, " ON DUPLICATE KEY UPDATE ");
+	for (i = 0; i < field_count; i++) {
+		if (i > 0) {
+			str_append_c(prefix, ',');
+			str_append_c(suffix, ',');
+		}
+		str_append(prefix, fields[i].map->value_field);
+		str_append_c(prefix, '=');
+		if (build->inc) {
+			str_printfa(prefix, "%s+%s",
+				    fields[i].map->value_field,
+				    fields[i].value);
+			str_append(suffix, fields[i].value);
+		} else {
+			str_printfa(suffix, "'%s'",
+				sql_escape_string(dict->db, fields[i].value));
 		}
 	}
 	return str_c(prefix);
@@ -552,11 +598,25 @@ static void sql_dict_set(struct dict_tra
 		return;
 	}
 
+	if (ctx->prev_inc_map != NULL)
+		sql_dict_prev_inc_flush(ctx);
+
 	T_BEGIN {
+		struct dict_sql_build_query build;
+		struct dict_sql_build_query_field field;
 		const char *query;
 
-		query = sql_dict_set_query(dict, map, &values, key, value,
-					   FALSE);
+		field.map = map;
+		field.value = value;
+
+		memset(&build, 0, sizeof(build));
+		build.dict = dict;
+		t_array_init(&build.fields, 1);
+		array_append(&build.fields, &field, 1);
+		build.extra_values = &values;
+		build.key1 = key[0];
+
+		query = sql_dict_set_query(&build);
 		sql_update(ctx->sql_ctx, query);
 	} T_END;
 }
@@ -570,6 +630,9 @@ static void sql_dict_unset(struct dict_t
 	const struct dict_sql_map *map;
 	ARRAY_TYPE(const_string) values;
 
+	if (ctx->prev_inc_map != NULL)
+		sql_dict_prev_inc_flush(ctx);
+
 	map = sql_dict_find_map(dict, key, &values);
 	if (map == NULL) {
 		i_error("sql dict unset: Invalid/unmapped key: %s", key);
@@ -587,6 +650,79 @@ static void sql_dict_unset(struct dict_t
 	} T_END;
 }
 
+static void sql_dict_atomic_inc_real(struct sql_dict_transaction_context *ctx,
+				     const char *key, long long diff)
+{
+	struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict;
+	const struct dict_sql_map *map;
+	ARRAY_TYPE(const_string) values;
+
+	map = sql_dict_find_map(dict, key, &values);
+	i_assert(map != NULL);
+
+	T_BEGIN {
+		struct dict_sql_build_query build;
+		struct dict_sql_build_query_field field;
+		const char *query;
+
+		field.map = map;
+		field.value = t_strdup_printf("%lld", diff);
+
+		memset(&build, 0, sizeof(build));
+		build.dict = dict;
+		t_array_init(&build.fields, 1);
+		array_append(&build.fields, &field, 1);
+		build.extra_values = &values;
+		build.key1 = key[0];
+
+		query = sql_dict_set_query(&build);
+		sql_update(ctx->sql_ctx, query);
+	} T_END;
+}
+
+static void sql_dict_prev_inc_flush(struct sql_dict_transaction_context *ctx)
+{
+	sql_dict_atomic_inc_real(ctx, ctx->prev_inc_key, ctx->prev_inc_diff);
+	i_free_and_null(ctx->prev_inc_key);
+	ctx->prev_inc_map = NULL;
+}
+
+static bool
+sql_dict_maps_are_mergeable(struct sql_dict *dict,
+			    const struct dict_sql_map *map1,
+			    const struct dict_sql_map *map2,
+			    const char *map1_key, const char *map2_key,
+			    const ARRAY_TYPE(const_string) *map2_values)
+{
+	const struct dict_sql_map *map3;
+	ARRAY_TYPE(const_string) map1_values;
+	const char *const *v1, *const *v2;
+	unsigned int i, count1, count2;
+
+	if (strcmp(map1->table, map2->table) != 0)
+		return FALSE;
+	if (map1_key[0] != map2_key[0])
+		return FALSE;
+	if (map1_key[0] == DICT_PATH_PRIVATE[0]) {
+		if (strcmp(map1->username_field, map2->username_field) != 0)
+			return FALSE;
+	}
+
+	map3 = sql_dict_find_map(dict, map1_key, &map1_values);
+	i_assert(map3 == map1);
+
+	v1 = array_get(&map1_values, &count1);
+	v2 = array_get(map2_values, &count2);
+	if (count1 != count2)
+		return FALSE;
+
+	for (i = 0; i < count1; i++) {
+		if (strcmp(v1[i], v2[i]) != 0)
+			return FALSE;
+	}
+	return TRUE;
+}
+
 static void sql_dict_atomic_inc(struct dict_transaction_context *_ctx,
 				const char *key, long long diff)
 {
@@ -603,12 +739,41 @@ static void sql_dict_atomic_inc(struct d
 		return;
 	}
 
-	T_BEGIN {
+	if (ctx->prev_inc_map == NULL) {
+		/* see if we can merge this increment SQL query with the
+		   next one */
+		ctx->prev_inc_map = map;
+		ctx->prev_inc_key = i_strdup(key);
+		ctx->prev_inc_diff = diff;
+		return;
+	}
+	if (!sql_dict_maps_are_mergeable(dict, ctx->prev_inc_map, map,
+					 ctx->prev_inc_key, key, &values)) {
+		sql_dict_prev_inc_flush(ctx);
+		sql_dict_atomic_inc_real(ctx, key, diff);
+	} else T_BEGIN {
+		struct dict_sql_build_query build;
+		struct dict_sql_build_query_field *field;
 		const char *query;
 
-		query = sql_dict_set_query(dict, map, &values, key,
-					   t_strdup_printf("%lld", diff), TRUE);
+		memset(&build, 0, sizeof(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_inc_map;
+		field->value = t_strdup_printf("%lld", ctx->prev_inc_diff);
+		field = array_append_space(&build.fields);
+		field->map = map;
+		field->value = t_strdup_printf("%lld", diff);


More information about the dovecot-cvs mailing list