libgda r3374 - in trunk: . doc/C doc/C/tmpl libgda libgda/thread-wrapper samples samples/AsyncExec tests/multi-threading tools



Author: vivien
Date: Tue Apr 14 18:49:11 2009
New Revision: 3374
URL: http://svn.gnome.org/viewvc/libgda?rev=3374&view=rev

Log:
2009-04-14  Vivien Malerba <malerba gnome-db org>

	* libgda/thread-wrapper/Makefile.am:
	* libgda/thread-wrapper/gda-thread-blob-op.[ch]: wrapper for GdaBlobOp
	objects
	* libgda/thread-wrapper/gda-thread-wrapper.[ch]:
	  - loosened locking
	  - allow the wrapped thread to use the GdaThreadWrapper object
	  - API change in gda_thread_wrapper_fetch_result()
	* libgda/thread-wrapper/gda-thread-recordset.c:
	  - use the new GdaThreadBlobOp object
	  - adaptations to the API change in gda_thread_wrapper_fetch_result()
	* libgda/thread-wrapper/gda-thread-provider.[ch]:
	  - renamed gda_thread_provider_get_type() to _gda_thread_provider_get_type()
	    to avoid exporting the symbol
	  - implemented asynchronous execution
	  - adaptations to the API change in gda_thread_wrapper_fetch_result()
	* tests/multi-threading/check_wrapper.c:
	  - adaptations to the API change in gda_thread_wrapper_fetch_result()
	* libgda/gda-connection.c:
	  - instanciate the database provider only when really needed
	  - loosened locking
	* tools/test_blob.sh: adapted to version 4.1
	* doc/C: doc. updates regarding multi-threading and asynchronous statement
	execution
	* Makefile.am:
	* samples/Makefile:
	* samples/README:
	* samples/AsyncExec: new example about hos to use the asynchronous statement
	execution API


Added:
   trunk/libgda/thread-wrapper/gda-thread-blob-op.c
   trunk/libgda/thread-wrapper/gda-thread-blob-op.h
   trunk/samples/AsyncExec/
   trunk/samples/AsyncExec/Makefile
   trunk/samples/AsyncExec/README
   trunk/samples/AsyncExec/example.c
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/doc/C/prov-writing.xml
   trunk/doc/C/tmpl/gda-thread-wrapper.sgml
   trunk/libgda/gda-connection-internal.h
   trunk/libgda/gda-connection.c
   trunk/libgda/thread-wrapper/Makefile.am
   trunk/libgda/thread-wrapper/gda-thread-provider.c
   trunk/libgda/thread-wrapper/gda-thread-provider.h
   trunk/libgda/thread-wrapper/gda-thread-recordset.c
   trunk/libgda/thread-wrapper/gda-thread-wrapper.c
   trunk/libgda/thread-wrapper/gda-thread-wrapper.h
   trunk/samples/Makefile
   trunk/samples/README
   trunk/tests/multi-threading/check_wrapper.c
   trunk/tools/test_blob.sh

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Tue Apr 14 18:49:11 2009
@@ -71,7 +71,10 @@
 	samples/Tree/example.c \
 	samples/SqlBuilder/Makefile \
         samples/SqlBuilder/README \
-        samples/SqlBuilder/example.c
+        samples/SqlBuilder/example.c \
+	samples/AsyncExec/Makefile \
+        samples/AsyncExec/README \
+        samples/AsyncExec/example.c
 
 EXTRA_DIST = \
 	COPYING \

Modified: trunk/doc/C/prov-writing.xml
==============================================================================
--- trunk/doc/C/prov-writing.xml	(original)
+++ trunk/doc/C/prov-writing.xml	Tue Apr 14 18:49:11 2009
@@ -34,13 +34,18 @@
     <title>Synchronous / asynchronous mode</title>
     <para>
       All the provider's commands are executed in a synchronous mode (the caller is blocked until the provider's
-      method terminates). However some virtual methods have the <parameter>task_id</parameter>, 
-      <parameter>async_cb</parameter> and <parameter>cb_data</parameter> which can be set when an asynchronous mode 
-      is required; asynchronous mode is requested if and only if the <parameter>async_cb</parameter> is not NULL.
+      method terminates). However some virtual methods have the a <parameter>task_id</parameter> parameter, 
+      an <parameter>async_cb</parameter> or <parameter>exec_cb</parameter> callback function pointer and 
+      a <parameter>cb_data</parameter> parameter which can be set when an asynchronous mode 
+      is required; asynchronous mode is requested if and only if the <parameter>async_cb</parameter> or
+      <parameter>exec_cb</parameter> parmeter is not NULL.
     </para>
     <para>
-      When an asynchronous mode is requested, the method should return a temporary result and set a task identifier
-      into the <parameter>task_id</parameter> parameter if not NULL.
+      When an asynchronous mode is requested, the method should return TRUE if it returns a boolean or NULL if it returns a
+      pointer and set a task identifier
+      into the <parameter>task_id</parameter> parameter if not NULL. The task identifier is passed again when
+      the <parameter>async_cb</parameter> or <parameter>exec_cb</parameter> callback functions are called by the
+      provider when the execution is finished.
     </para>
     <para>
       When the provider's method terminates, it then should call the function passed as <parameter>async_cb</parameter>
@@ -262,6 +267,31 @@
 	<link linkend="gda-connection-statement-execute">gda_connection_statement_execute()</link> for more information, note that
 	this method is also always the one called when any of the gda_connection_statement_execute*() methods are called.
       </para>
+      <para>
+	A non NULL <parameter>exec_cb</parameter> parameter specifies that the user requests an asynchronous execution: the
+	function has to return the NULL value immediately (it must not be blocking), and the <parameter>task_id</parameter>
+	parameter must be set to contain a provider specific task ID.
+	The <parameter>exec_cb</parameter> parameter points to a callback function (specified by the
+	<link linkend="GdaConnection">GdaConnection</link>) which the provider has to call once the statement has been
+	executed, using the same task ID which was defined when the statement_execute() function was called, and
+	the <parameter>cb_data</parameter> specified when the statement_execute() function was called.
+      </para>
+      <para>
+	Note that if an asynchronous execution is requested, then the <parameter>stmt</parameter>, <parameter>params</parameter>,
+	<parameter>col_types</parameter>, <parameter>last_inserted_row</parameter>, and <parameter>cb_data</parameter>
+	parameters are supposed to exist and not be altered during the time the statement is executed (the
+	<link linkend="GdaConnection">GdaConnection</link> ensures this) which means it's not necessary to make copies
+	of them during the execution.
+      </para>
+    </sect2>
+    <sect2>
+      <title>handle_async()</title>
+      <para>
+	This method, if implemented, is called to give the database provider a chance to execute some code in case an
+	asynchronous statement's execution has been requested. Often providers will send some data over the network to the
+	database server when the statement_execute() is called, and implement this virtual function as a way to get
+	some data from the database server to see if the execution is terminated.
+      </para>
     </sect2>
   </sect1>
 

Modified: trunk/doc/C/tmpl/gda-thread-wrapper.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-thread-wrapper.sgml	(original)
+++ trunk/doc/C/tmpl/gda-thread-wrapper.sgml	Tue Apr 14 18:49:11 2009
@@ -103,7 +103,7 @@
 
 @wrapper: 
 @may_lock: 
- out_id: 
+ exp_id: 
 @error: 
 @Returns: 
 

Modified: trunk/libgda/gda-connection-internal.h
==============================================================================
--- trunk/libgda/gda-connection-internal.h	(original)
+++ trunk/libgda/gda-connection-internal.h	Tue Apr 14 18:49:11 2009
@@ -24,6 +24,7 @@
 #define __GDA_CONNECTION_INTERNAL_H_
 
 #include <libgda/gda-decl.h>
+#include <libgda/gda-server-provider.h>
 #include <libgda/thread-wrapper/gda-thread-wrapper.h>
 
 G_BEGIN_DECLS
@@ -35,6 +36,12 @@
  */
 GdaConnection *_gda_open_internal_sqlite_connection (const gchar *cnc_string);
 
+typedef struct {
+	guint jid;
+	GdaServerProviderExecCallback async_cb;
+	gpointer cb_data;
+} ThreadConnectionAsyncTask;
+void _ThreadConnectionAsyncTask_free (ThreadConnectionAsyncTask *atd);
 
 /*
  * Functions dedicated to implementing a GdaConnection which uses a GdaThreadWrapper around
@@ -45,6 +52,9 @@
         GdaServerProvider *cnc_provider;
         GdaThreadWrapper *wrapper;
 	GArray *handlers_ids; /* array of gulong */
+
+	/* current async. tasks */
+	GSList *async_tasks; /* list of ThreadConnectionAsyncTask pointers */
 } ThreadConnectionData; /* Per connection private data for */
 void           _gda_connection_force_transaction_status (GdaConnection *cnc, GdaConnection *wrapped_cnc);
 

Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c	(original)
+++ trunk/libgda/gda-connection.c	Tue Apr 14 18:49:11 2009
@@ -781,10 +781,11 @@
 
 	/* try to find provider */
 	if (dsn_info->provider != NULL) {
-		GdaServerProvider *prov;
+		GdaProviderInfo *pinfo;
+		GdaServerProvider *prov = NULL;
 
-		prov = gda_config_get_provider (dsn_info->provider, error);
-		if (prov) {
+		pinfo = gda_config_get_provider_info (dsn_info->provider);
+		if (pinfo) {
 			if (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) {
 				if (!_gda_thread_wrapper_provider)
 					_gda_thread_wrapper_provider =
@@ -792,6 +793,14 @@
 										   NULL));
 				prov = _gda_thread_wrapper_provider;
 			}
+			else
+				prov = gda_config_get_provider (dsn_info->provider, error);
+		}
+		else
+			g_set_error (error, GDA_CONFIG_ERROR, GDA_CONFIG_PROVIDER_NOT_FOUND_ERROR,
+				     _("No provider '%s' installed"), dsn_info->provider);
+		
+		if (prov) {
 			if (PROV_CLASS (prov)->create_connection) {
 				cnc = PROV_CLASS (prov)->create_connection (prov);
 				if (cnc) 
@@ -915,14 +924,14 @@
 
 	/* try to find provider */
 	if (provider_name || real_provider) {
-		GdaServerProvider *prov;
+		GdaProviderInfo *pinfo;
+		GdaServerProvider *prov = NULL;
 
-		prov = gda_config_get_provider (provider_name ? provider_name : real_provider, error);
-		if (prov) {
+		pinfo = gda_config_get_provider_info (provider_name ? provider_name : real_provider);
+		if (pinfo) {
 			if (options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) {
 				gchar *tmp;
-				tmp = g_strdup_printf ("%s;PROVIDER_NAME=%s",
-						       real_cnc, gda_server_provider_get_name (prov));
+				tmp = g_strdup_printf ("%s;PROVIDER_NAME=%s", real_cnc, pinfo->id);
 				g_free (real_cnc);
 				real_cnc = tmp;
 				if (!_gda_thread_wrapper_provider)
@@ -931,7 +940,14 @@
 										   NULL));
 				prov = _gda_thread_wrapper_provider;
 			}
+			else
+				prov = gda_config_get_provider (provider_name ? provider_name : real_provider, error);
+		}
+		else
+			g_set_error (error, GDA_CONFIG_ERROR, GDA_CONFIG_PROVIDER_NOT_FOUND_ERROR,
+				     _("No provider '%s' installed"), provider_name ? provider_name : real_provider);
 
+		if (prov) {
 			if (PROV_CLASS (prov)->create_connection) {
 				cnc = PROV_CLASS (prov)->create_connection (prov);
 				if (cnc) 
@@ -1386,7 +1402,7 @@
 	g_return_if_fail (cnc->priv);
 	g_return_if_fail (GDA_IS_CONNECTION_EVENT (event));
 
-	gda_connection_lock ((GdaLockable*) cnc);
+	gda_mutex_lock (cnc->priv->mutex);
 	if (debug == -1) {
 		const gchar *str;
 		debug = 0;
@@ -1437,7 +1453,7 @@
 
 	if (gda_connection_event_get_event_type (event) == GDA_CONNECTION_EVENT_ERROR)
 		g_signal_emit (G_OBJECT (cnc), gda_connection_signals[ERROR], 0, event);
-	gda_connection_unlock ((GdaLockable*) cnc);
+	gda_mutex_unlock (cnc->priv->mutex);
 }
 
 /**
@@ -1764,7 +1780,7 @@
 
 	gda_connection_lock (GDA_LOCKABLE (cnc));
 
-	i = get_task_index (cnc, task_id, &is_completed, FALSE);
+	i = get_task_index (cnc, task_id, &is_completed, TRUE);
 	if (i >= 0) {
 		CncTask *task;
 		g_assert (!is_completed);
@@ -1799,12 +1815,12 @@
 		cnc_task_unlock (task);
 
 		/* execute next waiting task if there is one */
-		while (cnc->priv->waiting_tasks->len >= 1) {
+		if (cnc->priv->waiting_tasks->len >= 1) {
 			/* execute statement now as there are no other ones to be executed */
 			GError *lerror = NULL;
 			task = CNC_TASK (g_array_index (cnc->priv->waiting_tasks, gpointer, 0));
-			task->being_processed = TRUE;
 			cnc_task_lock (task);
+			task->being_processed = TRUE;
 			PROV_CLASS (cnc->priv->provider_obj)->statement_execute (cnc->priv->provider_obj, cnc, 
 										 task->stmt, 
 										 task->params, 
@@ -1824,9 +1840,11 @@
 			cnc_task_unlock (task);
 		}
 	}
-	g_print ("%s() called!!!\n", __FUNCTION__);
+	else
+		g_warning ("Provider called back for the execution of task %u (provider numbering) which does not exist, "
+			   "ignored.\n", task_id);
 
-	gda_connection_lock (GDA_LOCKABLE (cnc));
+	gda_connection_unlock (GDA_LOCKABLE (cnc));
 }
 
 /**
@@ -1839,14 +1857,14 @@
  * @need_last_insert_row: TRUE if the values of the last interted row must be computed
  * @error: a place to store errors, or %NULL
  *
- * This method is somilar to gda_connection_statement_execute() but is done asynchronously as this method returns
- * immediately a task ID. It's up to the caller to use gda_connection_async_fetch_result() regularly to check
+ * This method is similar to gda_connection_statement_execute() but is asynchronous as it method returns
+ * immediately with a task ID. It's up to the caller to use gda_connection_async_fetch_result() regularly to check
  * if the statement's execution is finished.
  *
  * It is possible to call the method several times to request several statements to be executed asynchronously, the
  * statements will be executed in the order in which they were requested.
  *
- * The parameters, if present, are copied and can be discaded or modified before the statement is actually executed.
+ * The parameters, if present, are copied and can be discarded or modified before the statement is actually executed.
  * The @stmt object is not copied but simply referenced (for performance reasons), and if it is modified before
  * it is actually executed, then its execution will not occur. It is however safe to call g_object_unref() on it if
  * it's not needed anymore.
@@ -1886,11 +1904,13 @@
 	if (cnc->priv->waiting_tasks->len == 1) {
 		/* execute statement now as there are no other ones to be executed */
 		GError *lerror = NULL;
-		task->being_processed = TRUE;
 		cnc_task_lock (task);
+		task->being_processed = TRUE;
 		PROV_CLASS (cnc->priv->provider_obj)->statement_execute (cnc->priv->provider_obj, cnc, 
-									 task->stmt, task->params, 
-									 task->model_usage, task->col_types,
+									 task->stmt,
+									 task->params, 
+									 task->model_usage,
+									 task->col_types,
 									 &(task->last_insert_row),
 									 &(task->prov_task_id),
 									 (GdaServerProviderExecCallback) async_stmt_exec_cb, 
@@ -3580,6 +3600,7 @@
 		ThreadConnectionData *cdata;
 		UpdateMetaStoreData data;
 		gpointer res;
+		guint jid;
 
 		cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
 		if (!cdata) 
@@ -3588,9 +3609,9 @@
 		data.cnc = cdata->sub_connection;
 		data.context = context;
 
-		gda_thread_wrapper_execute (cdata->wrapper, 
-					    (GdaThreadWrapperFunc) sub_thread_update_meta_store, &data, NULL, error);
-		res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+		jid = gda_thread_wrapper_execute (cdata->wrapper, 
+						  (GdaThreadWrapperFunc) sub_thread_update_meta_store, &data, NULL, error);
+		res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, error);
 		return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 	}
 
@@ -4544,12 +4565,12 @@
 static void
 statement_weak_notify_cb (GdaConnection *cnc, GdaStatement *stmt)
 {
-	gda_connection_lock ((GdaLockable*) cnc);
+	gda_mutex_lock (cnc->priv->mutex);
 
 	g_assert (cnc->priv->prepared_stmts);
 	g_hash_table_remove (cnc->priv->prepared_stmts, stmt);
 
-	gda_connection_unlock ((GdaLockable*) cnc);
+	gda_mutex_unlock (cnc->priv->mutex);
 }
 
 

Modified: trunk/libgda/thread-wrapper/Makefile.am
==============================================================================
--- trunk/libgda/thread-wrapper/Makefile.am	(original)
+++ trunk/libgda/thread-wrapper/Makefile.am	Tue Apr 14 18:49:11 2009
@@ -13,6 +13,8 @@
 
 libgda_threadwrapper_4_0_la_SOURCES = \
 	$(libgda_threadwrapper_headers) \
+	gda-thread-blob-op.h \
+	gda-thread-blob-op.c \
 	gda-thread-provider.h \
 	gda-thread-provider.c \
 	gda-thread-recordset.h \

Added: trunk/libgda/thread-wrapper/gda-thread-blob-op.c
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-blob-op.c	Tue Apr 14 18:49:11 2009
@@ -0,0 +1,245 @@
+/* GDA
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "gda-thread-blob-op.h"
+
+struct _GdaThreadBlobOpPrivate {
+	GdaThreadWrapper *wrapper;
+	GdaBlobOp        *wrapped_op;
+};
+
+static void gda_thread_blob_op_class_init (GdaThreadBlobOpClass *klass);
+static void gda_thread_blob_op_init       (GdaThreadBlobOp *blob,
+					   GdaThreadBlobOpClass *klass);
+static void gda_thread_blob_op_dispose   (GObject *object);
+
+static glong gda_thread_blob_op_get_length (GdaBlobOp *op);
+static glong gda_thread_blob_op_read       (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size);
+static glong gda_thread_blob_op_write      (GdaBlobOp *op, GdaBlob *blob, glong offset);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * Object init and dispose
+ */
+GType
+_gda_thread_blob_op_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (GdaThreadBlobOpClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gda_thread_blob_op_class_init,
+			NULL,
+			NULL,
+			sizeof (GdaThreadBlobOp),
+			0,
+			(GInstanceInitFunc) gda_thread_blob_op_init
+		};
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaThreadBlobOp", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+gda_thread_blob_op_init (GdaThreadBlobOp *op,
+			   GdaThreadBlobOpClass *klass)
+{
+	g_return_if_fail (GDA_IS_THREAD_BLOB_OP (op));
+
+	op->priv = g_new0 (GdaThreadBlobOpPrivate, 1);
+	op->priv->wrapper = NULL;
+	op->priv->wrapped_op = NULL;
+}
+
+static void
+gda_thread_blob_op_class_init (GdaThreadBlobOpClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GdaBlobOpClass *blob_class = GDA_BLOB_OP_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = gda_thread_blob_op_dispose;
+	blob_class->get_length = gda_thread_blob_op_get_length;
+	blob_class->read = gda_thread_blob_op_read;
+	blob_class->write = gda_thread_blob_op_write;
+}
+
+static void
+gda_thread_blob_op_dispose (GObject * object)
+{
+	GdaThreadBlobOp *thop = (GdaThreadBlobOp *) object;
+
+	g_return_if_fail (GDA_IS_THREAD_BLOB_OP (thop));
+
+	if (thop->priv) {
+		g_object_unref (thop->priv->wrapped_op);
+		g_object_unref (thop->priv->wrapper);
+		g_free (thop->priv);
+		thop->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+GdaBlobOp *
+_gda_thread_blob_op_new (GdaThreadWrapper *wrapper, GdaBlobOp *op)
+{
+	GdaThreadBlobOp *thop;
+
+	g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), NULL);
+	g_return_val_if_fail (GDA_IS_BLOB_OP (op), NULL);
+
+	thop = g_object_new (GDA_TYPE_THREAD_BLOB_OP, NULL);
+	thop->priv->wrapper = g_object_ref (wrapper);
+	thop->priv->wrapped_op = g_object_ref (op);
+	
+	return GDA_BLOB_OP (thop);
+}
+
+
+/*
+ * Virtual functions
+ */
+static glong *
+sub_thread_blob_op_get_length (GdaBlobOp *wrapped_op, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	glong *retptr;
+
+	retptr = g_new (glong, 1);
+	*retptr = gda_blob_op_get_length (wrapped_op);
+#ifdef GDA_DEBUG_NO
+	g_print ("/%s() => %ld\n", __FUNCTION__, *retptr);
+#endif
+
+	return retptr;
+}
+
+static glong
+gda_thread_blob_op_get_length (GdaBlobOp *op)
+{
+	GdaThreadBlobOp *thop;
+	glong *ptr, retval;
+	guint jid;
+	
+	thop = (GdaThreadBlobOp *) op;
+	jid = gda_thread_wrapper_execute (thop->priv->wrapper,
+					  (GdaThreadWrapperFunc) sub_thread_blob_op_get_length,
+					  thop->priv->wrapped_op, NULL, NULL);
+	ptr = (glong*) gda_thread_wrapper_fetch_result (thop->priv->wrapper, TRUE, jid, NULL);
+	retval = *ptr;
+	g_free (ptr);
+	return retval;
+}
+
+typedef struct {
+	GdaBlobOp *op;
+	GdaBlob *blob;
+	glong offset;
+	glong size;
+} ThreadData;
+
+static glong *
+sub_thread_blob_op_read (ThreadData *td, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	glong *retptr;
+
+	retptr = g_new (glong, 1);
+	*retptr = gda_blob_op_read (td->op, td->blob, td->offset, td->size);
+#ifdef GDA_DEBUG_NO
+	g_print ("/%s() => %ld\n", __FUNCTION__, *retptr);
+#endif
+
+	return retptr;
+}
+
+static glong
+gda_thread_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
+{
+	GdaThreadBlobOp *thop;
+	ThreadData td;
+	glong *ptr, retval;
+	guint jid;
+
+	thop = (GdaThreadBlobOp *) op;
+	td.op = thop->priv->wrapped_op;
+	td.blob = blob;
+	td.offset = offset;
+	td.size = size;
+
+	jid = gda_thread_wrapper_execute (thop->priv->wrapper,
+					  (GdaThreadWrapperFunc) sub_thread_blob_op_read,
+					  &td, NULL, NULL);
+	ptr = (glong*) gda_thread_wrapper_fetch_result (thop->priv->wrapper, TRUE, jid, NULL);
+	retval = *ptr;
+	g_free (ptr);
+	return retval;
+}
+
+static glong *
+sub_thread_blob_op_write (ThreadData *td, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	glong *retptr;
+
+	retptr = g_new (glong, 1);
+	*retptr = gda_blob_op_write (td->op, td->blob, td->offset);
+#ifdef GDA_DEBUG_NO
+	g_print ("/%s() => %ld\n", __FUNCTION__, *retptr);
+#endif
+
+	return retptr;
+}
+
+static glong
+gda_thread_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
+{
+	GdaThreadBlobOp *thop;
+	ThreadData td;
+	glong *ptr, retval;
+	guint jid;
+
+	thop = (GdaThreadBlobOp *) op;
+	td.op = thop->priv->wrapped_op;
+	td.blob = blob;
+	td.offset = offset;
+
+	jid = gda_thread_wrapper_execute (thop->priv->wrapper,
+					  (GdaThreadWrapperFunc) sub_thread_blob_op_write,
+					  &td, NULL, NULL);
+	ptr = (glong*) gda_thread_wrapper_fetch_result (thop->priv->wrapper, TRUE, jid, NULL);
+	retval = *ptr;
+	g_free (ptr);
+	return retval;
+}

Added: trunk/libgda/thread-wrapper/gda-thread-blob-op.h
==============================================================================
--- (empty file)
+++ trunk/libgda/thread-wrapper/gda-thread-blob-op.h	Tue Apr 14 18:49:11 2009
@@ -0,0 +1,56 @@
+/* GDA Library
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *         Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_THREAD_BLOB_OP_H__
+#define __GDA_THREAD_BLOB_OP_H__
+
+#include <libgda/gda-value.h>
+#include <libgda/gda-blob-op.h>
+#include "gda-thread-wrapper.h"
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_THREAD_BLOB_OP            (_gda_thread_blob_op_get_type())
+#define GDA_THREAD_BLOB_OP(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_THREAD_BLOB_OP, GdaThreadBlobOp))
+#define GDA_THREAD_BLOB_OP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_THREAD_BLOB_OP, GdaThreadBlobOpClass))
+#define GDA_IS_THREAD_BLOB_OP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_THREAD_BLOB_OP))
+#define GDA_IS_THREAD_BLOB_OP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_THREAD_BLOB_OP))
+
+typedef struct _GdaThreadBlobOp        GdaThreadBlobOp;
+typedef struct _GdaThreadBlobOpClass   GdaThreadBlobOpClass;
+typedef struct _GdaThreadBlobOpPrivate GdaThreadBlobOpPrivate;
+
+struct _GdaThreadBlobOp {
+	GdaBlobOp                   parent;
+	GdaThreadBlobOpPrivate     *priv;
+};
+
+struct _GdaThreadBlobOpClass {
+	GdaBlobOpClass              parent_class;
+};
+
+GType      _gda_thread_blob_op_get_type  (void) G_GNUC_CONST;
+GdaBlobOp *_gda_thread_blob_op_new       (GdaThreadWrapper *wrapper, GdaBlobOp *op);
+
+G_END_DECLS
+
+#endif

Modified: trunk/libgda/thread-wrapper/gda-thread-provider.c
==============================================================================
--- trunk/libgda/thread-wrapper/gda-thread-provider.c	(original)
+++ trunk/libgda/thread-wrapper/gda-thread-provider.c	Tue Apr 14 18:49:11 2009
@@ -1,5 +1,5 @@
 /* GDA Thread provider
- * Copyright (C) 2008 The GNOME Foundation.
+ * Copyright (C) 2009 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -116,6 +116,7 @@
 								 GType *col_types, GdaSet **last_inserted_row, 
 								 guint *task_id, GdaServerProviderExecCallback async_cb, 
 								 gpointer cb_data, GError **error);
+static gboolean             gda_thread_provider_handle_async (GdaServerProvider *provider, GdaConnection *cnc, GError **error);
 
 /* distributed transactions */
 static gboolean gda_thread_provider_xa_start    (GdaServerProvider *provider, GdaConnection *cnc, 
@@ -174,6 +175,7 @@
 	provider_class->statement_to_sql = gda_thread_provider_statement_to_sql;
 	provider_class->statement_prepare = gda_thread_provider_statement_prepare;
 	provider_class->statement_execute = gda_thread_provider_statement_execute;
+	provider_class->handle_async = gda_thread_provider_handle_async;
 
 	provider_class->is_busy = NULL;
 	provider_class->cancel = NULL;
@@ -197,7 +199,7 @@
 }
 
 GType
-gda_thread_provider_get_type (void)
+_gda_thread_provider_get_type (void)
 {
 	static GType type = 0;
 
@@ -282,7 +284,9 @@
 						       data->auth_string, data->options, error);
 	if (cnc)
 		data->out_cnc_provider = gda_connection_get_provider (cnc);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %p\n", __FUNCTION__, cnc);
+#endif
 	return cnc;
 }
 
@@ -326,18 +330,20 @@
 	GdaThreadWrapper *wr;
 	GdaConnection *sub_cnc;
 	GError *error = NULL;
+	guint jid;
 	g_assert (data);
 	data->auth_string = auth_string;
 	data->options = options & (~GDA_CONNECTION_OPTIONS_THREAD_SAFE);
 	wr = gda_thread_wrapper_new ();
-	gda_thread_wrapper_execute (wr, (GdaThreadWrapperFunc) sub_thread_open_connection, data, NULL, NULL);
-	sub_cnc = gda_thread_wrapper_fetch_result (wr, TRUE, NULL, &error);
+	jid = gda_thread_wrapper_execute (wr, (GdaThreadWrapperFunc) sub_thread_open_connection, data, NULL, NULL);
+	sub_cnc = gda_thread_wrapper_fetch_result (wr, TRUE, jid, &error);
 	g_free (dsn);
 	g_free (cnc_string);
 	g_free (auth_string);
 	if (!sub_cnc) {
-		TO_IMPLEMENT; /* create a GdaConnectionEvent from @error using 
-				 gda_connection_add_event_string () */
+		gda_connection_add_event_string (cnc, "%s", error && error->message ? error->message : _("No detail"));
+		if (error)
+			g_error_free (error);
 		g_object_unref (wr);
 		g_free (data);
 		return FALSE;
@@ -408,7 +414,9 @@
 	/* WARNING: function executed in sub thread! */
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->close_connection (data->prov, data->cnc);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -418,6 +426,7 @@
 	ThreadConnectionData *cdata;
 	CncProvData wdata;
 	gpointer res;
+	guint jid;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -429,9 +438,9 @@
 	wdata.prov = cdata->cnc_provider;
 	wdata.cnc = cdata->sub_connection;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_close_connection, &wdata, NULL, NULL);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_close_connection, &wdata, NULL, NULL);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -447,7 +456,9 @@
 	/* WARNING: function executed in sub thread! */
 	const gchar *retval;
 	retval = PROV_CLASS (data->prov)->get_server_version (data->prov, data->cnc);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval);
+#endif
 	return retval;
 }
 static const gchar *
@@ -455,6 +466,7 @@
 {
 	ThreadConnectionData *cdata;
 	CncProvData wdata;
+	guint jid;
 
 	if (!cnc) 
 		return NULL;
@@ -469,9 +481,9 @@
 	wdata.prov = cdata->cnc_provider;
 	wdata.cnc = cdata->sub_connection;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_get_server_version, &wdata, NULL, NULL);
-	return (const gchar*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_get_server_version, &wdata, NULL, NULL);
+	return (const gchar*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 }
 
 /*
@@ -485,7 +497,9 @@
 	/* WARNING: function executed in sub thread! */
 	const gchar *retval;
 	retval = PROV_CLASS (data->prov)->get_database (data->prov, data->cnc);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval);
+#endif
 	return retval;
 }
 
@@ -494,6 +508,7 @@
 {
 	ThreadConnectionData *cdata;
 	CncProvData wdata;
+	guint jid;
 
 	if (!cnc) 
 		return NULL;
@@ -508,9 +523,9 @@
 	wdata.prov = cdata->cnc_provider;
 	wdata.cnc = cdata->sub_connection;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_get_database, &wdata, NULL, NULL);
-	return (const gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_get_database, &wdata, NULL, NULL);
+	return (const gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 }
 
 /*
@@ -537,7 +552,9 @@
 	/* WARNING: function executed in sub thread! */
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->supports_operation (data->prov, data->cnc, data->type, data->options);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -548,6 +565,7 @@
 	ThreadConnectionData *cdata;
 	SupportsOperationData wdata;
 	gpointer res;
+	guint jid;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -559,9 +577,9 @@
 	wdata.prov = cdata->cnc_provider;
 	wdata.cnc = cdata->sub_connection;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_supports_operation, &wdata, NULL, NULL);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_supports_operation, &wdata, NULL, NULL);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -588,7 +606,9 @@
 							data->type, 
 							data->options,
 							error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %p\n", __FUNCTION__, op);
+#endif
 	return op;
 }
 
@@ -598,6 +618,7 @@
 {
 	ThreadConnectionData *cdata;
 	CreateOperationData wdata;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -615,9 +636,9 @@
 	wdata.type = type;
 	wdata.options = options;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_create_operation, &wdata, NULL, NULL);
-	return (GdaServerOperation*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_create_operation, &wdata, NULL, NULL);
+	return (GdaServerOperation*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, error);
 }
 
 /*
@@ -638,7 +659,9 @@
 							 data->cnc,
 							 data->op, 
 							 error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, str);
+#endif
 	return str;
 }
 
@@ -648,6 +671,7 @@
 {
 	ThreadConnectionData *cdata;
 	RenderOperationData wdata;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -664,9 +688,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.op = op;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_render_operation, &wdata, NULL, NULL);
-	return (gchar*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_render_operation, &wdata, NULL, NULL);
+	return (gchar*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, error);
 }
 
 /*
@@ -685,7 +709,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->perform_operation (data->prov, data->cnc, data->op, 
 							     NULL, NULL, NULL, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -697,6 +723,7 @@
 	ThreadConnectionData *cdata;
 	PerformOperationData wdata;
 	gpointer res;
+	guint jid;
 
 	/* If asynchronous connection opening is not supported, then exit now */
 	if (async_cb) {
@@ -721,9 +748,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.op = op;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_perform_operation, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_perform_operation, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -744,7 +771,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->begin_transaction (data->prov, data->cnc, data->name, 
 							     data->level, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -756,6 +785,7 @@
 	ThreadConnectionData *cdata;
 	BeginTransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -774,9 +804,9 @@
 	wdata.name = name;
 	wdata.level = level;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_begin_transaction, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_begin_transaction, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, error);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -797,7 +827,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->commit_transaction (data->prov, data->cnc, data->name, 
 							      error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -808,6 +840,7 @@
 	ThreadConnectionData *cdata;
 	TransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -825,9 +858,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.name = name;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_commit_transaction, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_commit_transaction, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -841,7 +874,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->rollback_transaction (data->prov, data->cnc, data->name, 
 								error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -852,6 +887,7 @@
 	ThreadConnectionData *cdata;
 	TransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -869,9 +905,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.name = name;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_rollback_transaction, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_rollback_transaction, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -885,7 +921,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->add_savepoint (data->prov, data->cnc, data->name, 
 							 error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -896,6 +934,7 @@
 	ThreadConnectionData *cdata;
 	TransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -913,9 +952,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.name = name;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_add_savepoint, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_add_savepoint, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -929,7 +968,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->rollback_savepoint (data->prov, data->cnc, data->name, 
 							 error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -940,6 +981,7 @@
 	ThreadConnectionData *cdata;
 	TransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -957,9 +999,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.name = name;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_rollback_savepoint, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_rollback_savepoint, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -973,7 +1015,9 @@
 	gboolean retval;
 	retval = PROV_CLASS (data->prov)->delete_savepoint (data->prov, data->cnc, data->name, 
 							 error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -984,6 +1028,7 @@
 	ThreadConnectionData *cdata;
 	TransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1001,9 +1046,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.name = name;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_delete_savepoint, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_delete_savepoint, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1054,7 +1099,9 @@
 	/* WARNING: function executed in sub thread! */
 	GdaDataHandler *retval;
 	retval = PROV_CLASS (data->prov)->get_data_handler (data->prov, data->cnc, data->type, data->dbms_type);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %p\n", __FUNCTION__, retval);
+#endif
 	return retval;
 }
 
@@ -1065,6 +1112,7 @@
 	ThreadConnectionData *cdata;
 	GetDataHandlerData wdata;
 	GdaDataHandler *res;
+	guint jid;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -1078,9 +1126,9 @@
 	wdata.type = type;
 	wdata.dbms_type = dbms_type;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_get_data_handler, &wdata, NULL, NULL);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_get_data_handler, &wdata, NULL, NULL);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return res;
 }
 
@@ -1101,7 +1149,9 @@
 	/* WARNING: function executed in sub thread! */
 	const gchar *retval;
 	retval = PROV_CLASS (data->prov)->get_def_dbms_type (data->prov, data->cnc, data->type);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval);
+#endif
 	return retval;
 }
 
@@ -1111,6 +1161,7 @@
 	ThreadConnectionData *cdata;
 	GetDefaultDbmsTypeData wdata;
 	const gchar *res;
+	guint jid;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -1123,9 +1174,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.type = type;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_get_default_dbms_type, &wdata, NULL, NULL);
-	res = (const gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_get_default_dbms_type, &wdata, NULL, NULL);
+	res = (const gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return res;
 }
 
@@ -1142,7 +1193,9 @@
 	/* WARNING: function executed in sub thread! */
 	GdaSqlParser *parser;
 	parser = PROV_CLASS (data->prov)->create_parser (data->prov, data->cnc);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %p\n", __FUNCTION__, parser);
+#endif
 	return parser;
 }
 
@@ -1151,6 +1204,7 @@
 {
 	ThreadConnectionData *cdata;
 	CncProvData wdata;
+	guint jid;
 
 	if (!cnc) 
 		return NULL;
@@ -1165,9 +1219,9 @@
 	wdata.prov = cdata->cnc_provider;
 	wdata.cnc = cdata->sub_connection;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_create_parser, &wdata, NULL, NULL);
-	return (GdaSqlParser *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_create_parser, &wdata, NULL, NULL);
+	return (GdaSqlParser *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 }
 
 /*
@@ -1195,7 +1249,9 @@
 	const gchar *retval;
 	retval = PROV_CLASS (data->prov)->statement_to_sql (data->prov, data->cnc, data->stmt,
 							    data->params, data->flags, data->params_used, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval);
+#endif
 	return retval;
 }
 
@@ -1206,6 +1262,7 @@
 {
 	ThreadConnectionData *cdata;
 	StmtToSqlData wdata;
+	guint jid;
 
 	if (!cnc) 
 		return NULL;
@@ -1224,9 +1281,9 @@
 	wdata.flags = flags;
 	wdata.params_used = params_used;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_statement_to_sql, &wdata, NULL, NULL);
-	return (gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_statement_to_sql, &wdata, NULL, NULL);
+	return (gchar *) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 }
 
 /*
@@ -1251,7 +1308,9 @@
 							     data->cnc,
 							     data->stmt, 
 							     error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1262,6 +1321,7 @@
 	ThreadConnectionData *cdata;
 	PrepareStatementData wdata;
 	gpointer res;
+	guint jid;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -1275,9 +1335,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.stmt = stmt;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_prepare_statement, &wdata, NULL, NULL);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_prepare_statement, &wdata, NULL, NULL);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, error);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1322,7 +1382,9 @@
 							     data->last_inserted_row,
 							     NULL, NULL, NULL,
 							     error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %p\n", __FUNCTION__, retval);
+#endif
 
 	if (GDA_IS_DATA_MODEL (retval)) {
 		/* substitute the GdaDataSelect with a GdaThreadRecordset */
@@ -1334,6 +1396,7 @@
 
 	return retval;
 }
+
 static GObject *
 gda_thread_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
 				       GdaStatement *stmt, GdaSet *params,
@@ -1343,40 +1406,103 @@
 				       GdaServerProviderExecCallback async_cb, gpointer cb_data, GError **error)
 {
 	ThreadConnectionData *cdata;
-	ExecuteStatementData wdata;
 
-	/* FIXME: handle async requests */
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
+
+	cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
 	if (async_cb) {
-		TO_IMPLEMENT;
+		ExecuteStatementData *wdata;
+		wdata = g_new0 (ExecuteStatementData, 1);
+		wdata->prov = cdata->cnc_provider;
+		wdata->cnc = cdata->sub_connection;
+		wdata->stmt = stmt;
+		wdata->params = params;
+		wdata->model_usage = model_usage;
+		wdata->col_types = col_types;
+		wdata->last_inserted_row = last_inserted_row;
+		
+		wdata->real_cnc = cnc;
+		wdata->wrapper = cdata->wrapper;
+		
+		ThreadConnectionAsyncTask *atd;
+		atd = g_new0 (ThreadConnectionAsyncTask, 1);
+		atd->async_cb = async_cb;
+		atd->cb_data = cb_data;
+		atd->jid = gda_thread_wrapper_execute (cdata->wrapper, 
+						       (GdaThreadWrapperFunc) sub_thread_execute_statement, wdata, 
+						       (GDestroyNotify) g_free, error);
+		cdata->async_tasks = g_slist_append (cdata->async_tasks, atd);
+
+		*task_id = atd->jid;
+
 		return NULL;
 	}
+	else {
+		GObject *res;
+		ExecuteStatementData wdata;
+		guint jid;
+
+		wdata.prov = cdata->cnc_provider;
+		wdata.cnc = cdata->sub_connection;
+		wdata.stmt = stmt;
+		wdata.params = params;
+		wdata.model_usage = model_usage;
+		wdata.col_types = col_types;
+		wdata.last_inserted_row = last_inserted_row;
+		
+		wdata.real_cnc = cnc;
+		wdata.wrapper = cdata->wrapper;
+		
+		jid = gda_thread_wrapper_execute (cdata->wrapper, 
+						  (GdaThreadWrapperFunc) sub_thread_execute_statement, &wdata, NULL, NULL);
+		res = (GObject*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, error);
+
+		return res;
+	}
+}
 
+static gboolean
+gda_thread_provider_handle_async (GdaServerProvider *provider, GdaConnection *cnc, GError **error)
+{
+	ThreadConnectionData *cdata;
 	GObject *res;
+	GError *lerror = NULL;
+	ThreadConnectionAsyncTask *atd;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
-	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
 
 	cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata) 
 		return FALSE;
-	
-	wdata.prov = cdata->cnc_provider;
-	wdata.cnc = cdata->sub_connection;
-	wdata.stmt = stmt;
-	wdata.params = params;
-	wdata.model_usage = model_usage;
-	wdata.col_types = col_types;
-	wdata.last_inserted_row = last_inserted_row;
-
-	wdata.real_cnc = cnc;
-	wdata.wrapper = cdata->wrapper;
-
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_execute_statement, &wdata, NULL, NULL);
-	res = (GObject*) gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, error);
 
-	return res;
+	if (!cdata->async_tasks)
+		return TRUE;
+
+	atd = (ThreadConnectionAsyncTask*) cdata->async_tasks->data;
+	res = (GObject*) gda_thread_wrapper_fetch_result (cdata->wrapper, FALSE, atd->jid, &lerror);
+	if (res) {
+		atd->async_cb (provider, cnc, atd->jid, res, lerror, atd->cb_data);
+		if (lerror)
+			g_error_free (lerror);
+		g_object_unref (res);
+
+		_ThreadConnectionAsyncTask_free (atd);
+		cdata->async_tasks = g_slist_delete_link (cdata->async_tasks, cdata->async_tasks);
+	}
+
+	return TRUE;
+}
+
+void
+_ThreadConnectionAsyncTask_free (ThreadConnectionAsyncTask *atd)
+{
+	g_free (atd);
 }
 
 /*
@@ -1395,7 +1521,9 @@
 	gboolean retval = FALSE;
 	retval = PROV_CLASS (data->prov)->xa_funcs->xa_start (data->prov, data->cnc, data->xid, 
 							      error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1406,6 +1534,7 @@
 	ThreadConnectionData *cdata;
 	XaTransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1424,9 +1553,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.xid = xid;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_xa_start, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_xa_start, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1441,7 +1570,9 @@
 	gboolean retval = FALSE;
 	retval = PROV_CLASS (data->prov)->xa_funcs->xa_end (data->prov, data->cnc, data->xid, 
 							    error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1452,6 +1583,7 @@
 	ThreadConnectionData *cdata;
 	XaTransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1470,9 +1602,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.xid = xid;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_xa_end, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_xa_end, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1486,7 +1618,9 @@
 	gboolean retval = FALSE;
 	retval = PROV_CLASS (data->prov)->xa_funcs->xa_prepare (data->prov, data->cnc, data->xid, 
 								error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1497,6 +1631,7 @@
 	ThreadConnectionData *cdata;
 	XaTransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1515,9 +1650,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.xid = xid;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_xa_prepare, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_xa_prepare, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1532,7 +1667,9 @@
 	gboolean retval = FALSE;
 	retval = PROV_CLASS (data->prov)->xa_funcs->xa_commit (data->prov, data->cnc, data->xid, 
 								error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1543,6 +1680,7 @@
 	ThreadConnectionData *cdata;
 	XaTransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1561,9 +1699,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.xid = xid;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_xa_commit, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_xa_commit, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1577,7 +1715,9 @@
 	gboolean retval = FALSE;
 	retval = PROV_CLASS (data->prov)->xa_funcs->xa_rollback (data->prov, data->cnc, data->xid, 
 								error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1588,6 +1728,7 @@
 	ThreadConnectionData *cdata;
 	XaTransactionData wdata;
 	gpointer res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1606,9 +1747,9 @@
 	wdata.cnc = cdata->sub_connection;
 	wdata.xid = xid;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_xa_rollback, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_xa_rollback, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return GPOINTER_TO_INT (res) ? TRUE : FALSE;
 }
 
@@ -1623,7 +1764,9 @@
 	/* WARNING: function executed in sub thread! */
 	GList *retval;
 	retval = PROV_CLASS (data->prov)->xa_funcs->xa_recover (data->prov, data->cnc, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %p\n", __FUNCTION__, retval);
+#endif
 	return GINT_TO_POINTER (retval ? 1 : 0);
 }
 
@@ -1634,6 +1777,7 @@
 	ThreadConnectionData *cdata;
 	CncProvData wdata;
 	GList *res;
+	guint jid;
 
 	if (!cnc) {
 		g_set_error (error, 0, 0, _("A connection is required"));
@@ -1650,9 +1794,9 @@
 	wdata.prov = cdata->cnc_provider;
 	wdata.cnc = cdata->sub_connection;
 
-	gda_thread_wrapper_execute (cdata->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_xa_recover, &wdata, NULL, error);
-	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, NULL, NULL);
+	jid = gda_thread_wrapper_execute (cdata->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_xa_recover, &wdata, NULL, error);
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, NULL);
 	return res;
 }
 
@@ -1665,7 +1809,7 @@
 	if (!cdata)
 		return;
 
-	/*disconnect signals handlers */
+	/* disconnect signals handlers */
 	gint i;
 	for (i = 0; i < cdata->handlers_ids->len; i++) {
 		gulong hid = g_array_index (cdata->handlers_ids, gulong, i);
@@ -1677,5 +1821,11 @@
 					 (GdaThreadWrapperVoidFunc) g_object_unref,
 					 cdata->sub_connection, NULL, NULL);
 	g_object_unref (cdata->wrapper);
+
+	/* free async data */
+	if (cdata->async_tasks) {
+		g_slist_foreach (cdata->async_tasks, (GFunc) _ThreadConnectionAsyncTask_free, NULL);
+		g_slist_free (cdata->async_tasks);
+	}
 	g_free (cdata);
 }

Modified: trunk/libgda/thread-wrapper/gda-thread-provider.h
==============================================================================
--- trunk/libgda/thread-wrapper/gda-thread-provider.h	(original)
+++ trunk/libgda/thread-wrapper/gda-thread-provider.h	Tue Apr 14 18:49:11 2009
@@ -1,9 +1,7 @@
 /* GDA Thread provider
- * Copyright (C) 1998 - 2009 The GNOME Foundation.
+ * Copyright (C) 2009 The GNOME Foundation.
  *
  * AUTHORS:
- *	Rodrigo Moya <rodrigo gnome-db org>
- *	Carlos Perellórí<carlos gnome-db org>
  *      Vivien Malerba <malerba gnome-db org>
  *
  * This Library is free software; you can redistribute it and/or
@@ -27,7 +25,7 @@
 
 #include <libgda/gda-server-provider.h>
 
-#define GDA_TYPE_THREAD_PROVIDER            (gda_thread_provider_get_type())
+#define GDA_TYPE_THREAD_PROVIDER            (_gda_thread_provider_get_type())
 #define GDA_THREAD_PROVIDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_THREAD_PROVIDER, GdaThreadProvider))
 #define GDA_THREAD_PROVIDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_THREAD_PROVIDER, GdaThreadProviderClass))
 #define GDA_IS_THREAD_PROVIDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_THREAD_PROVIDER))
@@ -50,7 +48,7 @@
 
 G_BEGIN_DECLS
 
-GType              gda_thread_provider_get_type (void) G_GNUC_CONST;
+GType              _gda_thread_provider_get_type (void) G_GNUC_CONST;
 
 G_END_DECLS
 

Modified: trunk/libgda/thread-wrapper/gda-thread-recordset.c
==============================================================================
--- trunk/libgda/thread-wrapper/gda-thread-recordset.c	(original)
+++ trunk/libgda/thread-wrapper/gda-thread-recordset.c	Tue Apr 14 18:49:11 2009
@@ -27,7 +27,7 @@
 #include "gda-thread-recordset.h"
 #include "gda-thread-provider.h"
 #include <libgda/providers-support/gda-data-select-priv.h>
-//#include "gda-thread-blob-op.h"
+#include "gda-thread-blob-op.h"
 
 static void gda_thread_recordset_class_init (GdaThreadRecordsetClass *klass);
 static void gda_thread_recordset_init       (GdaThreadRecordset *recset,
@@ -45,6 +45,8 @@
 struct _GdaThreadRecordsetPrivate {
 	GdaDataModel *sub_model;
 	GdaThreadWrapper *wrapper;
+	gint nblobs;
+	gint *blobs_conv;
 };
 static GObjectClass *parent_class = NULL;
 
@@ -65,6 +67,7 @@
 	recset->priv = g_new0 (GdaThreadRecordsetPrivate, 1);
 	recset->priv->sub_model = NULL;
 	recset->priv->wrapper = NULL;
+	recset->priv->blobs_conv = NULL;
 }
 
 static void
@@ -96,11 +99,16 @@
 		if (recset->priv->sub_model) {
 			/* unref recset->priv->sub_model in sub thread */
 			gda_thread_wrapper_execute_void (recset->priv->wrapper, 
-							 (GdaThreadWrapperFunc) g_object_unref,
+							 (GdaThreadWrapperVoidFunc) g_object_unref,
 							 recset->priv->sub_model, NULL, NULL);
 			recset->priv->sub_model = NULL;
 			g_object_unref (recset->priv->wrapper);
 			recset->priv->wrapper = NULL;
+
+			if (recset->priv->blobs_conv) {
+				g_free (recset->priv->blobs_conv);
+				recset->priv->blobs_conv = NULL;
+			}
 		}
 		g_free (recset->priv);
 		recset->priv = NULL;
@@ -159,6 +167,23 @@
 	model->priv->wrapper = g_object_ref (wrapper);
 	model->priv->sub_model = g_object_ref (sub_model);
 
+	gint ncols, i, nblobs;
+	gint *blobs_conv = NULL;
+	ncols = gda_data_model_get_n_columns (sub_model);
+	nblobs = 0;
+	for (i = 0; i < ncols; i++) {
+		GdaColumn *col;
+		col = gda_data_model_describe_column (sub_model, i);
+		if (gda_column_get_g_type (col) == GDA_TYPE_BLOB) {
+			if (!blobs_conv)
+				blobs_conv = g_new0 (gint, ncols);
+			blobs_conv [nblobs] = i;
+			nblobs++;
+		}
+	}
+	model->priv->blobs_conv = blobs_conv;
+	model->priv->nblobs = nblobs;
+
 	COPY_PUBLIC_DATA (sub_model, model);
 
         return GDA_DATA_MODEL (model);
@@ -177,9 +202,14 @@
 {
 	/* WARNING: function executed in sub thread! */
 	gint retval;
+	gint *res;
 	retval = DATA_SELECT_CLASS (model)->fetch_nb_rows (model);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %d\n", __FUNCTION__, retval);
-	return GINT_TO_POINTER (retval);
+#endif
+	res = g_new (gint, 1);
+	*res = retval;
+	return res;
 }
 
 static gint
@@ -187,10 +217,15 @@
 {
 	GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
 	gint nb;
-	gda_thread_wrapper_execute (rs->priv->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_fetch_nb_rows, 
-				    rs->priv->sub_model, NULL, NULL);
-	nb = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, NULL));
+	gint *res;
+	guint jid;
+	jid = gda_thread_wrapper_execute (rs->priv->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_fetch_nb_rows, 
+					  rs->priv->sub_model, NULL, NULL);
+	
+	res = (gint*) gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, jid, NULL);
+	nb = *res;
+	g_free (res);
 	COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
 	return nb;
 }
@@ -210,27 +245,53 @@
 	/* WARNING: function executed in sub thread! */
 	gboolean retval;
 	retval = DATA_SELECT_CLASS (data->select)->fetch_random (data->select, data->row, data->rownum, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval);
 }
 
+static void
+alter_blob_values (GdaThreadRecordset *rs, GdaRow **prow)
+{
+	gint i;
+	for (i = 0; i < rs->priv->nblobs; i++) {
+		GValue *value = gda_row_get_value (*prow, rs->priv->blobs_conv[i]);
+		if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
+			GdaBlob *blob;
+			blob = (GdaBlob*) gda_value_get_blob (value);
+			if (blob->op) {
+				GdaBlobOp *nop;
+				nop = _gda_thread_blob_op_new (rs->priv->wrapper, blob->op);
+				g_object_unref (blob->op);
+				blob->op = nop;
+			}
+		}
+	}
+}
+
 static gboolean
 gda_thread_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
 {
 	GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
 	ThreadData wdata;
 	gboolean retval;
+	guint jid;
 
 	wdata.select = (GdaDataSelect *) rs->priv->sub_model;
 	wdata.rownum = rownum;
 	wdata.row = prow;
-	gda_thread_wrapper_execute (rs->priv->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_fetch_random, 
-				    &wdata, NULL, NULL);
+	jid = gda_thread_wrapper_execute (rs->priv->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_fetch_random, 
+					  &wdata, NULL, NULL);
 
-	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ? 
+	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, jid, error)) ? 
 		TRUE : FALSE;
 	COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+
+	if (*prow && rs->priv->blobs_conv)
+		alter_blob_values (rs, prow);
+
 	return retval;
 }
 
@@ -243,7 +304,9 @@
 	/* WARNING: function executed in sub thread! */
 	gboolean retval;
 	retval = DATA_SELECT_CLASS (data->select)->fetch_next (data->select, data->row, data->rownum, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval);
 }
 
@@ -253,16 +316,21 @@
 	GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
 	ThreadData wdata;
 	gboolean retval;
+	guint jid;
 
 	wdata.select = (GdaDataSelect *) rs->priv->sub_model;
 	wdata.rownum = rownum;
 	wdata.row = prow;
-	gda_thread_wrapper_execute (rs->priv->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_fetch_next, 
-				    &wdata, NULL, NULL);
-	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ? 
+	jid = gda_thread_wrapper_execute (rs->priv->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_fetch_next, 
+					  &wdata, NULL, NULL);
+	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, jid, error)) ? 
 		TRUE : FALSE;
 	COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+
+	if (*prow && rs->priv->blobs_conv)
+		alter_blob_values (rs, prow);
+
 	return retval;
 }
 
@@ -275,7 +343,9 @@
 	/* WARNING: function executed in sub thread! */
 	gboolean retval;
 	retval = DATA_SELECT_CLASS (data->select)->fetch_prev (data->select, data->row, data->rownum, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval);
 }
 
@@ -285,16 +355,21 @@
 	GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
 	ThreadData wdata;
 	gboolean retval;
+	guint jid;
 
 	wdata.select = (GdaDataSelect *) rs->priv->sub_model;
 	wdata.rownum = rownum;
 	wdata.row = prow;
-	gda_thread_wrapper_execute (rs->priv->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_fetch_prev, 
-				    &wdata, NULL, NULL);
-	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ? 
+	jid = gda_thread_wrapper_execute (rs->priv->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_fetch_prev, 
+					  &wdata, NULL, NULL);
+	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, jid, error)) ? 
 		TRUE : FALSE;
 	COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+
+	if (*prow && rs->priv->blobs_conv)
+		alter_blob_values (rs, prow);
+
 	return retval;
 }
 
@@ -307,7 +382,9 @@
 	/* WARNING: function executed in sub thread! */
 	gboolean retval;
 	retval = DATA_SELECT_CLASS (data->select)->fetch_at (data->select, data->row, data->rownum, error);
+#ifdef GDA_DEBUG_NO
 	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
+#endif
 	return GINT_TO_POINTER (retval);
 }
 
@@ -317,15 +394,20 @@
 	GdaThreadRecordset *rs = (GdaThreadRecordset*) model;
 	ThreadData wdata;
 	gboolean retval;
+	guint jid;
 
 	wdata.select = (GdaDataSelect *) rs->priv->sub_model;
 	wdata.rownum = rownum;
 	wdata.row = prow;
-	gda_thread_wrapper_execute (rs->priv->wrapper, 
-				    (GdaThreadWrapperFunc) sub_thread_fetch_at, 
-				    &wdata, NULL, NULL);
-	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, NULL, error)) ? 
+	jid = gda_thread_wrapper_execute (rs->priv->wrapper, 
+					  (GdaThreadWrapperFunc) sub_thread_fetch_at, 
+					  &wdata, NULL, NULL);
+	retval = GPOINTER_TO_INT (gda_thread_wrapper_fetch_result (rs->priv->wrapper, TRUE, jid, error)) ? 
 		TRUE : FALSE;
 	COPY_PUBLIC_DATA (rs->priv->sub_model, rs);
+
+	if (*prow && rs->priv->blobs_conv)
+		alter_blob_values (rs, prow);
+
 	return retval;
 }

Modified: trunk/libgda/thread-wrapper/gda-thread-wrapper.c
==============================================================================
--- trunk/libgda/thread-wrapper/gda-thread-wrapper.c	(original)
+++ trunk/libgda/thread-wrapper/gda-thread-wrapper.c	Tue Apr 14 18:49:11 2009
@@ -341,6 +341,7 @@
 			}
 			job->shared->current_job = NULL;
 			shared_data_add_nb_waiting (job->shared, -1);
+			/*g_print ("... done job %d\n", job->job_id);*/
 			g_async_queue_push (job->reply_queue, res);
 		}
 		else
@@ -522,14 +523,14 @@
 	g_return_val_if_fail (wrapper->priv, 0);
 	g_return_val_if_fail (func, 0);
 
-	gda_mutex_lock (wrapper->priv->mutex);
-
 	td = get_thread_data (wrapper);
 	shared_data_add_nb_waiting (td->shared, 1);
 
 	job = g_new0 (Job, 1);
 	job->type = JOB_TYPE_EXECUTE;
+	gda_mutex_lock (wrapper->priv->mutex);
 	job->job_id = wrapper->priv->next_job_id++;
+	gda_mutex_unlock (wrapper->priv->mutex);
 	job->func = func;
 	job->void_func = NULL;
 	job->arg = arg;
@@ -538,9 +539,25 @@
 	job->shared = shared_data_ref (td->shared);
 
 	id = job->job_id;
-	gda_mutex_unlock (wrapper->priv->mutex);
+	/*g_print ("... submitted job %d\n", id);*/
 
-	g_async_queue_push (wrapper->priv->to_sub_thread, job);
+	if (g_thread_self () == wrapper->priv->sub_thread) {
+                Result *res = g_new0 (Result, 1);
+                res->job = job;
+                res->type = RESULT_TYPE_EXECUTE;
+                job->shared->current_job = job;
+                if (job->func)
+                        res->u.exe.result = job->func (job->arg, &(res->u.exe.error));
+                else {
+                        res->u.exe.result = NULL;
+                        job->void_func (job->arg, &(res->u.exe.error));
+                }
+                job->shared->current_job = NULL;
+                shared_data_add_nb_waiting (job->shared, -1);
+                g_async_queue_push (job->reply_queue, res);
+        }
+        else
+                g_async_queue_push (wrapper->priv->to_sub_thread, job);
 
 	return id;
 }
@@ -582,14 +599,14 @@
 	g_return_val_if_fail (wrapper->priv, 0);
 	g_return_val_if_fail (func, 0);
 
-	gda_mutex_lock (wrapper->priv->mutex);
-
 	td = get_thread_data (wrapper);
 	shared_data_add_nb_waiting (td->shared, 1);
 
 	job = g_new0 (Job, 1);
 	job->type = JOB_TYPE_EXECUTE;
+	gda_mutex_lock (wrapper->priv->mutex);
 	job->job_id = wrapper->priv->next_job_id++;
+	gda_mutex_unlock (wrapper->priv->mutex);
 	job->func = NULL;
 	job->void_func = func;
 	job->arg = arg;
@@ -598,9 +615,25 @@
 	job->shared = shared_data_ref (td->shared);
 
 	id = job->job_id;
-	gda_mutex_unlock (wrapper->priv->mutex);
+	/*g_print ("... submitted VOID job %d\n", id);*/
 
-	g_async_queue_push (wrapper->priv->to_sub_thread, job);
+	if (g_thread_self () == wrapper->priv->sub_thread) {
+                Result *res = g_new0 (Result, 1);
+                res->job = job;
+                res->type = RESULT_TYPE_EXECUTE;
+                job->shared->current_job = job;
+                if (job->func)
+                        res->u.exe.result = job->func (job->arg, &(res->u.exe.error));
+                else {
+                        res->u.exe.result = NULL;
+                        job->void_func (job->arg, &(res->u.exe.error));
+                }
+                job->shared->current_job = NULL;
+                shared_data_add_nb_waiting (job->shared, -1);
+                g_async_queue_push (job->reply_queue, res);
+        }
+        else
+		g_async_queue_push (wrapper->priv->to_sub_thread, job);
 
 	return id;
 }
@@ -627,11 +660,10 @@
 	g_return_if_fail (wrapper->priv);
 
 	gda_mutex_lock (wrapper->priv->mutex);
-
 	td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+	gda_mutex_unlock (wrapper->priv->mutex);
 	if (!td) {
 		/* nothing to be done for this thread */
-		gda_mutex_unlock (wrapper->priv->mutex);
 		return;
 	}
 
@@ -667,15 +699,13 @@
 		if (do_again)
 			goto again;
 	}
-	
-	gda_mutex_unlock (wrapper->priv->mutex);
 }
 
 /**
  * gda_thread_wrapper_fetch_result
  * @wrapper: a #GdaThreadWrapper object
  * @may_lock: TRUE if this funct must lock the caller untill a result is available
- * @id: a place to store the job ID of the function which execution has finished
+ * @exp_id: ID of the job for which a result is expected
  * @error: a place to store errors, for errors which may have occurred during the execution, or %NULL
  *
  * Use this method to check if the execution of a function is finished. The function's execution must have
@@ -689,7 +719,7 @@
  * Since: 4.2
  */
 gpointer
-gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, guint *out_id, GError **error)
+gda_thread_wrapper_fetch_result (GdaThreadWrapper *wrapper, gboolean may_lock, guint exp_id, GError **error)
 {
 	ThreadData *td;
 	Result *res = NULL;
@@ -697,32 +727,55 @@
 
 	g_return_val_if_fail (GDA_IS_THREAD_WRAPPER (wrapper), NULL);
 	g_return_val_if_fail (wrapper->priv, NULL);
+	g_return_val_if_fail (exp_id > 0, NULL);
 
 	gda_mutex_lock (wrapper->priv->mutex);
 	td = g_hash_table_lookup (wrapper->priv->threads_hash, g_thread_self());
+	gda_mutex_unlock (wrapper->priv->mutex);
 	if (!td) {
 		/* nothing to be done for this thread */
-		gda_mutex_unlock (wrapper->priv->mutex);
 		return NULL;
 	}
 	
-	gda_thread_wrapper_iterate (wrapper, may_lock);
-	if (td->results) {
-		res = RESULT (td->results->data);
-		td->results = g_slist_delete_link (td->results, td->results);
-		if (!(td->results) &&
-		    (td->shared->nb_waiting == 0) &&
-		    (g_async_queue_length (td->from_sub_thread) == 0) &&
-		    !td->signals_list) {
-			/* remove this ThreadData */
-			g_hash_table_remove (wrapper->priv->threads_hash, g_thread_self());
+	do {
+		if (td->results) {
+			/* see if we have the result we want */
+			GSList *list;
+			for (list = td->results; list; list = list->next) {
+				res = RESULT (list->data);
+				if (res->job->job_id == exp_id) {
+					/* found it */
+					td->results = g_slist_delete_link (td->results, list);
+					if (!(td->results) &&
+					    (td->shared->nb_waiting == 0) &&
+					    (g_async_queue_length (td->from_sub_thread) == 0) &&
+					    !td->signals_list) {
+						/* remove this ThreadData */
+						gda_mutex_lock (wrapper->priv->mutex);
+						g_hash_table_remove (wrapper->priv->threads_hash, g_thread_self());
+						gda_mutex_unlock (wrapper->priv->mutex);
+					}
+					goto out;
+				}
+			}
 		}
-	}
+		
+		if (may_lock) 
+			gda_thread_wrapper_iterate (wrapper, TRUE);
+		else {
+			gint len;
+			len = g_slist_length (td->results);
+			gda_thread_wrapper_iterate (wrapper, FALSE);
+			if (g_slist_length (td->results) == len) {
+				res = NULL;
+				break;
+			}
+		}
+	} while (1);
 
+ out:
 	if (res) {
 		g_assert (res->type == RESULT_TYPE_EXECUTE);
-		if (out_id)
-			*out_id = res->job->job_id;
 		if (res->u.exe.error) {
 			g_propagate_error (error, res->u.exe.error);
 			res->u.exe.error = NULL;
@@ -732,7 +785,6 @@
 		result_free (res);
 	}
 
-	gda_mutex_unlock (wrapper->priv->mutex);
 	return retval;
 }
 

Modified: trunk/libgda/thread-wrapper/gda-thread-wrapper.h
==============================================================================
--- trunk/libgda/thread-wrapper/gda-thread-wrapper.h	(original)
+++ trunk/libgda/thread-wrapper/gda-thread-wrapper.h	Tue Apr 14 18:49:11 2009
@@ -75,7 +75,7 @@
 							     gpointer arg, GDestroyNotify arg_destroy_func, GError **error);
 void                   gda_thread_wrapper_iterate           (GdaThreadWrapper *wrapper, gboolean may_block);
 gpointer               gda_thread_wrapper_fetch_result      (GdaThreadWrapper *wrapper, gboolean may_lock, 
-							     guint *out_id, GError **error);
+							     guint exp_id, GError **error);
 
 gint                   gda_thread_wrapper_get_waiting_size (GdaThreadWrapper *wrapper);
 

Added: trunk/samples/AsyncExec/Makefile
==============================================================================
--- (empty file)
+++ trunk/samples/AsyncExec/Makefile	Tue Apr 14 18:49:11 2009
@@ -0,0 +1,12 @@
+CFLAGS = -Wall -g -DGDA_DISABLE_DEPRECATED `pkg-config --cflags libgda-4.0`
+LDFLAGS = `pkg-config --libs libgda-4.0`
+
+all: example
+
+example: example.c
+	$(CC) -o example example.c $(CFLAGS) $(LDFLAGS)
+
+clean:
+	rm -f *~
+	rm -f *.o
+	rm -f example

Added: trunk/samples/AsyncExec/README
==============================================================================
--- (empty file)
+++ trunk/samples/AsyncExec/README	Tue Apr 14 18:49:11 2009
@@ -0,0 +1,61 @@
+Libgda simple example
+=====================
+
+Description:
+------------
+
+The example in this directory illustrate how to use the asynchronous statement execution
+introduced in Libgda 4.2.
+
+Compiling and running:
+----------------------
+
+To compile (make sure Libgda is installed prior to this):
+> make
+
+and to run:
+> ./example
+
+Output:
+-------
+Running should produce the following output:
+
+=========== Async. execution request:
+SELECT * from customers
+Assigned task id: 1
+=========== Async. execution request:
+SELECT * from products LIMIT 2
+Assigned task id: 2
+=========== Async. exececution request:
+SELECT * from customers WHERE id>=##theid::int
+        theid => 6
+Assigned task id: 3
+=========== Waiting for task 2
+Not yet executed...
+=========== Waiting for task 2
+Not yet executed...
+=========== Waiting for task 2
+ref | category | name            | price     | wh_stored
+----+----------+-----------------+-----------+----------
+1   |       14 | ACADEMY ACADEMY | 25.990000 |         0
+2   |        6 | ACADEMY ACE     | 20.990000 |         0
+(2 rows)
+=========== Waiting for task 10
+Task not found error: Can't find task 10
+=========== Waiting for task 1
+id | name            | default_served_by | country | city
+---+-----------------+-------------------+---------+-----
+ 2 | Ed Lamton       |                 4 | SP      | MDR
+ 3 | Lew Bonito      |                 1 | FR      | TLS
+ 4 | Mark Lawrencep  |              NULL | SP      | MDR
+ 9 | Greg Popoff     |                 2 | SP      | MDR
+10 | Vladimir Zirkov |                 4 | NULL    | NULL
+(5 rows)
+=========== Waiting for task 2
+Task not found error: Can't find task 2
+=========== Waiting for task 3
+id | name            | default_served_by | country | city
+---+-----------------+-------------------+---------+-----
+ 9 | Greg Popoff     |                 2 | SP      | MDR
+10 | Vladimir Zirkov |                 4 | NULL    | NULL
+(2 rows)
\ No newline at end of file

Added: trunk/samples/AsyncExec/example.c
==============================================================================
--- (empty file)
+++ trunk/samples/AsyncExec/example.c	Tue Apr 14 18:49:11 2009
@@ -0,0 +1,153 @@
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <string.h>
+#include <unistd.h>
+
+static GdaStatement *create_statement (const gchar *sql);
+static void fetch_execute_result (GdaConnection *cnc, guint task_id);
+static guint request_execution (GdaConnection *cnc, const gchar *sql);
+static guint request_execution_with_params (GdaConnection *cnc, const gchar *sql, ...);
+
+int
+main (int argc, char** argv)
+{
+	GdaConnection *cnc;
+	guint id1, id2, id3;
+	GError *error = NULL;
+
+	gda_init ();
+
+	cnc = gda_connection_open_from_dsn ("SalesTest", NULL, GDA_CONNECTION_OPTIONS_THREAD_SAFE, NULL);
+	if (!cnc) {
+		g_print ("Can't open connection: %s\n", error && error->message ? error->message : "No detail");
+		return 1;
+	}
+
+	/* execute */
+	id1 = request_execution (cnc, "SELECT * from customers");
+	id2 = request_execution (cnc, "SELECT * from products LIMIT 2");
+	id3 = request_execution_with_params (cnc, "SELECT * from customers WHERE id>=##theid::int", "theid", "6", NULL);
+
+	/* fetch result */
+	fetch_execute_result (cnc, id2);
+	fetch_execute_result (cnc, 10);
+	fetch_execute_result (cnc, id1);
+	fetch_execute_result (cnc, id2);
+	fetch_execute_result (cnc, id3);
+
+	/*gda_statement_get_parameters (stmt, &parameters, NULL);*/
+  
+	g_object_unref (cnc);
+
+	return 0;
+}
+
+static GdaStatement *
+create_statement (const gchar *sql)
+{
+	GdaSqlParser *parser;
+	GdaStatement *stmt;
+	parser = gda_sql_parser_new ();
+	stmt = gda_sql_parser_parse_string (parser,  sql, NULL, NULL);
+	g_object_unref (parser);
+	return stmt;
+}
+
+static guint
+request_execution (GdaConnection *cnc, const gchar *sql)
+{
+	GdaStatement *stmt;
+	guint id;
+	GError *error = NULL;
+
+	g_print ("=========== Async. execution request:\n%s\n", sql);
+	stmt = create_statement (sql);
+	id = gda_connection_async_statement_execute (cnc, stmt, NULL, GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, FALSE,
+						     &error);
+	g_object_unref (stmt);
+	if (id == 0) {
+		g_print ("Can't execute ASYNC: %s\n", error && error->message ? error->message : "No detail");
+		exit (1);
+	}
+	g_print ("Assigned task id: %u\n", id);
+	return id;
+}
+
+/*
+ * ... is a list of (const gchar *name, const gchar *value) couples, terminated by %NULL
+ */
+static guint
+request_execution_with_params (GdaConnection *cnc, const gchar *sql, ...)
+{
+	GdaStatement *stmt;
+	guint id;
+	GError *error = NULL;
+	GdaSet *params;
+	const gchar *pname;
+
+	g_print ("=========== Async. exececution request:\n%s\n", sql);
+	stmt = create_statement (sql);
+	if (! gda_statement_get_parameters (stmt, &params, &error)) {
+		g_print ("Can't get statement's parameters: %s\n", error && error->message ? error->message : "No detail");
+		exit (1);
+	}
+
+	if (params) {
+		va_list ap;
+		va_start (ap, sql);
+		for (pname = va_arg (ap, const gchar *); pname; pname = va_arg (ap, const gchar *)) {
+			const gchar *value;
+			GdaHolder *holder;
+			holder = gda_set_get_holder (params, pname);
+			value = va_arg (ap, const gchar *);
+			if (holder)
+				g_assert (gda_holder_set_value_str (holder, NULL, value, NULL));
+			g_print ("\t%s => %s\n", pname, value);
+			va_end (ap);
+		}
+	}
+	
+	id = gda_connection_async_statement_execute (cnc, stmt, params, GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, FALSE,
+						     &error);
+	if (params)
+		g_object_unref (params);
+	g_object_unref (stmt);
+	if (id == 0) {
+		g_print ("Can't execute ASYNC: %s\n", error && error->message ? error->message : "No detail");
+		exit (1);
+	}
+	g_print ("Assigned task id: %u\n", id);
+	return id;
+}
+
+static void
+fetch_execute_result (GdaConnection *cnc, guint task_id)
+{
+	GObject *result;
+	GError *error = NULL;
+	gint i;
+	for (i = 0; i < 10; i++) {
+		g_print ("=========== Waiting for task %u\n", task_id);
+		result = gda_connection_async_fetch_result (cnc, task_id, NULL, &error);
+		if (result) {
+			if (GDA_IS_DATA_MODEL (result))
+				gda_data_model_dump (GDA_DATA_MODEL (result), NULL);
+			else
+				g_print ("Unknown result: %s\n", G_OBJECT_TYPE_NAME (result));
+			g_object_unref (result);
+			return;
+		}
+		else if (error) {
+			if (error && (error->domain == GDA_CONNECTION_ERROR) &&
+			    (error->code == GDA_CONNECTION_TASK_NOT_FOUND_ERROR))
+				g_print ("Task not found error: %s\n", error->message);
+			else
+				g_print ("Execution failed: %s\n", error->message ? error->message : "No detail");
+			return;
+		}
+		else {
+			g_print ("Not yet executed...\n");
+			g_usleep (100000);
+		}
+	}
+}

Modified: trunk/samples/Makefile
==============================================================================
--- trunk/samples/Makefile	(original)
+++ trunk/samples/Makefile	Tue Apr 14 18:49:11 2009
@@ -1,5 +1,5 @@
 SHELL= /bin/sh 
-SUBDIRS = BDB DDL DirDataModel F-Spot Report SimpleExample SqlParserConsole TableCopy Virtual XSLT MetaStore Tree SqlBuilder
+SUBDIRS = BDB DDL DirDataModel F-Spot Report SimpleExample SqlParserConsole TableCopy Virtual XSLT MetaStore Tree SqlBuilder AsyncExec
 all:
 	for dir in ${SUBDIRS} ; do ( cd $$dir ; ${MAKE} ) ; done
 clean:

Modified: trunk/samples/README
==============================================================================
--- trunk/samples/README	(original)
+++ trunk/samples/README	Tue Apr 14 18:49:11 2009
@@ -19,5 +19,6 @@
 * in MetaStore/: simple example to show how to use and update a GdaMetaStore
 * in Tree/: simple example to show how to use the GdaTree object
 * in SqlBuilder: simple example to show how to use GdaSqlBuilder object to build various statements
+* in AsyncExec: simple example illustrating how to perform asynchronous statement execution
 
 Good luck and happy hacking!

Modified: trunk/tests/multi-threading/check_wrapper.c
==============================================================================
--- trunk/tests/multi-threading/check_wrapper.c	(original)
+++ trunk/tests/multi-threading/check_wrapper.c	Tue Apr 14 18:49:11 2009
@@ -178,19 +178,13 @@
 
 	/* pick up results */
 	for (i = 0; i < NCALLS; i++) {
-		guint id;
 		gpointer res;
-		res = gda_thread_wrapper_fetch_result (wrapper, TRUE, &id, NULL);
+		res = gda_thread_wrapper_fetch_result (wrapper, TRUE, ids[2*i], NULL);
 		if (GPOINTER_TO_INT (res) != i + GPOINTER_TO_INT (int_id) * 1000) {
 			g_print ("Error in %s() for thread %d: sub thread's exec result is wrong\n",
 				 __FUNCTION__, GPOINTER_TO_INT (int_id));
 			nfailed ++;
 		}
-		if (id != ids[2*i]) {
-			g_print ("Error in %s() for thread %d: sub thread's exec result is for wrong ID\n",
-				 __FUNCTION__, GPOINTER_TO_INT (int_id));
-			nfailed ++;
-		}
 #ifdef PRINT_CALLS
 		g_print ("<-- Thread %d jobID %u arg %d\n", GPOINTER_TO_INT (int_id), id, i + GPOINTER_TO_INT (int_id) * 1000);
 #endif
@@ -199,18 +193,13 @@
 
 		gchar *str;
 		estr = g_strdup_printf ("Hello %d.%d", GPOINTER_TO_INT (int_id), i);
-		str = gda_thread_wrapper_fetch_result (wrapper, TRUE, &id, NULL);
+		str = gda_thread_wrapper_fetch_result (wrapper, TRUE, ids[2*i+1], NULL);
 		if (!str || strcmp (str, estr)) {
 			g_print ("Error in %s() for thread %d: sub thread's exec result is wrong: got %s, exp: %s\n",
 				 __FUNCTION__, GPOINTER_TO_INT (int_id), str, estr);
 			nfailed ++;
 		}
 		g_free (estr);
-		if (id != ids[2*i+1]) {
-			g_print ("Error in %s() for thread %d: sub thread's exec result is for wrong ID\n",
-				 __FUNCTION__, GPOINTER_TO_INT (int_id));
-			nfailed ++;
-		}
 #ifdef PRINT_CALLS
 		g_print ("<-- Thread %d jobID %u arg %s\n", GPOINTER_TO_INT (int_id), id, str);
 #endif
@@ -583,22 +572,16 @@
 
 	/* pick up results */
 	for (i = 0; i < NCALLS; i++) {
-		guint id;
 		gpointer res;
 
 		g_signal_emit_by_name (dummy, "sig0", NULL); /* this signal should be ignored */
 
-		res = gda_thread_wrapper_fetch_result (wrapper, TRUE, &id, NULL);
+		res = gda_thread_wrapper_fetch_result (wrapper, TRUE, ids[i], NULL);
 		if (GPOINTER_TO_INT (res) != INT_TOKEN) {
 			g_print ("Error in %s() for thread %p: sub thread's exec result is wrong\n",
 				 __FUNCTION__, g_thread_self ());
 			nfailed ++;
 		}
-		if (id != ids[i]) {
-			g_print ("Error in %s() for thread %p: sub thread's exec result is for wrong ID\n",
-				 __FUNCTION__, g_thread_self ());
-			nfailed ++;
-		}
 #ifdef PRINT_CALLS
 		g_print ("<-- Thread %p jobID %u arg %d\n", g_thread_self (), id, INT_TOKEN);
 #endif

Modified: trunk/tools/test_blob.sh
==============================================================================
--- trunk/tools/test_blob.sh	(original)
+++ trunk/tools/test_blob.sh	Tue Apr 14 18:49:11 2009
@@ -21,7 +21,7 @@
 #prefix=sqlite
 
 rm -f EXPORT_gda_sql_c EXPORT_gda_sql_ico
-./gda-sql-4.0 ${prefix}blobs <<EOF
+./gda-sql-4.1 ${prefix}blobs <<EOF
 delete from blobs;
 .setex bl gda-sql.c
 insert into blobs (id, descr, blob) values (1, 'descr1', ##bl::GdaBlob);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]