changeset 22354:b9a2a6135419

lib-sql: Add API support for asynchronously iterating over rows. sql_query() can already do an async lookup, but the full result needs to be available immediately. This can be inefficient for large results. Add a new SQL_RESULT_NEXT_MORE return value and sql_result_more() for asynchronously requesting more results. This changes the API a bit, but isn't done by default by any drivers yet. Also callers that can't handle this are hopefully checking for "ret < 0", which allows them to handle such an async-more request as an error instead. sql_result_next_row() will be changed to return enum in a separate commit to keep backwards compatibility in v2.2.x.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 17 Jul 2017 14:22:35 +0300
parents be6d20b5644f
children d950ffd7cbe0
files src/lib-sql/driver-cassandra.c src/lib-sql/driver-mysql.c src/lib-sql/driver-pgsql.c src/lib-sql/driver-sqlite.c src/lib-sql/sql-api-private.h src/lib-sql/sql-api.c src/lib-sql/sql-api.h
diffstat 7 files changed, 66 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-sql/driver-cassandra.c	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/driver-cassandra.c	Mon Jul 17 14:22:35 2017 +0300
@@ -1511,7 +1511,8 @@
 		driver_cassandra_result_get_field_value_binary,
 		driver_cassandra_result_find_field_value,
 		driver_cassandra_result_get_values,
-		driver_cassandra_result_get_error
+		driver_cassandra_result_get_error,
+		NULL,
 	}
 };
 
--- a/src/lib-sql/driver-mysql.c	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/driver-mysql.c	Mon Jul 17 14:22:35 2017 +0300
@@ -694,7 +694,8 @@
 		driver_mysql_result_get_field_value_binary,
 		driver_mysql_result_find_field_value,
 		driver_mysql_result_get_values,
-		driver_mysql_result_get_error
+		driver_mysql_result_get_error,
+		NULL,
 	}
 };
 
@@ -709,7 +710,8 @@
 		driver_mysql_result_free,
 		driver_mysql_result_error_next_row,
 		NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-		driver_mysql_result_get_error
+		driver_mysql_result_get_error,
+		NULL,
 	},
 	.failed_try_retry = TRUE
 };
--- a/src/lib-sql/driver-pgsql.c	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/driver-pgsql.c	Mon Jul 17 14:22:35 2017 +0300
@@ -1181,7 +1181,8 @@
 		driver_pgsql_result_get_field_value_binary,
 		driver_pgsql_result_find_field_value,
 		driver_pgsql_result_get_values,
-		driver_pgsql_result_get_error
+		driver_pgsql_result_get_error,
+		NULL,
 	}
 };
 
--- a/src/lib-sql/driver-sqlite.c	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/driver-sqlite.c	Mon Jul 17 14:22:35 2017 +0300
@@ -441,7 +441,8 @@
 		driver_sqlite_result_get_field_value_binary,
 		driver_sqlite_result_find_field_value,
 		driver_sqlite_result_get_values,
-		driver_sqlite_result_get_error
+		driver_sqlite_result_get_error,
+		NULL,
 	}
 };
 
@@ -456,7 +457,8 @@
 		driver_sqlite_result_free,
 		driver_sqlite_result_error_next_row,
 		NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-		driver_sqlite_result_get_error
+		driver_sqlite_result_get_error,
+		NULL,
 	}
 };
 
--- a/src/lib-sql/sql-api-private.h	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/sql-api-private.h	Mon Jul 17 14:22:35 2017 +0300
@@ -126,6 +126,8 @@
 	const char *const *(*get_values)(struct sql_result *result);
 
 	const char *(*get_error)(struct sql_result *result);
+	void (*more)(struct sql_result **result, bool async,
+		     sql_query_callback_t *callback, void *context);
 };
 
 struct sql_field_map {
--- a/src/lib-sql/sql-api.c	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/sql-api.c	Mon Jul 17 14:22:35 2017 +0300
@@ -278,6 +278,30 @@
 	return 1;
 }
 
+#undef sql_result_more
+void sql_result_more(struct sql_result **result,
+		     sql_query_callback_t *callback, void *context)
+{
+	i_assert((*result)->v.more != NULL);
+
+	(*result)->v.more(result, TRUE, callback, context);
+}
+
+static void
+sql_result_more_sync_callback(struct sql_result *result, void *context)
+{
+	struct sql_result **dest_result = context;
+
+	*dest_result = result;
+}
+
+void sql_result_more_s(struct sql_result **result)
+{
+	i_assert((*result)->v.more != NULL);
+
+	(*result)->v.more(result, FALSE, sql_result_more_sync_callback, result);
+}
+
 unsigned int sql_result_get_fields_count(struct sql_result *result)
 {
 	return result->v.get_fields_count(result);
@@ -480,7 +504,8 @@
 		sql_result_not_connected_free,
 		sql_result_not_connected_next_row,
 		NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-		sql_result_not_connected_get_error
+		sql_result_not_connected_get_error,
+		NULL,
 	},
 	.failed_try_retry = TRUE
 };
--- a/src/lib-sql/sql-api.h	Thu Jul 06 12:40:33 2017 +0300
+++ b/src/lib-sql/sql-api.h	Mon Jul 17 14:22:35 2017 +0300
@@ -31,6 +31,17 @@
 	SQL_RESULT_ERROR_TYPE_WRITE_UNCERTAIN
 };
 
+enum sql_result_next {
+	/* Row was returned */
+	SQL_RESULT_NEXT_OK = 1,
+	/* There are no more rows */
+	SQL_RESULT_NEXT_LAST = 0,
+	/* Error occurred - see sql_result_get_error*() */
+	SQL_RESULT_NEXT_ERROR = -1,
+	/* There are more results - call sql_result_more() */
+	SQL_RESULT_NEXT_MORE = -99
+};
+
 #define SQL_DEF_STRUCT(name, struct_name, type, c_type) \
 	{ (type) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
 		((struct struct_name *)0)->name, c_type), \
@@ -106,10 +117,23 @@
 			    const struct sql_field_def *fields,
 			    void *dest, size_t dest_size);
 
-/* Go to next row, returns 1 if ok, 0 if this was the last row or -1 if error
-   occurred. This needs to be the first call for result. */
+/* Go to next row. See enum sql_result_next. */
 int sql_result_next_row(struct sql_result *result);
 
+/* If sql_result_next_row() returned SQL_RESULT_NEXT_MORE, this can be called
+   to continue returning more results. The result is freed with this call, so
+   it must not be accesed anymore until the callback is finished. */
+void sql_result_more(struct sql_result **result,
+		     sql_query_callback_t *callback, void *context);
+#define sql_result_more(result, callback, context) \
+	sql_result_more(result + \
+		CALLBACK_TYPECHECK(callback, void (*)( \
+			struct sql_result *, typeof(context))), \
+		(sql_query_callback_t *)callback, context)
+/* Synchronous version of sql_result_more(). The result will be replaced with
+   the new result. */
+void sql_result_more_s(struct sql_result **result);
+
 void sql_result_ref(struct sql_result *result);
 /* Needs to be called only with sql_query_s() or when result has been
    explicitly referenced. */