dovecot-2.2: dict-sql: Added support for async operations.

dovecot at dovecot.org dovecot at dovecot.org
Wed Sep 2 14:38:25 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/0d04ac4d43ca
changeset: 19066:0d04ac4d43ca
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Sep 02 17:37:16 2015 +0300
description:
dict-sql: Added support for async operations.

diffstat:

 src/lib-dict/dict-sql.c |  162 ++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 136 insertions(+), 26 deletions(-)

diffs (277 lines):

diff -r 3de8de46f4a8 -r 0d04ac4d43ca src/lib-dict/dict-sql.c
--- a/src/lib-dict/dict-sql.c	Wed Sep 02 17:36:47 2015 +0300
+++ b/src/lib-dict/dict-sql.c	Wed Sep 02 17:37:16 2015 +0300
@@ -63,6 +63,9 @@
 	pool_t inc_row_pool;
 	struct sql_dict_inc_row *inc_row;
 
+	dict_transaction_commit_callback_t *async_callback;
+	void *async_context;
+
 	unsigned int failed:1;
 };
 
@@ -106,6 +109,12 @@
 	pool_unref(&dict->pool);
 }
 
+static int sql_dict_wait(struct dict *dict ATTR_UNUSED)
+{
+	/* FIXME: lib-sql doesn't support this yet */
+	return 0;
+}
+
 static bool
 dict_sql_map_match(const struct dict_sql_map *map, const char *path,
 		   ARRAY_TYPE(const_string) *values, unsigned int *pat_len_r,
@@ -266,32 +275,44 @@
 	}
 }
 
+static int sql_lookup_get_query(struct sql_dict *dict, const char *key,
+				string_t *query)
+{
+	const struct dict_sql_map *map;
+	ARRAY_TYPE(const_string) values;
+
+	map = sql_dict_find_map(dict, key, &values);
+	if (map == NULL) {
+		i_error("sql dict lookup: Invalid/unmapped key: %s", key);
+		return -1;
+	}
+	str_printfa(query, "SELECT %s FROM %s",
+		    map->value_field, map->table);
+	sql_dict_where_build(dict, map, &values, key[0],
+			     SQL_DICT_RECURSE_NONE, query);
+	return 0;
+}
+
 static int sql_dict_lookup(struct dict *_dict, pool_t pool,
 			   const char *key, const char **value_r)
 {
 	struct sql_dict *dict = (struct sql_dict *)_dict;
-	const struct dict_sql_map *map;
-	ARRAY_TYPE(const_string) values;
-	struct sql_result *result;
+	struct sql_result *result = NULL;
 	int ret;
 
-	map = sql_dict_find_map(dict, key, &values);
-	if (map == NULL) {
-		i_error("sql dict lookup: Invalid/unmapped key: %s", key);
-		*value_r = NULL;
-		return 0;
-	}
-
 	T_BEGIN {
 		string_t *query = t_str_new(256);
 
-		str_printfa(query, "SELECT %s FROM %s",
-			    map->value_field, map->table);
-		sql_dict_where_build(dict, map, &values, key[0],
-				     SQL_DICT_RECURSE_NONE, query);
-		result = sql_query_s(dict->db, str_c(query));
+		ret = sql_lookup_get_query(dict, key, query);
+		if (ret == 0)
+			result = sql_query_s(dict->db, str_c(query));
 	} T_END;
 
+	if (ret < 0) {
+		*value_r = NULL;
+		return -1;
+	}
+
 	ret = sql_result_next_row(result);
 	if (ret <= 0) {
 		if (ret < 0) {
@@ -308,6 +329,48 @@
 	return ret;
 }
 
+struct sql_dict_lookup_context {
+	dict_lookup_callback_t *callback;
+	void *context;
+};
+
+static void
+sql_dict_lookup_async_callback(struct sql_result *sql_result,
+			       struct sql_dict_lookup_context *ctx)
+{
+	struct dict_lookup_result result;
+
+	memset(&result, 0, sizeof(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_result_get_field_value(sql_result, 0);
+	ctx->callback(&result, ctx->context);
+
+	i_free(ctx);
+}
+
+static void
+sql_dict_lookup_async(struct dict *_dict, const char *key,
+		      dict_lookup_callback_t *callback, void *context)
+{
+	struct sql_dict *dict = (struct sql_dict *)_dict;
+	struct sql_dict_lookup_context *ctx;
+
+	T_BEGIN {
+		string_t *query = t_str_new(256);
+
+		if (sql_lookup_get_query(dict, key, query) == 0) {
+			ctx = i_new(struct sql_dict_lookup_context, 1);
+			ctx->callback = callback;
+			ctx->context = context;
+			sql_query(dict->db, str_c(query),
+				  sql_dict_lookup_async_callback, ctx);
+		}
+	} T_END;
+}
+
 static const struct dict_sql_map *
 sql_dict_iterate_find_next_map(struct sql_dict_iterate_context *ctx,
 			       ARRAY_TYPE(const_string) *values)
@@ -356,8 +419,10 @@
 	if (map == NULL)
 		return FALSE;
 
-	if (ctx->result != NULL)
+	if (ctx->result != NULL) {
 		sql_result_unref(ctx->result);
+		ctx->result = NULL;
+	}
 
 	str_append(query, "SELECT ");
 	if ((ctx->flags & DICT_ITERATE_FLAG_NO_VALUE) == 0)
@@ -402,6 +467,15 @@
 	return TRUE;
 }
 
+static void sql_dict_iterate_callback(struct sql_result *result,
+				      struct sql_dict_iterate_context *ctx)
+{
+	sql_result_ref(result);
+	ctx->result = result;
+	if (ctx->ctx.async_callback != NULL)
+		ctx->ctx.async_callback(ctx->ctx.async_context);
+}
+
 static bool sql_dict_iterate_next_query(struct sql_dict_iterate_context *ctx)
 {
 	struct sql_dict *dict = (struct sql_dict *)ctx->ctx.dict;
@@ -413,14 +487,17 @@
 		ret = sql_dict_iterate_build_next_query(ctx, query);
 		if (!ret) {
 			/* failed */
+		} else if ((ctx->flags & DICT_ITERATE_FLAG_ASYNC) == 0) {
+			ctx->result = sql_query_s(dict->db, str_c(query));
 		} else {
-			ctx->result = sql_query_s(dict->db, str_c(query));
+			i_assert(ctx->result == NULL);
+			sql_query(dict->db, str_c(query),
+				  sql_dict_iterate_callback, ctx);
 		}
 	} T_END;
 	return ret;
 }
 
-
 static struct dict_iterate_context *
 sql_dict_iterate_init(struct dict *_dict, const char *const *paths,
 		      enum dict_iterate_flags flags)
@@ -445,6 +522,7 @@
 		i_error("sql dict iterate: Invalid/unmapped path: %s",
 			paths[0]);
 		ctx->result = NULL;
+		ctx->failed = TRUE;
 		return &ctx->ctx;
 	}
 	return &ctx->ctx;
@@ -459,12 +537,21 @@
 	unsigned int i, count;
 	int ret;
 
-	if (ctx->result == NULL) {
-		ctx->failed = TRUE;
+	_ctx->has_more = FALSE;
+	if (ctx->failed)
 		return FALSE;
-	}
 
-	while ((ret = sql_result_next_row(ctx->result)) == 0) {
+	for (;;) {
+		if (ctx->result == NULL) {
+			/* wait for async lookup to finish */
+			i_assert((ctx->flags & DICT_ITERATE_FLAG_ASYNC) != 0);
+			_ctx->has_more = TRUE;
+			return FALSE;
+		}
+
+		ret = sql_result_next_row(ctx->result);
+		if (ret != 0)
+			break;
 		/* see if there are more results in the next map.
 		   don't do it if we're looking for an exact match, since we
 		   already should have handled it. */
@@ -550,9 +637,26 @@
 	return FALSE;
 }
 
+static void
+sql_dict_transaction_commit_callback(const char *error,
+				     struct sql_dict_transaction_context *ctx)
+{
+	int ret;
+
+	if (error == NULL)
+		ret = sql_dict_transaction_has_nonexistent(ctx) ? 0 : 1;
+	else {
+		i_error("sql dict: commit failed: %s", error);
+		ret = -1;
+	}
+
+	if (ctx->async_callback != NULL)
+		ctx->async_callback(ret, ctx->async_context);
+	sql_dict_transaction_free(ctx);
+}
+
 static int
-sql_dict_transaction_commit(struct dict_transaction_context *_ctx,
-			    bool async ATTR_UNUSED,
+sql_dict_transaction_commit(struct dict_transaction_context *_ctx, bool async,
 			    dict_transaction_commit_callback_t *callback,
 			    void *context)
 {
@@ -570,6 +674,12 @@
 	} else if (!_ctx->changed) {
 		/* nothing changed, no need to commit */
 		sql_transaction_rollback(&ctx->sql_ctx);
+	} else if (async) {
+		ctx->async_callback = callback;
+		ctx->async_context = context;
+		sql_transaction_commit(&ctx->sql_ctx,
+			sql_dict_transaction_commit_callback, ctx);
+		return 1;
 	} else {
 		if (sql_transaction_commit_s(&ctx->sql_ctx, &error) < 0) {
 			i_error("sql dict: commit failed: %s", error);
@@ -935,7 +1045,7 @@
 	{
 		sql_dict_init,
 		sql_dict_deinit,
-		NULL,
+		sql_dict_wait,
 		sql_dict_lookup,
 		sql_dict_iterate_init,
 		sql_dict_iterate,
@@ -947,7 +1057,7 @@
 		sql_dict_unset,
 		sql_dict_append,
 		sql_dict_atomic_inc,
-		NULL
+		sql_dict_lookup_async
 	}
 };
 


More information about the dovecot-cvs mailing list