[dovecot-cvs] dovecot/src/lib-sql driver-mysql.c, 1.9, 1.10 driver-pgsql.c, 1.3, 1.4 sql-api-private.h, 1.3, 1.4 sql-api.c, 1.4, 1.5 sql-api.h, 1.4, 1.5

cras at dovecot.org cras at dovecot.org
Sat Dec 10 20:57:14 EET 2005


Update of /var/lib/cvs/dovecot/src/lib-sql
In directory talvi:/tmp/cvs-serv30564

Modified Files:
	driver-mysql.c driver-pgsql.c sql-api-private.h sql-api.c 
	sql-api.h 
Log Message:
Added support for transactions and synchronous SQL queries.



Index: driver-mysql.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-sql/driver-mysql.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- driver-mysql.c	9 Jun 2005 18:44:22 -0000	1.9
+++ driver-mysql.c	10 Dec 2005 18:57:11 -0000	1.10
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "buffer.h"
+#include "str.h"
 #include "sql-api-private.h"
 
 #ifdef HAVE_MYSQL
@@ -61,6 +62,12 @@
 	unsigned int fields_count;
 };
 
+struct mysql_transaction_context {
+	struct sql_transaction_context ctx;
+
+	string_t *queries;
+};
+
 extern struct sql_result driver_mysql_result;
 extern struct sql_result driver_mysql_error_result;
 
@@ -340,43 +347,59 @@
 	(void)driver_mysql_do_query(db, query, &conn);
 }
 
-static void driver_mysql_query(struct sql_db *_db, const char *query,
+static void driver_mysql_query(struct sql_db *db, const char *query,
 			       sql_query_callback_t *callback, void *context)
 {
+	struct sql_result *result;
+
+	result = sql_query_s(db, query);
+	result->callback = TRUE;
+	callback(result, context);
+	sql_result_free(result);
+}
+
+static struct sql_result *
+driver_mysql_query_s(struct sql_db *_db, const char *query)
+{
 	struct mysql_db *db = (struct mysql_db *)_db;
 	struct mysql_connection *conn;
-	struct mysql_result result;
+	struct mysql_result *result;
+
+	result = i_new(struct mysql_result, 1);
+	result->api = driver_mysql_result;
+	result->api.db = _db;
+	result->conn = conn;
 
 	switch (driver_mysql_do_query(db, query, &conn)) {
 	case 0:
 		/* not connected */
-		callback(&sql_not_connected_result, context);
-		return;
-
+		result->api = sql_not_connected_result;
+		break;
 	case 1:
 		/* query ok */
-		memset(&result, 0, sizeof(result));
-		result.api = driver_mysql_result;
-		result.api.db = _db;
-		result.conn = conn;
-		result.result = mysql_store_result(conn->mysql);
-		if (result.result == NULL)
+		result->result = mysql_store_result(conn->mysql);
+		if (result->result != NULL)
 			break;
-
-		callback(&result.api, context);
-                mysql_free_result(result.result);
-		return;
+		/* fallback */
 	case -1:
 		/* error */
+		result->api = driver_mysql_error_result;
 		break;
 	}
 
-	/* error */
-	memset(&result, 0, sizeof(result));
-	result.api = driver_mysql_error_result;
-	result.api.db = _db;
-	result.conn = conn;
-	callback(&result.api, context);
+	return &result->api;
+}
+
+static void driver_mysql_result_free(struct sql_result *_result)
+{
+	struct mysql_result *result = (struct mysql_result *)_result;
+
+	if (_result == &sql_not_connected_result || _result->callback)
+		return;
+
+	if (result->result != NULL)
+		mysql_free_result(result->result);
+	i_free(result);
 }
 
 static int driver_mysql_result_next_row(struct sql_result *_result)
@@ -468,18 +491,106 @@
 	return mysql_error(result->conn->mysql);
 }
 
+static struct sql_transaction_context *
+driver_mysql_transaction_begin(struct sql_db *db)
+{
+	struct mysql_transaction_context *ctx;
+
+	ctx = i_new(struct mysql_transaction_context, 1);
+	ctx->ctx.db = db;
+	ctx->queries = str_new(default_pool, 1024);
+	return &ctx->ctx;
+}
+
+static void
+driver_mysql_transaction_commit(struct sql_transaction_context *ctx,
+				sql_commit_callback_t *callback, void *context)
+{
+	const char *error;
+
+	if (sql_transaction_commit_s(ctx, &error) < 0)
+		callback(error, context);
+	else
+		callback(NULL, context);
+}
+
+static int
+driver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
+				  const char **error_r)
+{
+	struct mysql_transaction_context *ctx =
+		(struct mysql_transaction_context *)_ctx;
+	struct sql_result *result;
+	int ret = 0;
+
+	*error_r = NULL;
+
+	if (str_len(ctx->queries) > 0) {
+		str_append(ctx->queries, "COMMIT;");
+
+		result = sql_query_s(_ctx->db, str_c(ctx->queries));
+		if (sql_result_next_row(result) < 0) {
+			*error_r = sql_result_get_error(result);
+			ret = -1;
+		}
+		sql_result_free(result);
+	}
+	sql_transaction_rollback(_ctx);
+	return ret;
+}
+
+static void
+driver_mysql_transaction_rollback(struct sql_transaction_context *_ctx)
+{
+	struct mysql_transaction_context *ctx =
+		(struct mysql_transaction_context *)_ctx;
+
+	str_free(ctx->queries);
+	i_free(ctx);
+}
+
+static void
+driver_mysql_update(struct sql_transaction_context *_ctx, const char *query)
+{
+	struct mysql_transaction_context *ctx =
+		(struct mysql_transaction_context *)_ctx;
+
+	/* FIXME: with mysql we're just appending everything into one big
+	   string which gets committed in sql_transaction_commit(). we could
+	   avoid this if we knew for sure that transactions actually worked,
+	   but I don't know how to do that.. */
+	if (str_len(ctx->queries) == 0) {
+		/* try to use a transaction in any case,
+		   even if it doesn't work. */
+		str_append(ctx->queries, "BEGIN;");
+	}
+	str_append(ctx->queries, query);
+	str_append_c(ctx->queries, ';');
+}
+
 struct sql_db driver_mysql_db = {
+	"mysql",
+
 	driver_mysql_init,
 	driver_mysql_deinit,
 	driver_mysql_get_flags,
         driver_mysql_connect_all,
 	driver_mysql_exec,
-	driver_mysql_query
+	driver_mysql_query,
+	driver_mysql_query_s,
+
+	driver_mysql_transaction_begin,
+	driver_mysql_transaction_commit,
+	driver_mysql_transaction_commit_s,
+	driver_mysql_transaction_rollback,
+
+	driver_mysql_update
 };
 
 struct sql_result driver_mysql_result = {
 	NULL,
 
+	driver_mysql_result_free,
 	driver_mysql_result_next_row,
 	driver_mysql_result_get_fields_count,
 	driver_mysql_result_get_field_name,
@@ -487,7 +598,9 @@
 	driver_mysql_result_get_field_value,
 	driver_mysql_result_find_field_value,
 	driver_mysql_result_get_values,
-	driver_mysql_result_get_error
+	driver_mysql_result_get_error,
+
+	FALSE
 };
 
 static int
@@ -499,8 +612,11 @@
 struct sql_result driver_mysql_error_result = {
 	NULL,
 
+	driver_mysql_result_free,
 	driver_mysql_result_error_next_row,
 	NULL, NULL, NULL, NULL, NULL, NULL,
-	driver_mysql_result_get_error
+	driver_mysql_result_get_error,
+
+	FALSE
 };
 #endif

Index: driver-pgsql.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-sql/driver-pgsql.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- driver-pgsql.c	9 Jun 2005 18:44:22 -0000	1.3
+++ driver-pgsql.c	10 Dec 2005 18:57:11 -0000	1.4
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "ioloop.h"
+#include "ioloop-internal.h" /* kind of dirty, but it should be fine.. */
 #include "sql-api-private.h"
 
 #ifdef HAVE_PGSQL
@@ -22,6 +23,10 @@
 	struct pgsql_queue *queue, **queue_tail;
 	struct timeout *queue_to;
 
+	struct ioloop *ioloop;
+	struct sql_result *sync_result;
+
+	char *error;
 	time_t last_connect;
 	unsigned int connecting:1;
 	unsigned int connected:1;
@@ -49,6 +54,18 @@
 	struct pgsql_result *result;
 };
 
+struct pgsql_transaction_context {
+	struct sql_transaction_context ctx;
+
+	sql_commit_callback_t *callback;
+	void *context;
+
+	const char *error;
+
+	unsigned int opened:1;
+	unsigned int failed:1;
+};
+
 extern struct sql_result driver_pgsql_result;
 
 static void queue_send_next(struct pgsql_db *db);
@@ -167,11 +184,12 @@
 	struct pgsql_db *db = (struct pgsql_db *)_db;
 
         driver_pgsql_close(db);
+	i_free(db->error);
 	i_free(db);
 }
 
 static enum sql_db_flags
-driver_mysql_get_flags(struct sql_db *db __attr_unused__)
+driver_pgsql_get_flags(struct sql_db *db __attr_unused__)
 {
 	return 0;
 }
@@ -199,12 +217,14 @@
 		queue_send_next(db);
 }
 
-static void result_finish(struct pgsql_result *result)
+static void driver_pgsql_result_free(struct sql_result *_result)
 {
-	struct pgsql_db *db = (struct pgsql_db *)result->api.db;
+	struct pgsql_db *db = (struct pgsql_db *)_result->db;
+        struct pgsql_result *result = (struct pgsql_result *)_result;
+
+	if (result->api.callback)
+		return;
 
-	if (result->callback != NULL)
-		result->callback(&result->api, result->context);
 	if (result->pgres != NULL) {
 		PQclear(result->pgres);
 
@@ -212,6 +232,7 @@
 		i_assert(db->io == NULL);
 		db->io = io_add(PQsocket(db->pg), IO_READ,
 				consume_results, db);
+		db->io_dir = IO_READ;
 		consume_results(db);
 	} else {
 		db->querying = FALSE;
@@ -225,6 +246,20 @@
 		queue_send_next(db);
 }
 
+static void result_finish(struct pgsql_result *result)
+{
+	struct pgsql_db *db = (struct pgsql_db *)result->api.db;
+	int free_result = TRUE;
+
+	if (result->callback != NULL) {
+		result->api.callback = TRUE;
+		result->callback(&result->api, result->context);
+		free_result = db->sync_result != &result->api;
+	}
+	if (free_result)
+		driver_pgsql_result_free(&result->api);
+}
+
 static void get_result(void *context)
 {
         struct pgsql_result *result = context;
@@ -240,6 +275,7 @@
 		if (db->io == NULL) {
  			db->io = io_add(PQsocket(db->pg), IO_READ,
 					get_result, result);
+			db->io_dir = IO_READ;
 		}
 		return;
 	}
@@ -302,6 +338,7 @@
 		/* write blocks */
 		db->io = io_add(PQsocket(db->pg), IO_WRITE,
 				flush_callback, result);
+		db->io_dir = IO_WRITE;
 	} else {
 		get_result(result);
 	}
@@ -418,6 +455,44 @@
 	do_query(result, query);
 }
 
+static void pgsql_query_s_callback(struct sql_result *result, void *context)
+{
+        struct pgsql_db *db = context;
+
+	db->sync_result = result;
+	io_loop_stop(db->ioloop);
+}
+
+static struct sql_result *
+driver_pgsql_query_s(struct sql_db *_db, const char *query)
+{
+        struct pgsql_db *db = (struct pgsql_db *)_db;
+	struct io old_io;
+
+	if (db->io == NULL)
+		db->ioloop = io_loop_create(default_pool);
+	else {
+		/* have to move our existing I/O handler to new I/O loop */
+		old_io = *db->io;
+		io_remove(db->io);
+
+		db->ioloop = io_loop_create(default_pool);
+
+		db->io = io_add(old_io.fd, old_io.condition,
+				old_io.callback, old_io.context);
+	}
+
+	driver_pgsql_query(_db, query, pgsql_query_s_callback, db);
+
+	io_loop_run(db->ioloop);
+	io_loop_destroy(db->ioloop);
+	db->ioloop = NULL;
+
+	i_assert(db->io == NULL);
+
+	return db->sync_result;
+}
+
 static int driver_pgsql_result_next_row(struct sql_result *_result)
 {
 	struct pgsql_result *result = (struct pgsql_result *)_result;
@@ -543,6 +618,7 @@
 static const char *driver_pgsql_result_get_error(struct sql_result *_result)
 {
 	struct pgsql_result *result = (struct pgsql_result *)_result;
+	struct pgsql_db *db = (struct pgsql_db *)_result->db;
 	const char *msg;
 	size_t len;
 
@@ -552,22 +628,141 @@
 
 	/* Error message should contain trailing \n, we don't want it */
 	len = strlen(msg);
-	return len == 0 || msg[len-1] != '\n' ? msg :
-		t_strndup(msg, len-1);
+	i_free(db->error);
+	db->error = len == 0 || msg[len-1] != '\n' ?
+		i_strdup(msg) : i_strndup(msg, len-1);
+
+	return db->error;
+}
+
+static struct sql_transaction_context *
+driver_pgsql_transaction_begin(struct sql_db *db)
+{
+	struct pgsql_transaction_context *ctx;
+
+	ctx = i_new(struct pgsql_transaction_context, 1);
+	ctx->ctx.db = db;
+	return &ctx->ctx;
+}
+
+static void
+transaction_commit_callback(struct sql_result *result, void *context)
+{
+	struct pgsql_transaction_context *ctx =
+		(struct pgsql_transaction_context *)context;
+
+	if (sql_result_next_row(result) < 0)
+		ctx->callback(sql_result_get_error(result), ctx->context);
+	else
+		ctx->callback(NULL, ctx->context);
+}
+
+static void
+driver_pgsql_transaction_commit(struct sql_transaction_context *_ctx,
+				sql_commit_callback_t *callback, void *context)
+{
+	struct pgsql_transaction_context *ctx =
+		(struct pgsql_transaction_context *)_ctx;
+
+	if (ctx->failed) {
+		callback(ctx->error, context);
+		sql_exec(_ctx->db, "ROLLBACK");
+		i_free(ctx);
+		return;
+	}
+
+	ctx->callback = callback;
+	ctx->context = context;
+
+	sql_query(_ctx->db, "COMMIT", transaction_commit_callback, ctx);
+}
+
+static int
+driver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
+				  const char **error_r)
+{
+	struct pgsql_transaction_context *ctx =
+		(struct pgsql_transaction_context *)_ctx;
+	struct sql_result *result;
+
+	if (ctx->failed) {
+		*error_r = ctx->error;
+		sql_exec(_ctx->db, "ROLLBACK");
+	} else {
+		result = sql_query_s(_ctx->db, "COMMIT");
+		if (sql_result_next_row(result) < 0)
+			*error_r = sql_result_get_error(result);
+		else
+			*error_r = NULL;
+		sql_result_free(result);
+	}
+
+	i_free(ctx);
+	return *error_r == NULL ? 0 : -1;
+}
+
+static void
+driver_pgsql_transaction_rollback(struct sql_transaction_context *_ctx)
+{
+	struct pgsql_transaction_context *ctx =
+		(struct pgsql_transaction_context *)_ctx;
+
+	sql_exec(_ctx->db, "ROLLBACK");
+	i_free(ctx);
+}
+
+static void
+transaction_update_callback(struct sql_result *result, void *context)
+{
+	struct pgsql_transaction_context *ctx =
+		(struct pgsql_transaction_context *)context;
+
+	if (sql_result_next_row(result) < 0) {
+		ctx->failed = TRUE;
+		ctx->error = sql_result_get_error(result);
+	}
+}
+
+static void
+driver_pgsql_update(struct sql_transaction_context *_ctx, const char *query)
+{
+	struct pgsql_transaction_context *ctx =
+		(struct pgsql_transaction_context *)_ctx;
+
+	if (ctx->failed)
+		return;
+
+	if (!ctx->opened) {
+		ctx->opened = TRUE;
+		sql_query(_ctx->db, "BEGIN", transaction_update_callback, ctx);
+	}
+
+	sql_query(_ctx->db, query, transaction_update_callback, ctx);
 }
 
 struct sql_db driver_pgsql_db = {
+	"pgsql",
+
 	driver_pgsql_init,
 	driver_pgsql_deinit,
-        driver_mysql_get_flags,
+        driver_pgsql_get_flags,
 	driver_pgsql_connect,
 	driver_pgsql_exec,
-	driver_pgsql_query
+	driver_pgsql_query,
+	driver_pgsql_query_s,
+
+	driver_pgsql_transaction_begin,
+	driver_pgsql_transaction_commit,
+	driver_pgsql_transaction_commit_s,
+	driver_pgsql_transaction_rollback,
+
+	driver_pgsql_update
 };
 
 struct sql_result driver_pgsql_result = {
 	NULL,
 
+	driver_pgsql_result_free,
 	driver_pgsql_result_next_row,
 	driver_pgsql_result_get_fields_count,
 	driver_pgsql_result_get_field_name,
@@ -575,7 +770,9 @@
 	driver_pgsql_result_get_field_value,
 	driver_pgsql_result_find_field_value,
 	driver_pgsql_result_get_values,
-	driver_pgsql_result_get_error
+	driver_pgsql_result_get_error,
+
+	FALSE
 };
 
 #endif

Index: sql-api-private.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-sql/sql-api-private.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- sql-api-private.h	9 Jun 2005 18:44:22 -0000	1.3
+++ sql-api-private.h	10 Dec 2005 18:57:11 -0000	1.4
@@ -4,6 +4,8 @@
 #include "sql-api.h"
 
 struct sql_db {
+	const char *name;
+
 	struct sql_db *(*init)(const char *connect_string);
 	void (*deinit)(struct sql_db *db);
 
@@ -13,11 +15,23 @@
 	void (*exec)(struct sql_db *db, const char *query);
 	void (*query)(struct sql_db *db, const char *query,
 		      sql_query_callback_t *callback, void *context);
+	struct sql_result *(*query_s)(struct sql_db *db, const char *query);
+
+	struct sql_transaction_context *(*transaction_begin)(struct sql_db *db);
+	void (*transaction_commit)(struct sql_transaction_context *ctx,
+				   sql_commit_callback_t *callback,
+				   void *context);
+	int (*transaction_commit_s)(struct sql_transaction_context *ctx,
+				    const char **error_r);
+	void (*transaction_rollback)(struct sql_transaction_context *ctx);
+
+	void (*update)(struct sql_transaction_context *ctx, const char *query);
 };
 
 struct sql_result {
 	struct sql_db *db;
 
+	void (*free)(struct sql_result *result);
 	int (*next_row)(struct sql_result *result);
 
 	unsigned int (*get_fields_count)(struct sql_result *result);
@@ -32,6 +46,12 @@
 	const char *const *(*get_values)(struct sql_result *result);
 
 	const char *(*get_error)(struct sql_result *result);
+
+	unsigned int callback:1;
+};
+
+struct sql_transaction_context {
+	struct sql_db *db;
 };
 
 extern struct sql_db driver_mysql_db;

Index: sql-api.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-sql/sql-api.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- sql-api.c	9 Jun 2005 18:44:22 -0000	1.4
+++ sql-api.c	10 Dec 2005 18:57:11 -0000	1.5
@@ -3,17 +3,25 @@
 #include "lib.h"
 #include "sql-api-private.h"
 
-struct sql_db *sql_init(const char *db_driver,
-			const char *connect_string __attr_unused__)
-{
+struct sql_db *sql_db_drivers[] = {
 #ifdef HAVE_PGSQL
-	if (strcmp(db_driver, "pgsql") == 0)
-		return driver_pgsql_db.init(connect_string);
+	&driver_pgsql_db,
 #endif
 #ifdef HAVE_MYSQL
-	if (strcmp(db_driver, "mysql") == 0)
-		return driver_mysql_db.init(connect_string);
+	&driver_mysql_db,
 #endif
+	NULL
+};
+
+struct sql_db *sql_init(const char *db_driver,
+			const char *connect_string __attr_unused__)
+{
+	int i;
+
+	for (i = 0; sql_db_drivers[i] != NULL; i++) {
+		if (strcmp(db_driver, sql_db_drivers[i]->name) == 0)
+			return sql_db_drivers[i]->init(connect_string);
+	}
 
 	i_fatal("Unknown database driver '%s'", db_driver);
 }
@@ -44,6 +52,16 @@
 	db->query(db, query, callback, context);
 }
 
+struct sql_result *sql_query_s(struct sql_db *db, const char *query)
+{
+	return db->query_s(db, query);
+}
+
+void sql_result_free(struct sql_result *result)
+{
+	result->free(result);
+}
+
 int sql_result_next_row(struct sql_result *result)
 {
 	return result->next_row(result);
@@ -87,6 +105,11 @@
 	return result->get_error(result);
 }
 
+static void
+sql_result_not_connected_free(struct sql_result *result __attr_unused__)
+{
+}
+
 static int
 sql_result_not_connected_next_row(struct sql_result *result __attr_unused__)
 {
@@ -99,10 +122,40 @@
 	return "Not connected to database";
 }
 
+struct sql_transaction_context *sql_transaction_begin(struct sql_db *db)
+{
+	return db->transaction_begin(db);
+}
+
+void sql_transaction_commit(struct sql_transaction_context *ctx,
+			    sql_commit_callback_t *callback, void *context)
+{
+	ctx->db->transaction_commit(ctx, callback, context);
+}
+
+int sql_transaction_commit_s(struct sql_transaction_context *ctx,
+			     const char **error_r)
+{
+	return ctx->db->transaction_commit_s(ctx, error_r);
+}
+
+void sql_transaction_rollback(struct sql_transaction_context *ctx)
+{
+	ctx->db->transaction_rollback(ctx);
+}
+
+void sql_update(struct sql_transaction_context *ctx, const char *query)
+{
+	ctx->db->update(ctx, query);
+}
+
 struct sql_result sql_not_connected_result = {
 	NULL,
 
+	sql_result_not_connected_free,
 	sql_result_not_connected_next_row,
 	NULL, NULL, NULL, NULL, NULL, NULL,
-	sql_result_not_connected_get_error
+	sql_result_not_connected_get_error,
+
+	FALSE
 };

Index: sql-api.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-sql/sql-api.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- sql-api.h	7 Aug 2005 11:41:25 -0000	1.4
+++ sql-api.h	10 Dec 2005 18:57:11 -0000	1.5
@@ -1,18 +1,21 @@
 #ifndef __SQL_API_H
 #define __SQL_API_H
 
+/* This SQL API is designed to work asynchronously. The underlying drivers
+   however may not. */
+
 enum sql_db_flags {
 	/* Set if queries are not executed asynchronously */
 	SQL_DB_FLAG_BLOCKING		= 0x01,
 };
 
-/* This SQL API is designed to work asynchronously. The underlying drivers
-   however may not. */
-
 struct sql_db;
 struct sql_result;
 
 typedef void sql_query_callback_t(struct sql_result *result, void *context);
+typedef void sql_commit_callback_t(const char *error, void *context);
+
+extern struct sql_db *sql_db_drivers[];
 
 /* Initialize database connections. db_driver is the database driver name,
    eg. "mysql" or "pgsql". connect_string is driver-specific. */
@@ -32,11 +35,16 @@
 /* Execute SQL query and return result in callback. */
 void sql_query(struct sql_db *db, const char *query,
 	       sql_query_callback_t *callback, void *context);
+/* Execute blocking SQL query and return result. */
+struct sql_result *sql_query_s(struct sql_db *db, const char *query);
 
 /* 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. */
 int sql_result_next_row(struct sql_result *result);
 
+/* Needs to be called only with sql_query_s(). */
+void sql_result_free(struct sql_result *result);
+
 /* Return number of fields in result. */
 unsigned int sql_result_get_fields_count(struct sql_result *result);
 /* Return name of the given field index. */
@@ -56,4 +64,18 @@
 /* Return last error message in result. */
 const char *sql_result_get_error(struct sql_result *result);
 
+/* Begin a new transaction. Currently you're limited to only one open
+   transaction at a time. */
+struct sql_transaction_context *sql_transaction_begin(struct sql_db *db);
+/* Commit transaction. */
+void sql_transaction_commit(struct sql_transaction_context *ctx,
+			    sql_commit_callback_t *callback, void *context);
+/* Synchronous commit. Returns 0 if ok, -1 if error. */
+int sql_transaction_commit_s(struct sql_transaction_context *ctx,
+			     const char **error_r);
+void sql_transaction_rollback(struct sql_transaction_context *ctx);
+
+/* Execute query in given transaction. */
+void sql_update(struct sql_transaction_context *ctx, const char *query);
+
 #endif



More information about the dovecot-cvs mailing list