[libgda] Multi threading and GdaSqlBuilder improvements



commit 981374e707a6276b2fd4003a6c27228d16643365
Author: Vivien Malerba <malerba gnome-db org>
Date:   Fri Apr 17 21:34:06 2009 +0200

    Multi threading and GdaSqlBuilder improvements
    
    	* providers/postgres/gda-postgres-provider.c: fix for bug
    	#578748
    	* libgda/thread-wrapper/gda-thread-wrapper.c: don't attempt to
    	create a GdaThreadWrapper object if g_thread_supported() is FALSE
    	* libgda/thread-wrapper/Makefile.am:
    	* libgda/thread-wrapper/gda-thread-meta.[ch]: meta data
    	retreival implementation
    	* libgda/thread-wrapper/gda-thread-provider.[ch]:
    	  - reuse the same GdaThreadWrapper object for providers which
    	    can only be
    	    accessed from a single thread
    	  - correctly behave when g_thread_supported() is FALSE
    	  - use new implementation of meta data retreival
    	* libgda/gda-server-provider.c: doc. correction
    	* libgda/gda-connection.c:
    	  - removed debug message
    	  - corrected locking in gda_connection_update_meta_store()
    	  - return an error if the GDA_CONNECTION_OPTIONS_THREAD_SAFE
    	    flag is specified
    	    but g_thread_supported() is FALSE
    	  - gda_connection_update_meta_store()'s implementation is now
    	    generic (no specific
    	    implementation for connections which use a thread wrapper)
    	* tools/gda-sql.c:
    	  - use the GDA_CONNECTION_OPTIONS_THREAD_SAFE flag when opening
    	    connections
    	  - meta data retreival is now done in the background
    	    (statements can be executed
    	    while it is being done)
    	* libgda/Makefile.am:
    	* libgda/libgda.symbols:
    	* libgda/gda-repetitive-statement.[ch]: new object to prepare
    	the execution of a statement with several differents variables' values sets
    	* libgda/gda-connection.c: added gda_connection_repetitive_statement_execute()
    	* doc/C: doc.update
    	* configure.in: a system installed SQLite is now only searched
    	for if the --enable-system-sqlite option is passed, and the presence of the
    	sqlite3CreateFunc symbol is not anymore required. The only drawback of using a
    	system installed SQLite is that meta data regarding functions will not be
    	available
    	* Makefile.am:
    	* samples/MetaStore/background.c:
    	* samples/MetaStore/README: new example showing how to update
    	the meta data in the background (in a sub thread) using the GdaThreadWrapper
    	object
    	* libgda/handlers/gda-handler-time.c: fix mem leak
    	* samples/SqlBuilder/example.c:
    	* libgda/gda-sql-builder.[ch]: improved API and features of the
    	GdaSqlBuilder object
    	* libgda.doap: project description
---
 ChangeLog                                      |   44 ++
 Makefile.am                                    |    1 +
 configure.in                                   |   33 +-
 doc/C/libgda-4.0-docs.sgml                     |    2 +
 doc/C/libgda-4.0-sections.txt                  |   26 +-
 doc/C/tmpl/gda-connection.sgml                 |   15 +
 doc/C/tmpl/gda-repetitive-statement.sgml       |   71 +++
 doc/C/tmpl/gda-sql-statement.sgml              |    4 +-
 libgda.doap                                    |   26 +
 libgda/Makefile.am                             |    2 +
 libgda/gda-connection-internal.h               |   13 +
 libgda/gda-connection.c                        |  142 +++--
 libgda/gda-connection.h                        |    9 +-
 libgda/gda-decl.h                              |    3 +
 libgda/gda-repetitive-statement.c              |  224 +++++++
 libgda/gda-repetitive-statement.h              |   55 ++
 libgda/gda-server-provider.c                   |    2 +-
 libgda/gda-sql-builder.c                       |  328 ++++++++--
 libgda/gda-sql-builder.h                       |   20 +-
 libgda/handlers/gda-handler-time.c             |    1 +
 libgda/libgda.symbols                          |    6 +
 libgda/sql-parser/gda-statement-struct-parts.h |    2 +-
 libgda/thread-wrapper/Makefile.am              |    2 +
 libgda/thread-wrapper/gda-thread-meta.c        |  795 ++++++++++++++++++++++++
 libgda/thread-wrapper/gda-thread-meta.h        |  197 ++++++
 libgda/thread-wrapper/gda-thread-provider.c    |   87 +++-
 libgda/thread-wrapper/gda-thread-provider.h    |    2 +
 libgda/thread-wrapper/gda-thread-wrapper.c     |    9 +-
 providers/postgres/gda-postgres-provider.c     |    5 +-
 samples/MetaStore/Makefile                     |    7 +-
 samples/MetaStore/README                       |   30 +-
 samples/MetaStore/background.c                 |  107 ++++
 samples/SqlBuilder/example.c                   |   60 ++-
 tools/gda-sql.c                                |    6 +-
 34 files changed, 2178 insertions(+), 158 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d1ebd8e..d5e6f15 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2009-04-17  Vivien Malerba <malerba gnome-db org>
+
+	* providers/postgres/gda-postgres-provider.c: fix for bug #578748
+	* libgda/thread-wrapper/gda-thread-wrapper.c: don't attempt to create a GdaThreadWrapper
+	object if g_thread_supported() is FALSE
+	* libgda/thread-wrapper/Makefile.am:
+	* libgda/thread-wrapper/gda-thread-meta.[ch]: meta data retreival implementation
+	* libgda/thread-wrapper/gda-thread-provider.[ch]:
+	  - reuse the same GdaThreadWrapper object for providers which can only be
+	    accessed from a single thread
+	  - correctly behave when g_thread_supported() is FALSE
+	  - use new implementation of meta data retreival
+	* libgda/gda-server-provider.c: doc. correction
+	* libgda/gda-connection.c:
+	  - removed debug message
+	  - corrected locking in gda_connection_update_meta_store()
+	  - return an error if the GDA_CONNECTION_OPTIONS_THREAD_SAFE flag is specified
+	    but g_thread_supported() is FALSE
+	  - gda_connection_update_meta_store()'s implementation is now generic (no specific
+	    implementation for connections which use a thread wrapper)
+	* tools/gda-sql.c:
+	  - use the GDA_CONNECTION_OPTIONS_THREAD_SAFE flag when opening connections
+	  - meta data retreival is now done in the background (statements can be executed
+	    while it is being done)
+	* libgda/Makefile.am:
+	* libgda/libgda.symbols:
+	* libgda/gda-repetitive-statement.[ch]: new object to prepare the execution of
+	a statement with several differents variables' values sets
+	* libgda/gda-connection.c: added gda_connection_repetitive_statement_execute()
+	* doc/C: doc.update
+	* configure.in: a system installed SQLite is now only searched for if the
+	--enable-system-sqlite option is passed, and the presence of the sqlite3CreateFunc
+	symbol is not anymore required. The only drawback of using a system installed
+	SQLite is that meta data regarding functions will not be available
+	* Makefile.am:
+	* samples/MetaStore/background.c:
+	* samples/MetaStore/README: new example showing how to update the meta data
+	in the background (in a sub thread) using the GdaThreadWrapper object
+	* libgda/handlers/gda-handler-time.c: fix mem leak
+	* samples/SqlBuilder/example.c:
+	* libgda/gda-sql-builder.[ch]: improved API and features of the GdaSqlBuilder
+	object
+	* libgda.doap: project description
+
 2009-04-14  Vivien Malerba <malerba gnome-db org>
 
 	* libgda/thread-wrapper/Makefile.am:
diff --git a/Makefile.am b/Makefile.am
index ead5a4d..fc7fa6d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,6 +65,7 @@ example_files = \
 	samples/XSLT/transform.c \
 	samples/MetaStore/Makefile \
 	samples/MetaStore/README \
+	samples/MetaStore/background.c \
 	samples/MetaStore/example.c \
 	samples/Tree/Makefile \
 	samples/Tree/README \
diff --git a/configure.in b/configure.in
index 7d82ce2..548667d 100644
--- a/configure.in
+++ b/configure.in
@@ -1106,31 +1106,30 @@ if test x"$platform_win32" = "xyes"
 then
         AM_CONDITIONAL(HAVE_SQLITE, 0)
 else
-	SQLITE_MODULES="sqlite3 >= 3.6.0"
-	PKG_CHECK_MODULES(SQLITE, $SQLITE_MODULES, have_sqlite=yes, have_sqlite=no)
-	if test x"$have_sqlite" = "xyes"
-	then
-		AC_CHECK_LIB(sqlite3, sqlite3_table_column_metadata,[sqlite3_api=1], [sqlite3_api=0], $SQLITE_CFLAGS $SQLITE_LIBS)
+	AC_ARG_ENABLE(system-sqlite,
+        [  --enable-system-sqlite  Use SQLite installed on the system [default=no]],
+        use_syst_sqlite="Yes", use_syst_sqlite="No")
+
+	if test "x$use_syst_sqlite" = "xYes"; then
+		SQLITE_MODULES="sqlite3 >= 3.6.11"
+		PKG_CHECK_MODULES(SQLITE, $SQLITE_MODULES, have_sqlite=yes, have_sqlite=no)
+		if test x"$have_sqlite" = "xyes"
+		then
+			AC_CHECK_LIB(sqlite3, sqlite3_table_column_metadata,[sqlite3_api=1], [sqlite3_api=0], $SQLITE_CFLAGS $SQLITE_LIBS)
 	
-        	if test $sqlite3_api = 0
-        	then
-                	AC_MSG_RESULT([Installed SQLite was not compiled with the SQLITE_ENABLE_COLUMN_METADATA, using embedded SQLite])
-                	have_sqlite=no
-        	else
-			AC_CHECK_LIB(sqlite3, sqlite3CreateFunc,[sqlite3_api=1], [sqlite3_api=0], $SQLITE_CFLAGS $SQLITE_LIBS)
 			if test $sqlite3_api = 0
-			then
-				AC_MSG_RESULT([Could not find the sqlite3CreateFunc symbol in installed SQLite library, using embedded SQLite])
+        		then
+				AC_MSG_RESULT([Installed SQLite was not compiled with the SQLITE_ENABLE_COLUMN_METADATA, using embedded SQLite])
                 		have_sqlite=no
-			else
-                		AC_MSG_RESULT(Using this version of SQLite)
+        		else
+                		AC_MSG_RESULT(Note: using system installed version of SQLite, meta data for functions will not be available)
 				have_sqlite=yes
                 		SQLITE_CFLAGS="$SQLITE_CFLAGS -DHAVE_SQLITE"
                 		AC_SUBST(SQLITE_CFLAGS)
                 		AC_SUBST(SQLITE_LIBS)
                 		AC_SUBST(SQLITE_VERS)
-			fi
-        	fi
+        		fi
+		fi
 	fi
 	AM_CONDITIONAL(HAVE_SQLITE, test x"$have_sqlite" = "xyes")
 fi
diff --git a/doc/C/libgda-4.0-docs.sgml b/doc/C/libgda-4.0-docs.sgml
index 5e6892b..2b067cd 100644
--- a/doc/C/libgda-4.0-docs.sgml
+++ b/doc/C/libgda-4.0-docs.sgml
@@ -95,6 +95,7 @@
 <!ENTITY libgda-GdaHolder SYSTEM "xml/gda-holder.xml">
 <!ENTITY libgda-GdaSet SYSTEM "xml/gda-set.xml">
 <!ENTITY libgda-GdaStatement SYSTEM "xml/gda-statement.xml">
+<!ENTITY libgda-GdaRepetitiveStatement SYSTEM "xml/gda-repetitive-statement.xml">
 <!ENTITY libgda-GdaSqlStatement SYSTEM "xml/gda-sql-statement.xml">
 <!ENTITY libgda-GdaBatch SYSTEM "xml/gda-batch.xml">
 <!ENTITY libgda-GdaSqlParser SYSTEM "xml/gda-sql-parser.xml">
@@ -545,6 +546,7 @@
       &libgda-GdaSqlParser;
       &libgda-GdaSqlBuilder;
       &libgda-GdaStatement;
+      &libgda-GdaRepetitiveStatement;
       &libgda-GdaBatch;
       &libgda-GdaHolder;
       &libgda-GdaSet;
diff --git a/doc/C/libgda-4.0-sections.txt b/doc/C/libgda-4.0-sections.txt
index 0e5ae5a..aa74914 100644
--- a/doc/C/libgda-4.0-sections.txt
+++ b/doc/C/libgda-4.0-sections.txt
@@ -123,6 +123,7 @@ gda_connection_statement_execute_select
 gda_connection_statement_execute_select_full
 gda_connection_statement_execute_select_fullv
 gda_connection_statement_execute_non_select
+gda_connection_repetitive_statement_execute
 gda_connection_batch_execute
 gda_connection_begin_transaction
 gda_connection_commit_transaction
@@ -1644,13 +1645,17 @@ gda_sql_builder_get_statement
 gda_sql_builder_get_sql_statement
 <SUBSECTION>
 gda_sql_builder_set_table
-gda_sql_builder_add_field_value
+gda_sql_builder_add_field
 <SUBSECTION>
 gda_sql_builder_literal
 gda_sql_builder_expr
 gda_sql_builder_param
 <SUBSECTION>
-gda_sql_builder_cond2
+gda_sql_builder_cond
+<SUBSECTION>
+gda_sql_builder_select_add_target
+gda_sql_builder_select_join_targets
+gda_sql_builder_join_add_field
 <SUBSECTION Standard>
 GDA_SQL_BUILDER
 GDA_SQL_BUILDER_GET_CLASS
@@ -1662,6 +1667,7 @@ gda_sql_builder_get_type
 <SECTION>
 <FILE>gda-thread-wrapper</FILE>
 <TITLE>GdaThreadWrapper</TITLE>
+<INCLUDE>libgda/thread-wrapper/gda-thread-wrapper.h</INCLUDE>
 GdaThreadWrapper
 gda_thread_wrapper_new
 GdaThreadWrapperFunc
@@ -1681,3 +1687,19 @@ GDA_IS_THREAD_WRAPPER
 GDA_TYPE_THREAD_WRAPPER
 gda_thread_wrapper_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gda-repetitive-statement</FILE>
+<TITLE>GdaRepetitiveStatement</TITLE>
+GdaRepetitiveStatement
+gda_repetitive_statement_new
+gda_repetitive_statement_get_template_set
+gda_repetitive_statement_get_all_sets
+gda_repetitive_statement_append_set
+<SUBSECTION Standard>
+GDA_REPETITIVE_STATEMENT
+GDA_REPETITIVE_STATEMENT_GET_CLASS
+GDA_IS_REPETITIVE_STATEMENT
+GDA_TYPE_REPETITIVE_STATEMENT
+gda_repetitive_statement_get_type
+</SECTION>
diff --git a/doc/C/tmpl/gda-connection.sgml b/doc/C/tmpl/gda-connection.sgml
index afef969..5b4b33d 100644
--- a/doc/C/tmpl/gda-connection.sgml
+++ b/doc/C/tmpl/gda-connection.sgml
@@ -165,6 +165,7 @@ A connection to a database
 @GDA_CONNECTION_STATEMENT_TYPE_ERROR: 
 @GDA_CONNECTION_CANT_LOCK_ERROR: 
 @GDA_CONNECTION_TASK_NOT_FOUND_ERROR: 
+ GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR: 
 
 <!-- ##### MACRO GDA_CONNECTION_ERROR ##### -->
 <para>
@@ -344,6 +345,20 @@ A connection to a database
 @Returns: 
 
 
+<!-- ##### FUNCTION gda_connection_repetitive_statement_execute ##### -->
+<para>
+
+</para>
+
+ cnc: 
+ rstmt: 
+ model_usage: 
+ col_types: 
+ stop_on_error: 
+ error: 
+ Returns: 
+
+
 <!-- ##### FUNCTION gda_connection_batch_execute ##### -->
 <para>
 
diff --git a/doc/C/tmpl/gda-repetitive-statement.sgml b/doc/C/tmpl/gda-repetitive-statement.sgml
new file mode 100644
index 0000000..f3f75a7
--- /dev/null
+++ b/doc/C/tmpl/gda-repetitive-statement.sgml
@@ -0,0 +1,71 @@
+<!-- ##### SECTION Title ##### -->
+GdaRepetitiveStatement
+
+<!-- ##### SECTION Short_Description ##### -->
+Execute the same statement several times with different values
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+  The #GdaRepetitiveStatement object allows one to specify a statement to be executed
+  several times using different variables' values sets for each execution. Using the object
+  has almost no interrest at all if the statement to be executed several times has no parameter.
+</para>
+<para>
+  Use the gda_connection_repetitive_statement_execute() method to execute the repetitive statement.
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+  #GdaStatement, #GdaBatch, #GdaConnection
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GdaRepetitiveStatement ##### -->
+<para>
+
+</para>
+
+ parent_instance: 
+
+<!-- ##### FUNCTION gda_repetitive_statement_new ##### -->
+<para>
+
+</para>
+
+ stmt: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_repetitive_statement_get_template_set ##### -->
+<para>
+
+</para>
+
+ rstmt: 
+ set: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_repetitive_statement_get_all_sets ##### -->
+<para>
+
+</para>
+
+ rstmt: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_repetitive_statement_append_set ##### -->
+<para>
+
+</para>
+
+ rstmt: 
+ values: 
+ make_copy: 
+ Returns: 
+
+
diff --git a/doc/C/tmpl/gda-sql-statement.sgml b/doc/C/tmpl/gda-sql-statement.sgml
index 3964032..87c7460 100644
--- a/doc/C/tmpl/gda-sql-statement.sgml
+++ b/doc/C/tmpl/gda-sql-statement.sgml
@@ -1271,8 +1271,8 @@ Specifies the type of functions passed to gda_sql_any_part_foreach().
 @any: inheritance structure
 @type: type of join
 @position: represents a join between a target at (pos &lt; @position) and the one at @position
- expr: joining expression
- use: 
+ expr: joining expression, or %NULL
+ use: list of #GdaSqlField pointers to use when joining, or %NULL
 @_gda_reserved1: 
 @_gda_reserved2: 
 
diff --git a/libgda.doap b/libgda.doap
new file mode 100644
index 0000000..116daa5
--- /dev/null
+++ b/libgda.doap
@@ -0,0 +1,26 @@
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+         xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#";
+         xmlns:foaf="http://xmlns.com/foaf/0.1/";
+         xmlns:gnome="http://api.gnome.org/doap-extensions#";
+         xmlns="http://usefulinc.com/ns/doap#";>
+
+  <!--
+  This is a DOAP template file.  It contains Pulse's best guesses at
+  some basic content.  You should verify the information in this file
+  and modify anything that isn't right.  Add the corrected file to your
+  source code repository to help tools better understand your project.
+  -->
+
+  <name xml:lang="en">libgda</name>
+  <shortdesc xml:lang="en">Database access library</shortdesc>
+  <homepage rdf:resource="http://www.gnome-db.org/"; />
+  <mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-db-list"; />
+
+  <maintainer>
+    <foaf:Person>
+      <foaf:name>Vivien Malerba</foaf:name>
+      <foaf:mbox rdf:resource="malerba gnome-db org" />
+      <gnome:userid>vivien</gnome:userid>
+    </foaf:Person>
+  </maintainer>
+</Project>
diff --git a/libgda/Makefile.am b/libgda/Makefile.am
index 6ab15df..563c45d 100644
--- a/libgda/Makefile.am
+++ b/libgda/Makefile.am
@@ -62,6 +62,7 @@ gda_headers = \
 	gda-meta-struct.h \
 	gda-mutex.h \
 	gda-quark-list.h \
+	gda-repetitive-statement.h \
 	gda-row.h \
 	gda-set.h \
 	gda-server-operation.h \
@@ -131,6 +132,7 @@ libgda_sources =  \
 	gda-meta-struct-io.c \
 	gda-mutex.c \
 	gda-quark-list.c \
+	gda-repetitive-statement.c \
 	gda-row.c \
 	gda-set.c \
 	gda-server-operation.c \
diff --git a/libgda/gda-connection-internal.h b/libgda/gda-connection-internal.h
index 61d51cc..39a61bf 100644
--- a/libgda/gda-connection-internal.h
+++ b/libgda/gda-connection-internal.h
@@ -29,6 +29,19 @@
 
 G_BEGIN_DECLS
 
+/* 
+ * Warnings
+ */
+#ifdef GDA_DEBUG
+#define ASSERT_TABLE_NAME(x,y) g_assert (!strcmp ((x), (y)))
+#define WARN_METHOD_NOT_IMPLEMENTED(prov,method) g_warning ("Provider '%s' does not implement the META method '%s()', please report the error to bugzilla.gnome.org", gda_server_provider_get_name (prov), (method))
+#define WARN_META_UPDATE_FAILURE(x,method) if (!(x)) g_print ("%s (meta method => %s) ERROR: %s\n", __FUNCTION__, (method), error && *error && (*error)->message ? (*error)->message : "???")
+#else
+#define ASSERT_TABLE_NAME(x,y)
+#define WARN_METHOD_NOT_IMPLEMENTED(prov,method)
+#define WARN_META_UPDATE_FAILURE(x,method)
+#endif
+
 /*
  * Opens a connection to an SQLite database. This function is intended to be used
  * internally when objects require an SQLite connection, for example for the GdaMetaStore
diff --git a/libgda/gda-connection.c b/libgda/gda-connection.c
index fad1f8e..52aac06 100644
--- a/libgda/gda-connection.c
+++ b/libgda/gda-connection.c
@@ -46,6 +46,7 @@
 #include <libgda/gda-mutex.h>
 #include <libgda/gda-lockable.h>
 #include <libgda/thread-wrapper/gda-thread-provider.h>
+#include <libgda/gda-repetitive-statement.h>
 
 #define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
 
@@ -746,6 +747,13 @@ gda_connection_open_from_dsn (const gchar *dsn, const gchar *auth_string,
 	gchar *real_auth_string = NULL;
 
 	g_return_val_if_fail (dsn && *dsn, NULL);
+
+	if ((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) && !g_thread_supported ()) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR,
+			      "%s", _("Multi threading is not supported or enabled"));
+		return NULL;
+	}
+
 	gda_dsn_split (dsn, &real_dsn, &user, &pass);
 	if (!real_dsn) {
 		g_free (user);
@@ -889,6 +897,13 @@ gda_connection_open_from_string (const gchar *provider_name, const gchar *cnc_st
 	gchar *real_auth_string = NULL;
 
 	g_return_val_if_fail (cnc_string && *cnc_string, NULL);
+
+	if ((options & GDA_CONNECTION_OPTIONS_THREAD_SAFE) && !g_thread_supported ()) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR,
+			      "%s", _("Multi threading is not supported or enabled"));
+		return NULL;
+	}
+
 	gda_connection_string_split (cnc_string, &real_cnc, &real_provider, &user, &pass);
 	if (!real_cnc) {
 		g_free (user);
@@ -2433,6 +2448,81 @@ gda_connection_statement_execute_select_full (GdaConnection *cnc, GdaStatement *
 	return model;
 }
 
+/**
+ * gda_connection_repetitive_statement_execute
+ * @cnc: a #GdaConnection
+ * @rstmt: a #GdaRepetitiveStatement object
+ * @model_usage: specifies how the returned data model will be used as a #GdaStatementModelUsage enum
+ * @col_types: an array of GType to request each returned GdaDataModel's column's GType, see gda_connection_statement_execute_select_full() for more information
+ * @stop_on_error: set to TRUE if the method has to stop on the first error.
+ * @error: a place to store errors, or %NULL
+ *
+ * Executes the statement upon which @rstmt is built. Note that as several statements can actually be executed by this
+ * method, it is recommended to be within a transaction.
+ *
+ * If @error is not %NULL and @stop_on_error is %FALSE, then it may contain the last error which occurred.
+ *
+ * Returns: a new list of #GObject pointers (see gda_connection_statement_execute() for more information about what they
+ * represent), one for each actual execution of the statement upon which @rstmt is built. If @stop_on_error is %FALSE, then
+ * the list may contain some %NULL pointers which refer to statements which failed to execute.
+ *
+ * Since: 4.2
+ */
+GSList *
+gda_connection_repetitive_statement_execute (GdaConnection *cnc, GdaRepetitiveStatement *rstmt,
+					     GdaStatementModelUsage model_usage, GType *col_types,
+					     gboolean stop_on_error, GError **error)
+{
+	GSList *sets_list, *list;
+	GSList *retlist = NULL;
+	GdaStatement *stmt;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (cnc->priv, NULL);
+	g_return_val_if_fail (cnc->priv->provider_obj, NULL);
+	g_return_val_if_fail (GDA_IS_REPETITIVE_STATEMENT (rstmt), NULL);
+	g_return_val_if_fail (PROV_CLASS (cnc->priv->provider_obj)->statement_execute, NULL);
+
+	g_object_get (rstmt, "statement", &stmt, NULL);
+	g_return_val_if_fail (stmt, NULL);
+
+	gda_connection_lock ((GdaLockable*) cnc);
+
+	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
+	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
+		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
+
+	sets_list = gda_repetitive_statement_get_all_sets (rstmt);
+	for (list = sets_list; list; list = list->next) {
+		GObject *obj;
+		GError *lerror = NULL;
+		obj = PROV_CLASS (cnc->priv->provider_obj)->statement_execute (cnc->priv->provider_obj, cnc, stmt, 
+									       GDA_SET (list->data), 
+									       model_usage, col_types, NULL, 
+									       NULL, NULL, NULL, &lerror);
+		if (!obj) {
+			if (stop_on_error)
+				break;
+			else {
+				if (error && *error) {
+					g_error_free (*error);
+					*error = NULL;
+				}
+				g_propagate_error (error, lerror);
+			}
+		}
+		else
+			retlist = g_slist_prepend (retlist, obj);	
+	}
+	g_slist_free (sets_list);
+
+	gda_connection_unlock ((GdaLockable*) cnc);
+
+	g_object_unref (stmt);
+
+	return g_slist_reverse (retlist);
+}
+
 /*
  * Forces the GdaTransactionStatus. This is reserved to connections which are thread wrappers
  *
@@ -2824,15 +2914,6 @@ check_parameters (GdaMetaContext *context, GError **error, gint nb, ...)
 static gboolean
 local_meta_update (GdaServerProvider *provider, GdaConnection *cnc, GdaMetaContext *context, GError **error)
 {
-#ifdef GDA_DEBUG
-#define ASSERT_TABLE_NAME(x,y) g_assert (!strcmp ((x), (y)))
-#define WARN_METHOD_NOT_IMPLEMENTED(prov,method) g_warning ("Provider '%s' does not implement the META method '%s()', please report the error to bugzilla.gnome.org", gda_server_provider_get_name (prov), (method))
-#define WARN_META_UPDATE_FAILURE(x,method) if (!(x)) g_print ("%s (meta method => %s) ERROR: %s\n", __FUNCTION__, (method), error && *error && (*error)->message ? (*error)->message : "???")
-#else
-#define ASSERT_TABLE_NAME(x,y)
-#define WARN_METHOD_NOT_IMPLEMENTED(prov,method)
-#define WARN_META_UPDATE_FAILURE(x,method)
-#endif
 	const gchar *tname = context->table_name;
 	GdaMetaStore *store;
 	gboolean retval;
@@ -3531,21 +3612,6 @@ suggest_update_cb_downstream (GdaMetaStore *store, GdaMetaContext *suggest, Down
 	return NULL;
 }
 
-typedef struct {
-	GdaConnection  *cnc;
-	GdaMetaContext *context;
-} UpdateMetaStoreData;
-
-static gpointer
-sub_thread_update_meta_store (UpdateMetaStoreData *data, GError **error)
-{
-	/* WARNING: function executed in sub thread! */
-	gboolean retval;
-	retval = gda_connection_update_meta_store (data->cnc, data->context, error);
-	g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");
-	return GINT_TO_POINTER (retval ? 1 : 0);
-}
-
 /**
  * gda_connection_update_meta_store
  * @cnc: a #GdaConnection object.
@@ -3596,29 +3662,13 @@ gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, G
 
 	gda_connection_lock ((GdaLockable*) cnc);
 
-	if (cnc->priv->is_thread_wrapper) {
-		ThreadConnectionData *cdata;
-		UpdateMetaStoreData data;
-		gpointer res;
-		guint jid;
-
-		cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
-		if (!cdata) 
-			return FALSE;
-
-		data.cnc = cdata->sub_connection;
-		data.context = context;
-
-		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;
-	}
-
 	/* Get or create the GdaMetaStore object */
 	store = gda_connection_get_meta_store (cnc);
 	g_assert (store);
 
+	/* unlock connection */
+	gda_connection_unlock ((GdaLockable*) cnc);
+
 	/* prepare local context */
 	GdaMetaContext lcontext;
 
@@ -3638,7 +3688,6 @@ gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, G
 		if (!up_templates) {
 			if (lerror) {
 				g_propagate_error (error, lerror);
-				gda_connection_unlock ((GdaLockable*) cnc);
 				return FALSE;
 			}
 		}
@@ -3646,7 +3695,6 @@ gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, G
 		if (!dn_templates) {
 			if (lerror) {
 				g_propagate_error (error, lerror);
-				gda_connection_unlock ((GdaLockable*) cnc);
 				return FALSE;
 			}
 		}
@@ -3700,7 +3748,6 @@ gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, G
 		g_slist_free (cbd.context_templates);
 		g_hash_table_destroy (cbd.context_templates_hash);
 
-		gda_connection_unlock ((GdaLockable*) cnc);
 		return retval;
 	}
 	else {
@@ -3764,7 +3811,6 @@ gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, G
 		g_assert (nb == sizeof (rmeta) / sizeof (RMeta));
 
 		if (! _gda_meta_store_begin_data_reset (store, error)) {
-			gda_connection_unlock ((GdaLockable*) cnc);
 			return FALSE;
 		}
 
@@ -3791,12 +3837,10 @@ gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, G
 			}
 		}
 		retval = _gda_meta_store_finish_data_reset (store, error);
-		gda_connection_unlock ((GdaLockable*) cnc);
 		return retval;
 
 	onerror:
 		_gda_meta_store_cancel_data_reset (store, NULL);
-		gda_connection_unlock ((GdaLockable*) cnc);
 		return FALSE;
 	}
 }
diff --git a/libgda/gda-connection.h b/libgda/gda-connection.h
index ae1604d..5e636a9 100644
--- a/libgda/gda-connection.h
+++ b/libgda/gda-connection.h
@@ -55,7 +55,8 @@ typedef enum {
 	GDA_CONNECTION_OPEN_ERROR,
 	GDA_CONNECTION_STATEMENT_TYPE_ERROR,
 	GDA_CONNECTION_CANT_LOCK_ERROR,
-	GDA_CONNECTION_TASK_NOT_FOUND_ERROR
+	GDA_CONNECTION_TASK_NOT_FOUND_ERROR,
+	GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR
 } GdaConnectionError;
 #define GDA_CONNECTION_NONEXIST_DSN_ERROR GDA_CONNECTION_DSN_NOT_FOUND_ERROR
 
@@ -197,6 +198,12 @@ GObject             *gda_connection_async_fetch_result      (GdaConnection *cnc,
 gboolean             gda_connection_async_cancel            (GdaConnection *cnc, guint task_id, GError **error);
 
 
+/* repetitive statement */
+GSList             *gda_connection_repetitive_statement_execute (GdaConnection *cnc, GdaRepetitiveStatement *rstmt,
+								 GdaStatementModelUsage model_usage, GType *col_types,
+								 gboolean stop_on_error, GError **error);
+
+/* transactions */
 gboolean             gda_connection_begin_transaction    (GdaConnection *cnc, const gchar *name, 
 							  GdaTransactionIsolation level, GError **error);
 gboolean             gda_connection_commit_transaction   (GdaConnection *cnc, const gchar *name, GError **error);
diff --git a/libgda/gda-decl.h b/libgda/gda-decl.h
index 646e9ec..0db3879 100644
--- a/libgda/gda-decl.h
+++ b/libgda/gda-decl.h
@@ -89,6 +89,9 @@ typedef struct _GdaStatement GdaStatement;
 typedef struct _GdaStatementClass GdaStatementClass;
 typedef struct _GdaStatementPrivate GdaStatementPrivate;
 
+typedef struct _GdaRepetitiveStatement GdaRepetitiveStatement;
+typedef struct _GdaRepetitiveStatementClass GdaRepetitiveStatementClass;
+
 typedef struct _GdaSqlParser GdaSqlParser;
 typedef struct _GdaSqlParserClass GdaSqlParserClass;
 typedef struct _GdaSqlParserPrivate GdaSqlParserPrivate;
diff --git a/libgda/gda-repetitive-statement.c b/libgda/gda-repetitive-statement.c
new file mode 100644
index 0000000..3f0008c
--- /dev/null
+++ b/libgda/gda-repetitive-statement.c
@@ -0,0 +1,224 @@
+/* GDA library
+ * 
+ * Copyright (C) Daniel Espinosa Ortiz 2008 <esodan gmail com>
+ * 
+ * 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 <gda-repetitive-statement.h>
+#include <libgda/gda-set.h>
+#include <libgda/gda-holder.h>
+#include <libgda/gda-connection.h>
+
+typedef struct _GdaRepetitiveStatementPrivate GdaRepetitiveStatementPrivate;
+struct _GdaRepetitiveStatementPrivate
+{
+	GdaStatement* statement;
+	GSList* values_sets; /* list of GdaSet pointers, objects referenced here */
+};
+
+#define GDA_REPETITIVE_STATEMENT_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDA_TYPE_REPETITIVE_STATEMENT, GdaRepetitiveStatementPrivate))
+
+enum {
+	PROP_0,
+	PROP_STATEMENT
+};
+
+G_DEFINE_TYPE (GdaRepetitiveStatement, gda_repetitive_statement, G_TYPE_OBJECT);
+
+static void
+gda_repetitive_statement_init (GdaRepetitiveStatement *object)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (object);
+	
+	priv->statement = NULL;
+	priv->values_sets = NULL;
+}
+
+static void
+gda_repetitive_statement_finalize (GObject *object)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (object);
+	
+	g_object_unref (priv->statement);
+	g_slist_foreach (priv->values_sets, (GFunc) g_object_unref, NULL);
+	g_slist_free (priv->values_sets);
+
+	G_OBJECT_CLASS (gda_repetitive_statement_parent_class)->finalize (object);
+}
+
+static void
+gda_repetitive_statement_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	
+	g_return_if_fail (GDA_IS_REPETITIVE_STATEMENT (object));
+	
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (object);
+	switch (prop_id) {
+	case PROP_STATEMENT:
+		priv->statement = g_value_dup_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gda_repetitive_statement_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	
+	g_return_if_fail (GDA_IS_REPETITIVE_STATEMENT (object));
+
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (object);
+	switch (prop_id) {
+	case PROP_STATEMENT:
+		g_value_set_object (value, priv->statement);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gda_repetitive_statement_class_init (GdaRepetitiveStatementClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GdaRepetitiveStatementPrivate));
+
+	object_class->finalize = gda_repetitive_statement_finalize;
+	object_class->set_property = gda_repetitive_statement_set_property;
+	object_class->get_property = gda_repetitive_statement_get_property;
+
+	g_object_class_install_property (object_class,
+	                                 PROP_STATEMENT,
+	                                 g_param_spec_object ("statement",
+	                                                      "stmt",
+	                                                      "Statement to Execute",
+	                                                      GDA_TYPE_STATEMENT,
+	                                                      G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+/**
+ * gda_repetitive_statement_new
+ * @stmt: a #GdaStatement object
+ *
+ * Creates a new #GdaRepetitiveStatement object which, when executed, will execute @stmt once for all
+ * the values set which will have been defined using gda_repetitive_statement_append_set().
+ * Use gda_connection_repetitive_statement_execute() to actually execute it.
+ *
+ * Returns: a new #GdaRepetitiveStatement object
+ *
+ * Since: 4.2
+ */
+GdaRepetitiveStatement*
+gda_repetitive_statement_new (GdaStatement *stmt)
+{
+	GdaRepetitiveStatement *rstmt;
+	
+	rstmt = GDA_REPETITIVE_STATEMENT (g_object_new (GDA_TYPE_REPETITIVE_STATEMENT,
+							"statement", stmt, NULL));
+	
+	return rstmt;
+}
+
+/**
+ * gda_repetitive_statement_get_template_set
+ * @rstmt: a #GdaRepetitiveStatement object
+ * @set: a place to store the returned template set
+ * @error: a place to store error, or %NULL
+ *
+ * Gets a new #GdaSet object with the parameters used by the template statement in the
+ * @rstmt object. 
+ *
+ * Use this object with gda_repetitive_statement_append_set().
+ *
+ * Returns: TRUE on success, FALSE on error
+ *
+ * Since: 4.2
+ */
+gboolean
+gda_repetitive_statement_get_template_set (GdaRepetitiveStatement *rstmt, GdaSet **set, GError **error)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (rstmt);
+	
+	return gda_statement_get_parameters (priv->statement, set, error);
+}
+
+/**
+ * gda_repetitive_statement_append_set
+ * @rstmt: a #GdaRepetitiveStatement object
+ * @values: a #GdaSet object with the values to be used
+ * @make_copy: %TRUE if @values is copied, and %FALSE if @values is only ref'ed
+ *
+ * Specifies that @rstmt be executed one time with the values contained in @values. 
+ *
+ * A new #GdaSet to be used as the @values argument can be obtained using
+ * gda_repetitive_statement_get_template_set().
+ *
+ * Returns: a new #GdaRepetitiveStatement object
+ *
+ * Since: 4.2
+ */
+gboolean
+gda_repetitive_statement_append_set (GdaRepetitiveStatement *rstmt, GdaSet *values, gboolean make_copy)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	GdaSet *set;
+
+	g_return_val_if_fail (GDA_IS_REPETITIVE_STATEMENT(rstmt), FALSE);
+	g_return_val_if_fail (GDA_IS_SET (values), FALSE);
+	
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (rstmt);
+	
+	if (make_copy)
+		set = gda_set_copy (values);
+	else
+		set = g_object_ref (values);
+	priv->values_sets = g_slist_prepend (priv->values_sets, set);
+	
+	return TRUE;
+}
+
+/**
+ * gda_repetitive_statement_get_all_sets
+ * @rstmt: a #GdaRepetitiveStatement object
+ *
+ * Get all the values sets which will have been added using gda_repetitive_statement_append_set().
+ *
+ * Returns: a new #GSList of #GdaSet objects (free with g_slist_free()).
+ *
+ * Since: 4.2
+ */
+GSList*
+gda_repetitive_statement_get_all_sets (GdaRepetitiveStatement *rstmt)
+{
+	GdaRepetitiveStatementPrivate *priv;
+	
+	priv = GDA_REPETITIVE_STATEMENT_PRIVATE (rstmt);
+	
+	return g_slist_reverse (priv->values_sets);
+}
diff --git a/libgda/gda-repetitive-statement.h b/libgda/gda-repetitive-statement.h
new file mode 100644
index 0000000..355362f
--- /dev/null
+++ b/libgda/gda-repetitive-statement.h
@@ -0,0 +1,55 @@
+/* GDA library
+ * 
+ * Copyright (C) Daniel Espinosa Ortiz 2008 <esodan gmail com>
+ * 
+ * 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_REPETITIVE_STATEMENT_H_
+#define _GDA_REPETITIVE_STATEMENT_H_
+
+#include <glib-object.h>
+#include <libgda/gda-decl.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_REPETITIVE_STATEMENT             (gda_repetitive_statement_get_type ())
+#define GDA_REPETITIVE_STATEMENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDA_TYPE_REPETITIVE_STATEMENT, GdaRepetitiveStatement))
+#define GDA_REPETITIVE_STATEMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GDA_TYPE_REPETITIVE_STATEMENT, GdaRepetitiveStatementClass))
+#define GDA_IS_REPETITIVE_STATEMENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDA_TYPE_REPETITIVE_STATEMENT))
+#define GDA_IS_REPETITIVE_STATEMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_REPETITIVE_STATEMENT))
+#define GDA_REPETITIVE_STATEMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GDA_TYPE_REPETITIVE_STATEMENT, GdaRepetitiveStatementClass))
+
+struct _GdaRepetitiveStatementClass
+{
+	GObjectClass parent_class;
+};
+
+struct _GdaRepetitiveStatement
+{
+	GObject parent_instance;
+};
+
+GType                   gda_repetitive_statement_get_type         (void) G_GNUC_CONST;
+
+GdaRepetitiveStatement* gda_repetitive_statement_new              (GdaStatement *stmt);
+gboolean                gda_repetitive_statement_get_template_set (GdaRepetitiveStatement *rstmt, GdaSet **set, GError **error);
+GSList                 *gda_repetitive_statement_get_all_sets     (GdaRepetitiveStatement *rstmt);
+gboolean                gda_repetitive_statement_append_set       (GdaRepetitiveStatement *rstmt, GdaSet *values, gboolean make_copy);
+
+G_END_DECLS
+
+#endif /* _GDA_REPETITIVE_STATEMENT_H_ */
diff --git a/libgda/gda-server-provider.c b/libgda/gda-server-provider.c
index aa0cb17..424ab4b 100644
--- a/libgda/gda-server-provider.c
+++ b/libgda/gda-server-provider.c
@@ -267,7 +267,7 @@ gda_server_provider_get_version (GdaServerProvider *provider)
 }
 
 /**
- * gda_server_provider_get_info
+ * gda_server_provider_get_name
  * @provider: a #GdaServerProvider object.
  *
  * Get the name (identifier) of the provider
diff --git a/libgda/gda-sql-builder.c b/libgda/gda-sql-builder.c
index 21eebc1..8f4a278 100644
--- a/libgda/gda-sql-builder.c
+++ b/libgda/gda-sql-builder.c
@@ -48,7 +48,6 @@ static GStaticRecMutex init_mutex = G_STATIC_REC_MUTEX_INIT;
 
 typedef struct {
 	GdaSqlAnyPart *part;
-	gboolean       to_free; /* TRUE if @part is not included in a GdaSqlStatement, and needs to be freed */
 }  SqlPart;
 
 struct _GdaSqlBuilderPrivate {
@@ -124,9 +123,14 @@ gda_sql_builder_class_init (GdaSqlBuilderClass *klass)
 static void
 any_part_free (SqlPart *part)
 {
-	if (part->to_free) {
+	switch (part->part->type) {
+	case GDA_SQL_ANY_EXPR:
+		gda_sql_expr_free ((GdaSqlExpr*) part->part);
+		break;
+	default:
 		TO_IMPLEMENT;
 	}
+
 	g_free (part);
 }
 
@@ -250,7 +254,6 @@ add_part (GdaSqlBuilder *builder, guint id, GdaSqlAnyPart *part)
 	*realid = id;
 	p = g_new0 (SqlPart, 1);
 	p->part = part;
-	p->to_free = TRUE; /* p->part not used yet in GdaSqlStatement */
 	g_hash_table_insert (builder->priv->parts_hash, realid, p);
 	return id;
 }
@@ -280,27 +283,19 @@ use_part (SqlPart *p, GdaSqlAnyPart *parent)
 	if (!p)
 		return NULL;
 
-	if (p->to_free) {
-		/* take ownership */
-		p->to_free = FALSE;
-		p->part->parent = parent;
-		return p->part;
-	}
-	else {
-		/* copy */
-		GdaSqlAnyPart *anyp = NULL;
-		switch (p->part->type) {
-		case GDA_SQL_ANY_EXPR:
-			anyp = (GdaSqlAnyPart*) gda_sql_expr_copy ((GdaSqlExpr*) p->part);
-			break;
-		default:
-			TO_IMPLEMENT;
-			return NULL;
-		}
-		if (anyp)
-			anyp->parent = parent;
-		return anyp;
+	/* copy */
+	GdaSqlAnyPart *anyp = NULL;
+	switch (p->part->type) {
+	case GDA_SQL_ANY_EXPR:
+		anyp = (GdaSqlAnyPart*) gda_sql_expr_copy ((GdaSqlExpr*) p->part);
+		break;
+	default:
+		TO_IMPLEMENT;
+		return NULL;
 	}
+	if (anyp)
+		anyp->parent = parent;
+	return anyp;
 }
 
 /**
@@ -469,52 +464,88 @@ gda_sql_builder_set_where (GdaSqlBuilder *builder, guint cond_id)
 
 
 /**
- * gda_sql_builder_add_field_value
+ * gda_sql_builder_add_field
  * @builder: a #GdaSqlBuilder object
- * @field_name: a field name
+ * @field_id: the ID of the field's name or definition
  * @value_id: the ID of the value to set the field to
  *
- * Valid only for: INSERT, UPDATE statements
+ * Valid only for: INSERT, UPDATE, SELECT statements
  *
- * Specifies that the field named @field_name will be set to the value identified by @value_id.
+ * For INSERT and UPDATE: specifies that the field named @field_name will be set to the value identified by @value_id.
+ * For SELECT: add a selected item to the statement, and if @value_id is not %0, then use it as an alias
  *
  * Since: 4.2
  */
 void
-gda_sql_builder_add_field_value (GdaSqlBuilder *builder, const gchar *field_name, guint value_id)
+gda_sql_builder_add_field (GdaSqlBuilder *builder, guint field_id, guint value_id)
 {
 	g_return_if_fail (GDA_IS_SQL_BUILDER (builder));
 	g_return_if_fail (builder->priv->main_stmt);
-	g_return_if_fail (field_name && *field_name);
-	g_return_if_fail (value_id > 0);
 
-	SqlPart *p;
-	p = get_part (builder, value_id, GDA_SQL_ANY_EXPR);
-	if (!p)
+	SqlPart *value_part, *field_part;
+	GdaSqlExpr *field_expr;
+	value_part = get_part (builder, value_id, GDA_SQL_ANY_EXPR);
+	field_part = get_part (builder, field_id, GDA_SQL_ANY_EXPR);
+	if (!field_part)
 		return;
+	field_expr = (GdaSqlExpr*) (field_part->part);
 
-	GdaSqlField *field = gda_sql_field_new (GDA_SQL_ANY_PART (builder->priv->main_stmt->contents));
-	field->field_name = g_strdup (field_name);
+	/* checks */
+	switch (builder->priv->main_stmt->stmt_type) {
+	case GDA_SQL_STATEMENT_UPDATE:
+	case GDA_SQL_STATEMENT_INSERT:
+		if (!value_part)
+			return;
+		if (!field_expr->value || (G_VALUE_TYPE (field_expr->value) !=  G_TYPE_STRING)) {
+			g_warning (_("Wrong field format"));
+			return;
+		}
+		break;
+	case GDA_SQL_STATEMENT_SELECT:
+		/* no specific check */
+		break;
+	default:
+		g_warning (_("Wrong statement type"));
+		return;
+	}
 
+	/* work */
 	switch (builder->priv->main_stmt->stmt_type) {
 	case GDA_SQL_STATEMENT_UPDATE: {
 		GdaSqlStatementUpdate *upd = (GdaSqlStatementUpdate*) builder->priv->main_stmt->contents;
+		GdaSqlField *field = gda_sql_field_new (GDA_SQL_ANY_PART (upd));
+		field->field_name = g_value_dup_string (field_expr->value);
 		upd->fields_list = g_slist_append (upd->fields_list, field);
-		upd->expr_list = g_slist_append (upd->expr_list, use_part (p, GDA_SQL_ANY_PART (upd)));
-
+		upd->expr_list = g_slist_append (upd->expr_list, use_part (value_part, GDA_SQL_ANY_PART (upd)));
 		break;
 	}
 	case GDA_SQL_STATEMENT_INSERT:{
 		GdaSqlStatementInsert *ins = (GdaSqlStatementInsert*) builder->priv->main_stmt->contents;
-		ins->fields_list = g_slist_append (ins->fields_list, field);
+		GdaSqlField *field = gda_sql_field_new (GDA_SQL_ANY_PART (ins));
+		field->field_name = g_value_dup_string (field_expr->value);
 
+		ins->fields_list = g_slist_append (ins->fields_list, field);
 		
 		if (! ins->values_list)
 			ins->values_list = g_slist_append (NULL,
-							   g_slist_append (NULL, use_part (p, GDA_SQL_ANY_PART (ins))));
+							   g_slist_append (NULL,
+							   use_part (value_part, GDA_SQL_ANY_PART (ins))));
 		else
 			ins->values_list->data = g_slist_append ((GSList*) ins->values_list->data, 
-								 use_part (p, GDA_SQL_ANY_PART (ins)));
+								 use_part (value_part, GDA_SQL_ANY_PART (ins)));
+		break;
+	}
+	case GDA_SQL_STATEMENT_SELECT: {
+		GdaSqlStatementSelect *sel = (GdaSqlStatementSelect*) builder->priv->main_stmt->contents;
+		GdaSqlSelectField *field;
+		field = gda_sql_select_field_new (GDA_SQL_ANY_PART (sel));
+		field->expr = (GdaSqlExpr*) use_part (field_part, GDA_SQL_ANY_PART (field));
+		if (value_part) {
+			GdaSqlExpr *value_expr = (GdaSqlExpr*) (value_part->part);
+			if (G_VALUE_TYPE (value_expr->value) ==  G_TYPE_STRING)
+				field->as = g_value_dup_string (value_expr->value);
+		}
+		sel->expr_list = g_slist_append (sel->expr_list, field);
 		break;
 	}
 	default:
@@ -685,30 +716,23 @@ gda_sql_builder_param (GdaSqlBuilder *builder, guint id, const gchar *param_name
 	return add_part (builder, id, (GdaSqlAnyPart *) expr);
 }
 
-guint
-gda_sql_builder_cond1 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1)
-{
-	TO_IMPLEMENT;
-	return 0;
-}
-
-
 /**
- * gda_sql_builder_cond2
+ * gda_sql_builder_cond
  * @builder: a #GdaSqlBuilder object
  * @id: the requested ID, or 0 if to be determined by @builder
  * @op: type of condition
  * @op1: the ID of the 1st argument (not 0)
- * @op2: the ID of the 2nd argument (not 0)
+ * @op2: the ID of the 2nd argument (may be %0 if @op needs only one operand)
+ * @op3: the ID of the 3rd argument (may be %0 if @op needs only one or two operand)
  *
- * Builds a new expression which reprenents a condition (or operation) with two operands.
+ * Builds a new expression which reprenents a condition (or operation).
  *
  * Returns: the ID of the new expression, or 0 if there was an error
  *
  * Since: 4.2
  */
 guint
-gda_sql_builder_cond2 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1, guint op2)
+gda_sql_builder_cond (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1, guint op2, guint op3)
 {
 	g_return_val_if_fail (GDA_IS_SQL_BUILDER (builder), 0);
 	g_return_val_if_fail (builder->priv->main_stmt, 0);
@@ -718,23 +742,211 @@ gda_sql_builder_cond2 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op,
 	if (!p1)
 		return 0;
 	p2 = get_part (builder, op2, GDA_SQL_ANY_EXPR);
-	if (!p2)
-		return 0;
 	
 	GdaSqlExpr *expr;
 	expr = gda_sql_expr_new (NULL);
 	expr->cond = gda_sql_operation_new (GDA_SQL_ANY_PART (expr));
 	expr->cond->operator_type = op;
 	expr->cond->operands = g_slist_append (NULL, use_part (p1, GDA_SQL_ANY_PART (expr->cond)));
-	expr->cond->operands = g_slist_append (expr->cond->operands, use_part (p2, GDA_SQL_ANY_PART (expr->cond)));
+	if (p2) {
+		SqlPart *p3;
+		expr->cond->operands = g_slist_append (expr->cond->operands,
+						       use_part (p2, GDA_SQL_ANY_PART (expr->cond)));
+		p3 = get_part (builder, op3, GDA_SQL_ANY_EXPR);
+		if (p3)
+			expr->cond->operands = g_slist_append (expr->cond->operands,
+							       use_part (p3, GDA_SQL_ANY_PART (expr->cond)));	
+	}
 
 	return add_part (builder, id, (GdaSqlAnyPart *) expr);
 }
 
+typedef struct {
+	GdaSqlSelectTarget target; /* inheritance! */
+	guint part_id; /* copied from this part ID */
+} BuildTarget;
+
+/**
+ * gda_sql_builder_select_add_target
+ * @builder: a #GdaSqlBuilder object
+ * @id: the requested ID, or 0 if to be determined by @builder
+ * @table_id: the ID of the expression holding a table reference (not %0)
+ * @alias: the alias to give to the target, or %NULL
+ *
+ * Adds a new target to a SELECT statement
+ *
+ * Returns: the ID of the new target, or 0 if there was an error
+ *
+ * Since: 4.2
+ */
+guint
+gda_sql_builder_select_add_target (GdaSqlBuilder *builder, guint id, guint table_id, const gchar *alias)
+{
+	g_return_val_if_fail (GDA_IS_SQL_BUILDER (builder), 0);
+	g_return_val_if_fail (builder->priv->main_stmt, 0);
+	
+	if (builder->priv->main_stmt->stmt_type != GDA_SQL_STATEMENT_SELECT) {
+		g_warning (_("Wrong statement type"));
+		return 0;
+	}
+
+	SqlPart *p;
+	p = get_part (builder, table_id, GDA_SQL_ANY_EXPR);
+	if (!p)
+		return 0;
+	
+	BuildTarget *btarget;
+	GdaSqlStatementSelect *sel = (GdaSqlStatementSelect*) builder->priv->main_stmt->contents;
+	btarget = g_new0 (BuildTarget, 1);
+        GDA_SQL_ANY_PART(btarget)->type = GDA_SQL_ANY_SQL_SELECT_TARGET;
+        GDA_SQL_ANY_PART(btarget)->parent = GDA_SQL_ANY_PART (sel->from);
+	if (id)
+		btarget->part_id = id;
+	else
+		btarget->part_id = builder->priv->next_assigned_id --;
+	
+	((GdaSqlSelectTarget*) btarget)->expr = (GdaSqlExpr*) use_part (p, GDA_SQL_ANY_PART (btarget));
+	if (alias) 
+		((GdaSqlSelectTarget*) btarget)->as = g_strdup (alias);
+
+	/* add target to sel->from. NOTE: @btarget is NOT added to the "repository" or GdaSqlAnyPart parts
+	* like others */
+	if (!sel->from)
+		sel->from = gda_sql_select_from_new (GDA_SQL_ANY_PART (sel));
+	sel->from->targets = g_slist_append (sel->from->targets, btarget);
+
+	return btarget->part_id;
+}
+
+typedef struct {
+	GdaSqlSelectJoin join; /* inheritance! */
+	guint part_id; /* copied from this part ID */
+} BuilderJoin;
 
+/**
+ * gda_sql_builder_select_join_targets
+ * @builder: a #GdaSqlBuilder object
+ * @id: the requested ID, or 0 if to be determined by @builder
+ * @left_target_id: the ID of the left target to use (not %0)
+ * @right_target_id: the ID of the right target to use (not %0)
+ * @join_type: the type of join
+ * @join_expr: joining expression's ID, or %0
+ *
+ * Joins two targets in a SELECT statement
+ *
+ * Returns: the ID of the new join, or 0 if there was an error
+ *
+ * Since: 4.2
+ */
 guint
-gda_sql_builder_cond3 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1, guint op2, guint op3)
+gda_sql_builder_select_join_targets (GdaSqlBuilder *builder, guint id,
+				     guint left_target_id, guint right_target_id,
+				     GdaSqlSelectJoinType join_type,
+				     guint join_expr)
 {
-	TO_IMPLEMENT;
-	return 0;
+	g_return_val_if_fail (GDA_IS_SQL_BUILDER (builder), 0);
+	g_return_val_if_fail (builder->priv->main_stmt, 0);
+	
+	if (builder->priv->main_stmt->stmt_type != GDA_SQL_STATEMENT_SELECT) {
+		g_warning (_("Wrong statement type"));
+		return 0;
+	}
+
+	/* determine join position */
+	GdaSqlStatementSelect *sel = (GdaSqlStatementSelect*) builder->priv->main_stmt->contents;
+	GSList *list;
+	gint left_pos = -1, right_pos = -1;
+	gint pos;
+	for (pos = 0, list = sel->from ? sel->from->targets : NULL;
+	     list;
+	     pos++, list = list->next) {
+		BuildTarget *btarget = (BuildTarget*) list->data;
+		if (btarget->part_id == left_target_id)
+			left_pos = pos;
+		else if (btarget->part_id == right_target_id)
+			right_pos = pos;
+		if ((left_pos != -1) && (right_pos != -1))
+			break;
+	}
+	if ((left_pos == -1) || (right_pos == -1)) {
+		g_warning (_("Unknown part ID %u"), (left_pos == -1) ? left_pos : right_pos);
+		return 0;
+	}
+	
+	if (left_pos > right_pos) {
+		TO_IMPLEMENT;
+	}
+	
+	/* create join */
+	BuilderJoin *bjoin;
+	GdaSqlSelectJoin *join;
+
+	bjoin = g_new0 (BuilderJoin, 1);
+	GDA_SQL_ANY_PART(bjoin)->type = GDA_SQL_ANY_SQL_SELECT_JOIN;
+        GDA_SQL_ANY_PART(bjoin)->parent = GDA_SQL_ANY_PART (sel->from);
+	if (id)
+		bjoin->part_id = id;
+	else
+		bjoin->part_id = builder->priv->next_assigned_id --;
+	join = (GdaSqlSelectJoin*) bjoin;
+	join->type = join_type;
+	join->position = right_pos;
+	
+	SqlPart *ep;
+	ep = get_part (builder, join_expr, GDA_SQL_ANY_EXPR);
+	if (ep)
+		join->expr = (GdaSqlExpr*) use_part (ep, GDA_SQL_ANY_PART (join));
+
+	sel->from->joins = g_slist_append (sel->from->joins, bjoin);
+
+	return bjoin->part_id;
+}
+
+/**
+ * gda_sql_builder_join_add_field
+ * @builder: a #GdaSqlBuilder object
+ * @join_id: the ID of the join to modify (not %0)
+ * @field_name: the name of the field to use in the join condition (not %NULL)
+ *
+ * Alter a joins in a SELECT statement to make its condition on the field which name
+ * is @field_name
+ *
+ * Returns: the ID of the new join, or 0 if there was an error
+ *
+ * Since: 4.2
+ */
+void
+gda_sql_builder_join_add_field (GdaSqlBuilder *builder, guint join_id, const gchar *field_name)
+{
+	g_return_if_fail (GDA_IS_SQL_BUILDER (builder));
+	g_return_if_fail (builder->priv->main_stmt);
+	g_return_if_fail (field_name);
+	
+	if (builder->priv->main_stmt->stmt_type != GDA_SQL_STATEMENT_SELECT) {
+		g_warning (_("Wrong statement type"));
+		return;
+	}
+
+	/* determine join */
+	GdaSqlStatementSelect *sel = (GdaSqlStatementSelect*) builder->priv->main_stmt->contents;
+	GdaSqlSelectJoin *join = NULL;
+	GSList *list;
+	for (list = sel->from ? sel->from->joins : NULL;
+	     list;
+	     list = list->next) {
+		BuilderJoin *bjoin = (BuilderJoin*) list->data;
+		if (bjoin->part_id == join_id) {
+			join = (GdaSqlSelectJoin*) bjoin;
+			break;
+		}
+	}
+	if (!join) {
+		g_warning (_("Unknown part ID %u"), join_id);
+		return;
+	}
+
+	GdaSqlField *field;
+	field = gda_sql_field_new (GDA_SQL_ANY_PART (join));
+	field->field_name = g_strdup (field_name);
+	join->use = g_slist_append (join->use, field);
 }
diff --git a/libgda/gda-sql-builder.h b/libgda/gda-sql-builder.h
index ce3cf2e..c3cc362 100644
--- a/libgda/gda-sql-builder.h
+++ b/libgda/gda-sql-builder.h
@@ -74,15 +74,27 @@ guint              gda_sql_builder_literal (GdaSqlBuilder *builder, guint id, co
 guint              gda_sql_builder_expr (GdaSqlBuilder *builder, guint id, GdaDataHandler *dh, GType type, ...);
 guint              gda_sql_builder_param (GdaSqlBuilder *builder, guint id, const gchar *param_name, GType type, gboolean nullok);
 
-guint              gda_sql_builder_cond1 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1);
-guint              gda_sql_builder_cond2 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1, guint op2);
-guint              gda_sql_builder_cond3 (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op, guint op1, guint op2, guint op3);
+guint              gda_sql_builder_cond (GdaSqlBuilder *builder, guint id, GdaSqlOperatorType op,
+					 guint op1, guint op2, guint op3);
+
+
+/* SELECT Statement API */
+guint              gda_sql_builder_select_add_target (GdaSqlBuilder *builder, guint id,
+						      guint table_id, const gchar *alias);
+guint              gda_sql_builder_select_join_targets (GdaSqlBuilder *builder, guint id,
+							guint left_target_id, guint right_target_id,
+							GdaSqlSelectJoinType join_type,
+							guint join_expr);
+void               gda_sql_builder_join_add_field (GdaSqlBuilder *builder, guint join_id, const gchar *field_name);
+
 
 /* General Statement API */
 void              gda_sql_builder_set_table (GdaSqlBuilder *builder, const gchar *table_name);
 void              gda_sql_builder_set_where (GdaSqlBuilder *builder, guint cond_id);
 
-void              gda_sql_builder_add_field_value (GdaSqlBuilder *builder, const gchar *field_name, guint value_id);
+void              gda_sql_builder_add_field (GdaSqlBuilder *builder, guint field_id, guint value_id);
+
+
 
 G_END_DECLS
 
diff --git a/libgda/handlers/gda-handler-time.c b/libgda/handlers/gda-handler-time.c
index f56c336..7a4bbeb 100644
--- a/libgda/handlers/gda-handler-time.c
+++ b/libgda/handlers/gda-handler-time.c
@@ -273,6 +273,7 @@ handler_compute_locale (GdaHandlerTime *hdl)
 
 	date = g_date_new_dmy (4, 7, 1976); /* Same date used by GLib */
 	g_date_strftime (buf, 127, "%x", date);
+	g_date_free (date);
 
 	/* 1st number */
 	ptr = buf;
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 6b35168..9b51887 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -148,6 +148,7 @@
 	gda_connection_open_from_string
 	gda_connection_options_get_type
 	gda_connection_perform_operation
+	gda_connection_repetitive_statement_execute
 	gda_connection_rollback_savepoint
 	gda_connection_rollback_transaction
 	gda_connection_schema_get_type
@@ -466,6 +467,11 @@
 	gda_quark_list_new
 	gda_quark_list_new_from_string
 	gda_quark_list_remove
+	gda_repetitive_statement_append_set
+	gda_repetitive_statement_get_all_sets
+	gda_repetitive_statement_get_template_set
+	gda_repetitive_statement_get_type
+	gda_repetitive_statement_new
 	gda_rfc1738_decode
 	gda_rfc1738_encode
 	gda_row_get_length
diff --git a/libgda/sql-parser/gda-statement-struct-parts.h b/libgda/sql-parser/gda-statement-struct-parts.h
index c18a624..406c8c7 100644
--- a/libgda/sql-parser/gda-statement-struct-parts.h
+++ b/libgda/sql-parser/gda-statement-struct-parts.h
@@ -286,7 +286,7 @@ struct _GdaSqlSelectJoin
 	GdaSqlSelectJoinType  type;
 	gint                  position; /* between a target at (pos < @position) and the one @position */
 	GdaSqlExpr           *expr;
-	GSList               *use;
+	GSList               *use; /* list of GdaSqlField pointers */
 
 	/* Padding for future expansion */
 	gpointer         _gda_reserved1;
diff --git a/libgda/thread-wrapper/Makefile.am b/libgda/thread-wrapper/Makefile.am
index f40aa0b..6ca6725 100644
--- a/libgda/thread-wrapper/Makefile.am
+++ b/libgda/thread-wrapper/Makefile.am
@@ -15,6 +15,8 @@ libgda_threadwrapper_4_0_la_SOURCES = \
 	$(libgda_threadwrapper_headers) \
 	gda-thread-blob-op.h \
 	gda-thread-blob-op.c \
+	gda-thread-meta.h \
+	gda-thread-meta.c \
 	gda-thread-provider.h \
 	gda-thread-provider.c \
 	gda-thread-recordset.h \
diff --git a/libgda/thread-wrapper/gda-thread-meta.c b/libgda/thread-wrapper/gda-thread-meta.c
new file mode 100644
index 0000000..8e0db09
--- /dev/null
+++ b/libgda/thread-wrapper/gda-thread-meta.c
@@ -0,0 +1,795 @@
+/* GDA capi provider
+ * 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 <string.h>
+#include "gda-thread-meta.h"
+#include "gda-thread-provider.h"
+#include <libgda/gda-meta-store.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/gda-connection-private.h>
+#include <libgda/gda-connection-internal.h>
+
+#define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
+
+/* common implementation of the functions used when no parameter is passed */
+typedef struct {
+	GdaServerProvider *prov;
+	GdaConnection *cnc;
+	GdaMetaStore *store;
+	GdaMetaContext *context;
+} BasicThreadData;
+
+#define main_thread_basic_core(func,prov,cnc,store,context,error) \
+	ThreadConnectionData *cdata; \
+	BasicThreadData wdata; \
+	gpointer res; \
+	guint jid; \
+        cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data ((cnc)); \
+	if (!cdata) \
+		return FALSE; \
+	wdata.prov = cdata->cnc_provider; \
+	wdata.cnc = cdata->sub_connection; \
+        wdata.store = (store); \
+        wdata.context = (context); \
+	jid = gda_thread_wrapper_execute (cdata->wrapper, \
+					  (GdaThreadWrapperFunc) (func), &wdata, NULL, NULL); \
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, (error)); \
+	return GPOINTER_TO_INT (res) ? TRUE : FALSE
+
+#define sub_thread_basic_core(func,name) \
+	gboolean retval; \
+	if (! (func)) {		  \
+	        WARN_METHOD_NOT_IMPLEMENTED (data->prov, (name)); \
+		return GINT_TO_POINTER (0); \
+	} \
+	retval = (func) (data->prov, data->cnc, data->store, data->context, error); \
+	/*g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");*/ \
+	return GINT_TO_POINTER (retval ? 1 : 0)
+
+typedef struct {
+	GdaServerProvider *prov;
+	GdaConnection *cnc;
+	GdaMetaStore *store;
+	GdaMetaContext *context;
+	const GValue *v1;
+	const GValue *v2;
+	const GValue *v3;
+	const GValue *v4;
+} DetailledThreadData;
+
+#define main_thread_detailled_core(func,prov,cnc,store,context,arg1,arg2,arg3,arg4,error) \
+	ThreadConnectionData *cdata; \
+	DetailledThreadData wdata; \
+	gpointer res; \
+	guint jid; \
+        cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data ((cnc)); \
+	if (!cdata) \
+		return FALSE; \
+	wdata.prov = cdata->cnc_provider; \
+	wdata.cnc = cdata->sub_connection; \
+        wdata.store = (store); \
+        wdata.context = (context); \
+        wdata.v1 = (arg1); \
+        wdata.v2 = (arg2); \
+        wdata.v3 = (arg3); \
+        wdata.v4 = (arg4); \
+	jid = gda_thread_wrapper_execute (cdata->wrapper, \
+					  (GdaThreadWrapperFunc) (func), &wdata, NULL, NULL); \
+	res = gda_thread_wrapper_fetch_result (cdata->wrapper, TRUE, jid, (error)); \
+	return GPOINTER_TO_INT (res) ? TRUE : FALSE
+
+#define sub_thread_detailled1_core(func,name) \
+	gboolean retval; \
+	if (! (func)) {		  \
+	        WARN_METHOD_NOT_IMPLEMENTED (data->prov, (name)); \
+		return GINT_TO_POINTER (0); \
+	} \
+	retval = (func) (data->prov, data->cnc, data->store, data->context, error, data->v1); \
+	/*g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");*/ \
+	return GINT_TO_POINTER (retval ? 1 : 0)
+
+#define sub_thread_detailled2_core(func,name) \
+	gboolean retval; \
+	if (! (func)) {		  \
+	        WARN_METHOD_NOT_IMPLEMENTED (data->prov, (name)); \
+		return GINT_TO_POINTER (0); \
+	} \
+	retval = (func) (data->prov, data->cnc, data->store, data->context, error, data->v1, data->v2); \
+	/*g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");*/ \
+	return GINT_TO_POINTER (retval ? 1 : 0)
+
+#define sub_thread_detailled3_core(func,name) \
+	gboolean retval; \
+	if (! (func)) {		  \
+	        WARN_METHOD_NOT_IMPLEMENTED (data->prov, (name)); \
+		return GINT_TO_POINTER (0); \
+	} \
+	retval = (func) (data->prov, data->cnc, data->store, data->context, error, data->v1, data->v2, data->v3); \
+	/*g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");*/ \
+	return GINT_TO_POINTER (retval ? 1 : 0)
+
+#define sub_thread_detailled4_core(func,name) \
+	gboolean retval; \
+	if (! (func)) {		  \
+	        WARN_METHOD_NOT_IMPLEMENTED (data->prov, (name)); \
+		return GINT_TO_POINTER (0); \
+	} \
+	retval = (func) (data->prov, data->cnc, data->store, data->context, error, data->v1, data->v2, data->v3, data->v4); \
+	/*g_print ("/%s() => %s\n", __FUNCTION__, retval ? "TRUE" : "FALSE");*/ \
+	return GINT_TO_POINTER (retval ? 1 : 0)
+
+/*
+ * Meta initialization
+ */
+void
+_gda_thread_provider_meta_init (GdaServerProvider *provider)
+{
+	/* nothing to be done */
+}
+
+
+
+static gpointer
+sub_thread__gda_thread_meta__info (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._info, "_info");
+}
+
+gboolean
+_gda_thread_meta__info (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__info, prov, cnc, store, context, error);
+}
+
+
+
+static gpointer
+sub_thread__gda_thread_meta__btypes (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._btypes, "_btypes");
+}
+
+gboolean
+_gda_thread_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc, 
+			  GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__btypes, prov, cnc, store, context, error);
+}
+
+
+static gpointer
+sub_thread__gda_thread_meta__udt (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._udt, "_udt");
+}
+gboolean
+_gda_thread_meta__udt (GdaServerProvider *prov, GdaConnection *cnc, 
+		       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__udt, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_udt (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled2_core (PROV_CLASS (data->prov)->meta_funcs.udt, "_udt");
+}
+gboolean
+_gda_thread_meta_udt (GdaServerProvider *prov, GdaConnection *cnc, 
+		      GdaMetaStore *store, GdaMetaContext *context, GError **error,
+		      const GValue *udt_catalog, const GValue *udt_schema)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_udt, prov, cnc, store, context, 
+				    udt_catalog, udt_schema, NULL, NULL, error);
+}
+
+
+
+static gpointer
+sub_thread__gda_thread_meta__udt_cols (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._udt_cols, "_udt_cols");
+}
+
+gboolean
+_gda_thread_meta__udt_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__udt_cols, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_udt_cols (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.udt_cols, "udt_cols");
+}
+
+gboolean
+_gda_thread_meta_udt_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			   const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_udt_cols, prov, cnc, store, context, 
+				    udt_catalog, udt_schema, udt_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__enums (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._enums, "_enums");
+}
+
+gboolean
+_gda_thread_meta__enums (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__enums, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_enums (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.enums, "enums");
+}
+
+gboolean
+_gda_thread_meta_enums (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_enums, prov, cnc, store, context, 
+				    udt_catalog, udt_schema, udt_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__domains (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._domains, "_domains");
+}
+
+gboolean
+_gda_thread_meta__domains (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__domains, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_domains (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled2_core (PROV_CLASS (data->prov)->meta_funcs.domains, "domains");
+}
+
+gboolean
+_gda_thread_meta_domains (GdaServerProvider *prov, GdaConnection *cnc, 
+			  GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			  const GValue *domain_catalog, const GValue *domain_schema)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_domains, prov, cnc, store, context, 
+				    domain_catalog, domain_schema, NULL, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__constraints_dom (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._constraints_dom, "_constraints_dom");
+}
+
+gboolean
+_gda_thread_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc, 
+				   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__constraints_dom, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_constraints_dom (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.constraints_dom, "constraints_dom");
+}
+
+gboolean
+_gda_thread_meta_constraints_dom (GdaServerProvider *prov, GdaConnection *cnc, 
+				  GdaMetaStore *store, GdaMetaContext *context, GError **error,
+				  const GValue *domain_catalog, const GValue *domain_schema, 
+				  const GValue *domain_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_constraints_dom, prov, cnc, store, context, 
+				    domain_catalog, domain_schema, domain_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__el_types (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._el_types, "_el_types");
+}
+
+gboolean
+_gda_thread_meta__el_types (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__el_types, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_el_types (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled1_core (PROV_CLASS (data->prov)->meta_funcs.el_types, "el_types");
+}
+
+gboolean
+_gda_thread_meta_el_types (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			   const GValue *specific_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_el_types, prov, cnc, store, context, 
+				    specific_name, NULL, NULL, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__collations (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._collations, "_collations");
+}
+
+gboolean
+_gda_thread_meta__collations (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__collations, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_collations (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.collations, "collations");
+}
+
+gboolean
+_gda_thread_meta_collations (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			     const GValue *collation_catalog, const GValue *collation_schema, 
+			     const GValue *collation_name_n)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_collations, prov, cnc, store, context, 
+				    collation_catalog, collation_schema, collation_name_n, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__character_sets (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._character_sets, "_character_sets");
+}
+
+gboolean
+_gda_thread_meta__character_sets (GdaServerProvider *prov, GdaConnection *cnc, 
+				  GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__character_sets, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_character_sets (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.character_sets, "character_sets");
+}
+
+gboolean
+_gda_thread_meta_character_sets (GdaServerProvider *prov, GdaConnection *cnc, 
+				 GdaMetaStore *store, GdaMetaContext *context, GError **error,
+				 const GValue *chset_catalog, const GValue *chset_schema, 
+				 const GValue *chset_name_n)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_character_sets, prov, cnc, store, context, 
+				    chset_catalog, chset_schema, chset_name_n, NULL, error);
+}
+
+
+static gpointer
+sub_thread__gda_thread_meta__schemata (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._schemata, "_schemata");
+}
+
+gboolean
+_gda_thread_meta__schemata (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__schemata, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_schemata (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled2_core (PROV_CLASS (data->prov)->meta_funcs.schemata, "schemata");
+}
+
+gboolean
+_gda_thread_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+			   const GValue *catalog_name, const GValue *schema_name_n)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_schemata, prov, cnc, store, context, 
+				    catalog_name, schema_name_n, NULL, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__tables_views (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._tables_views, "_tables_views");
+}
+
+gboolean
+_gda_thread_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__tables_views, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_tables_views (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.tables_views, "tables_views");
+}
+
+gboolean
+_gda_thread_meta_tables_views (GdaServerProvider *prov, GdaConnection *cnc, 
+			       GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			       const GValue *table_catalog, const GValue *table_schema, 
+			       const GValue *table_name_n)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_tables_views, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name_n, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__columns (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._columns, "_columns");
+}
+
+gboolean
+_gda_thread_meta__columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__columns, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_columns (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.columns, "columns");
+}
+
+gboolean
+_gda_thread_meta_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			  GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			  const GValue *table_catalog, const GValue *table_schema, 
+			  const GValue *table_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_columns, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__view_cols (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._view_cols, "_view_cols");
+}
+
+gboolean
+_gda_thread_meta__view_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__view_cols, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_view_cols (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.view_cols, "view_cols");
+}
+
+gboolean
+_gda_thread_meta_view_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			    const GValue *view_catalog, const GValue *view_schema, 
+			    const GValue *view_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_view_cols, prov, cnc, store, context, 
+				    view_catalog, view_schema, view_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__constraints_tab (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._constraints_tab, "_constraints_tab");
+}
+
+gboolean
+_gda_thread_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
+				   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__constraints_tab, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_constraints_tab (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled4_core (PROV_CLASS (data->prov)->meta_funcs.constraints_tab, "constraints_tab");
+}
+
+gboolean
+_gda_thread_meta_constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
+				  GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+				  const GValue *table_catalog, const GValue *table_schema, 
+				  const GValue *table_name, const GValue *constraint_name_n)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_constraints_tab, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name, constraint_name_n, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__constraints_ref (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._constraints_ref, "_constraints_ref");
+}
+
+gboolean
+_gda_thread_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
+				   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__constraints_ref, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_constraints_ref (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled4_core (PROV_CLASS (data->prov)->meta_funcs.constraints_ref, "constraints_ref");
+}
+
+gboolean
+_gda_thread_meta_constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
+				  GdaMetaStore *store, GdaMetaContext *context, GError **error,
+				  const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+				  const GValue *constraint_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_constraints_ref, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name, constraint_name, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__key_columns (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._key_columns, "_key_columns");
+}
+
+gboolean
+_gda_thread_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__key_columns, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_key_columns (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled4_core (PROV_CLASS (data->prov)->meta_funcs.key_columns, "key_columns");
+}
+
+gboolean
+_gda_thread_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			      const GValue *table_catalog, const GValue *table_schema, 
+			      const GValue *table_name, const GValue *constraint_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_key_columns, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name, constraint_name, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__check_columns (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._check_columns, "_check_columns");
+}
+
+gboolean
+_gda_thread_meta__check_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+				 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__check_columns, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_check_columns (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled4_core (PROV_CLASS (data->prov)->meta_funcs.check_columns, "check_columns");
+}
+
+gboolean
+_gda_thread_meta_check_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error,
+				const GValue *table_catalog, const GValue *table_schema, 
+				const GValue *table_name, const GValue *constraint_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_check_columns, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name, constraint_name, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__triggers (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._triggers, "_triggers");
+}
+
+gboolean
+_gda_thread_meta__triggers (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__triggers, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_triggers (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.triggers, "triggers");
+}
+
+gboolean
+_gda_thread_meta_triggers (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			   const GValue *table_catalog, const GValue *table_schema, 
+			   const GValue *table_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_triggers, prov, cnc, store, context, 
+				    table_catalog, table_schema, table_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__routines (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._routines, "_routines");
+}
+
+gboolean
+_gda_thread_meta__routines (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__routines, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_routines (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.routines, "routines");
+}
+
+gboolean
+_gda_thread_meta_routines (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			   const GValue *routine_catalog, const GValue *routine_schema, 
+			   const GValue *routine_name_n)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_routines, prov, cnc, store, context, 
+				    routine_catalog, routine_schema, routine_name_n, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__routine_col (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._routine_col, "_routine_col");
+}
+
+gboolean
+_gda_thread_meta__routine_col (GdaServerProvider *prov, GdaConnection *cnc, 
+			       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__routine_col, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_routine_col (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.routine_col, "routine_col");
+}
+
+gboolean
+_gda_thread_meta_routine_col (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			      const GValue *rout_catalog, const GValue *rout_schema, 
+			      const GValue *rout_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_routine_col, prov, cnc, store, context, 
+				    rout_catalog, rout_schema, rout_name, NULL, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta__routine_par (BasicThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_basic_core (PROV_CLASS (data->prov)->meta_funcs._routine_par, "_routine_par");
+}
+
+gboolean
+_gda_thread_meta__routine_par (GdaServerProvider *prov, GdaConnection *cnc, 
+			       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	main_thread_basic_core (sub_thread__gda_thread_meta__routine_par, prov, cnc, store, context, error);
+}
+
+static gpointer
+sub_thread__gda_thread_meta_routine_par (DetailledThreadData *data, GError **error)
+{
+	/* WARNING: function executed in sub thread! */
+	sub_thread_detailled3_core (PROV_CLASS (data->prov)->meta_funcs.routine_par, "routine_par");
+}
+
+gboolean
+_gda_thread_meta_routine_par (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			      const GValue *rout_catalog, const GValue *rout_schema, 
+			      const GValue *rout_name)
+{
+	main_thread_detailled_core (sub_thread__gda_thread_meta_routine_par, prov, cnc, store, context, 
+				    rout_catalog, rout_schema, rout_name, NULL, error);
+}
diff --git a/libgda/thread-wrapper/gda-thread-meta.h b/libgda/thread-wrapper/gda-thread-meta.h
new file mode 100644
index 0000000..e911786
--- /dev/null
+++ b/libgda/thread-wrapper/gda-thread-meta.h
@@ -0,0 +1,197 @@
+/* GDA capi provider
+ * 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; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_THREAD_META_H__
+#define __GDA_THREAD_META_H__
+
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+void     _gda_thread_provider_meta_init    (GdaServerProvider *provider);
+
+/* _information_schema_catalog_name */
+gboolean _gda_thread_meta__info            (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+
+/* _builtin_data_types */
+gboolean _gda_thread_meta__btypes          (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+
+/* _udt */
+gboolean _gda_thread_meta__udt             (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_udt              (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *udt_catalog, const GValue *udt_schema);
+
+/* _udt_columns */
+gboolean _gda_thread_meta__udt_cols        (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_udt_cols         (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name);
+
+/* _enums */
+gboolean _gda_thread_meta__enums           (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_enums            (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name);
+
+/* _domains */
+gboolean _gda_thread_meta__domains         (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_domains          (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *domain_catalog, const GValue *domain_schema);
+
+/* _domain_constraints */
+gboolean _gda_thread_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_constraints_dom  (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *domain_catalog, const GValue *domain_schema, 
+					    const GValue *domain_name);
+
+/* _element_types */
+gboolean _gda_thread_meta__el_types        (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_el_types         (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *specific_name);
+
+/* _collations */
+gboolean _gda_thread_meta__collations      (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_collations       (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *collation_catalog, const GValue *collation_schema, 
+					    const GValue *collation_name_n);
+
+/* _character_sets */
+gboolean _gda_thread_meta__character_sets  (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_character_sets   (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *chset_catalog, const GValue *chset_schema, 
+					    const GValue *chset_name_n);
+
+/* _schemata */
+gboolean _gda_thread_meta__schemata        (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_schemata         (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+					    const GValue *catalog_name, const GValue *schema_name_n);
+
+/* _tables or _views */
+gboolean _gda_thread_meta__tables_views    (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_tables_views     (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *table_catalog, const GValue *table_schema, 
+					    const GValue *table_name_n);
+
+/* _columns */
+gboolean _gda_thread_meta__columns         (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_columns          (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *table_catalog, const GValue *table_schema, 
+					    const GValue *table_name);
+
+/* _view_column_usage */
+gboolean _gda_thread_meta__view_cols       (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_view_cols        (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *view_catalog, const GValue *view_schema, 
+					    const GValue *view_name);
+
+/* _table_constraints */
+gboolean _gda_thread_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_constraints_tab  (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+					    const GValue *table_catalog, const GValue *table_schema, 
+					    const GValue *table_name, const GValue *constraint_name_n);
+
+/* _referential_constraints */
+gboolean _gda_thread_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_constraints_ref  (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+					    const GValue *constraint_name);
+
+/* _key_column_usage */
+gboolean _gda_thread_meta__key_columns     (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_key_columns      (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *table_catalog, const GValue *table_schema, 
+					    const GValue *table_name, const GValue *constraint_name);
+
+/* _check_column_usage */
+gboolean _gda_thread_meta__check_columns   (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_check_columns    (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *table_catalog, const GValue *table_schema, 
+					    const GValue *table_name, const GValue *constraint_name);
+
+/* _triggers */
+gboolean _gda_thread_meta__triggers        (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_triggers         (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *table_catalog, const GValue *table_schema, 
+					    const GValue *table_name);
+
+/* _routines */
+gboolean _gda_thread_meta__routines       (GdaServerProvider *prov, GdaConnection *cnc, 
+					   GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_routines        (GdaServerProvider *prov, GdaConnection *cnc, 
+					   GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					   const GValue *routine_catalog, const GValue *routine_schema, 
+					   const GValue *routine_name_n);
+
+/* _routine_columns */
+gboolean _gda_thread_meta__routine_col     (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_routine_col      (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *rout_catalog, const GValue *rout_schema, 
+					    const GValue *rout_name);
+
+/* _parameters */
+gboolean _gda_thread_meta__routine_par     (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_thread_meta_routine_par      (GdaServerProvider *prov, GdaConnection *cnc, 
+					    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					    const GValue *rout_catalog, const GValue *rout_schema, 
+					    const GValue *rout_name);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/libgda/thread-wrapper/gda-thread-provider.c b/libgda/thread-wrapper/gda-thread-provider.c
index 40e22a7..b3c8790 100644
--- a/libgda/thread-wrapper/gda-thread-provider.c
+++ b/libgda/thread-wrapper/gda-thread-provider.c
@@ -33,11 +33,18 @@
 #include <sql-parser/gda-sql-parser.h>
 #include <libgda/gda-connection-internal.h>
 #include "gda-thread-provider.h"
+#include "gda-thread-meta.h"
 #include "gda-thread-wrapper.h"
 #include "gda-thread-recordset.h"
 
 #define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
 
+struct _GdaThreadProviderPrivate {
+	GHashTable *prov_wrappers; /* key = a provider name, value = a GdaThreadWrapper,
+				    * set only if the key provider has the limiting_thread member set,
+				    * because it means we must reuse the same GdaThreadWrapper */
+};
+
 /*
  * Per connection private data is defined as ThreadConnectionData
  */
@@ -182,6 +189,48 @@ gda_thread_provider_class_init (GdaThreadProviderClass *klass)
 	provider_class->create_connection = gda_thread_provider_create_connection;
 
 	memset (&(provider_class->meta_funcs), 0, sizeof (GdaServerProviderMeta));
+	provider_class->meta_funcs._info = _gda_thread_meta__info;
+        provider_class->meta_funcs._btypes = _gda_thread_meta__btypes;
+        provider_class->meta_funcs._udt = _gda_thread_meta__udt;
+        provider_class->meta_funcs.udt = _gda_thread_meta_udt;
+        provider_class->meta_funcs._udt_cols = _gda_thread_meta__udt_cols;
+        provider_class->meta_funcs.udt_cols = _gda_thread_meta_udt_cols;
+        provider_class->meta_funcs._enums = _gda_thread_meta__enums;
+        provider_class->meta_funcs.enums = _gda_thread_meta_enums;
+        provider_class->meta_funcs._domains = _gda_thread_meta__domains;
+        provider_class->meta_funcs.domains = _gda_thread_meta_domains;
+        provider_class->meta_funcs._constraints_dom = _gda_thread_meta__constraints_dom;
+        provider_class->meta_funcs.constraints_dom = _gda_thread_meta_constraints_dom;
+        provider_class->meta_funcs._el_types = _gda_thread_meta__el_types;
+        provider_class->meta_funcs.el_types = _gda_thread_meta_el_types;
+        provider_class->meta_funcs._collations = _gda_thread_meta__collations;
+        provider_class->meta_funcs.collations = _gda_thread_meta_collations;
+        provider_class->meta_funcs._character_sets = _gda_thread_meta__character_sets;
+        provider_class->meta_funcs.character_sets = _gda_thread_meta_character_sets;
+        provider_class->meta_funcs._schemata = _gda_thread_meta__schemata;
+        provider_class->meta_funcs.schemata = _gda_thread_meta_schemata;
+        provider_class->meta_funcs._tables_views = _gda_thread_meta__tables_views;
+        provider_class->meta_funcs.tables_views = _gda_thread_meta_tables_views;
+        provider_class->meta_funcs._columns = _gda_thread_meta__columns;
+        provider_class->meta_funcs.columns = _gda_thread_meta_columns;
+        provider_class->meta_funcs._view_cols = _gda_thread_meta__view_cols;
+        provider_class->meta_funcs.view_cols = _gda_thread_meta_view_cols;
+        provider_class->meta_funcs._constraints_tab = _gda_thread_meta__constraints_tab;
+        provider_class->meta_funcs.constraints_tab = _gda_thread_meta_constraints_tab;
+        provider_class->meta_funcs._constraints_ref = _gda_thread_meta__constraints_ref;
+        provider_class->meta_funcs.constraints_ref = _gda_thread_meta_constraints_ref;
+        provider_class->meta_funcs._key_columns = _gda_thread_meta__key_columns;
+        provider_class->meta_funcs.key_columns = _gda_thread_meta_key_columns;
+        provider_class->meta_funcs._check_columns = _gda_thread_meta__check_columns;
+        provider_class->meta_funcs.check_columns = _gda_thread_meta_check_columns;
+        provider_class->meta_funcs._triggers = _gda_thread_meta__triggers;
+        provider_class->meta_funcs.triggers = _gda_thread_meta_triggers;
+        provider_class->meta_funcs._routines = _gda_thread_meta__routines;
+        provider_class->meta_funcs.routines = _gda_thread_meta_routines;
+        provider_class->meta_funcs._routine_col = _gda_thread_meta__routine_col;
+        provider_class->meta_funcs.routine_col = _gda_thread_meta_routine_col;
+        provider_class->meta_funcs._routine_par = _gda_thread_meta__routine_par;
+        provider_class->meta_funcs.routine_par = _gda_thread_meta_routine_par;
 
 	/* distributed transactions: if not supported, then provider_class->xa_funcs should be set to NULL */
 	provider_class->xa_funcs = g_new0 (GdaServerProviderXa, 1);
@@ -191,11 +240,16 @@ gda_thread_provider_class_init (GdaThreadProviderClass *klass)
 	provider_class->xa_funcs->xa_commit = gda_thread_provider_xa_commit;
 	provider_class->xa_funcs->xa_rollback = gda_thread_provider_xa_rollback;
 	provider_class->xa_funcs->xa_recover = gda_thread_provider_xa_recover;
+
+	provider_class->limiting_thread = NULL;
 }
 
 static void
 gda_thread_provider_init (GdaThreadProvider *thread_prv, GdaThreadProviderClass *klass)
 {
+	thread_prv->priv = g_new0 (GdaThreadProviderPrivate, 1);
+	thread_prv->priv->prov_wrappers = g_hash_table_new_full (g_str_hash, g_str_equal,
+								 g_free, g_object_unref);
 }
 
 GType
@@ -297,6 +351,9 @@ gda_thread_provider_open_connection (GdaServerProvider *provider, GdaConnection
 				     GdaQuarkList *params, GdaQuarkList *auth,
 				     guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data)
 {
+	GdaThreadWrapper *wr = NULL;
+	gboolean wr_created = FALSE;
+
 	g_return_val_if_fail (GDA_IS_THREAD_PROVIDER (provider), FALSE);
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 
@@ -319,22 +376,41 @@ gda_thread_provider_open_connection (GdaServerProvider *provider, GdaConnection
 	if (dsn) {
 		data = g_new0 (OpenConnectionData, 1);
 		data->dsn = dsn;
+		
+		GdaDsnInfo *dsninfo;
+		dsninfo = gda_config_get_dsn_info (dsn);
+		if (dsninfo)
+			wr = g_hash_table_lookup (GDA_THREAD_PROVIDER (provider)->priv->prov_wrappers,
+						  dsninfo->provider);
 	}
 	else if (cnc_string) {
 		data = g_new0 (OpenConnectionData, 1);
 		data->prov_name = gda_quark_list_find (params, "PROVIDER_NAME");
 		data->cnc_string = cnc_string;
+		wr = g_hash_table_lookup (GDA_THREAD_PROVIDER (provider)->priv->prov_wrappers,
+					  data->prov_name);
 	}
+	g_assert (data);
 	
 	/* open sub connection */
-	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 ();
+
+	if (!wr) {
+		wr_created = TRUE;
+		wr = gda_thread_wrapper_new ();
+		if (!wr) {
+			gda_connection_add_event_string (cnc, "%s", _("Multi threading is not supported or enabled"));
+			g_free (data);
+			return FALSE;
+		}
+	}
+	else
+		g_object_ref (wr);
 	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);
@@ -359,6 +435,13 @@ gda_thread_provider_open_connection (GdaServerProvider *provider, GdaConnection
 	gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_thread_free_cnc_data);
 	setup_signals (cnc, cdata);
 
+	if (wr_created && PROV_CLASS (cdata->cnc_provider)->limiting_thread) {
+		/* keep GdaThreadWrapper for other uses */
+		g_hash_table_insert (GDA_THREAD_PROVIDER (provider)->priv->prov_wrappers,
+				     g_strdup (gda_server_provider_get_name (cdata->cnc_provider)),
+				     g_object_ref (wr));
+	}
+
 	return TRUE;
 }
 
diff --git a/libgda/thread-wrapper/gda-thread-provider.h b/libgda/thread-wrapper/gda-thread-provider.h
index 8cc7ec2..fd857ef 100644
--- a/libgda/thread-wrapper/gda-thread-provider.h
+++ b/libgda/thread-wrapper/gda-thread-provider.h
@@ -33,9 +33,11 @@
 
 typedef struct _GdaThreadProvider      GdaThreadProvider;
 typedef struct _GdaThreadProviderClass GdaThreadProviderClass;
+typedef struct _GdaThreadProviderPrivate GdaThreadProviderPrivate;
 
 struct _GdaThreadProvider {
 	GdaServerProvider provider;
+	GdaThreadProviderPrivate *priv;
 };
 
 struct _GdaThreadProviderClass {
diff --git a/libgda/thread-wrapper/gda-thread-wrapper.c b/libgda/thread-wrapper/gda-thread-wrapper.c
index cf1dd64..c6f7dd7 100644
--- a/libgda/thread-wrapper/gda-thread-wrapper.c
+++ b/libgda/thread-wrapper/gda-thread-wrapper.c
@@ -478,13 +478,15 @@ gda_thread_wrapper_get_property (GObject *object,
  *
  * Creates a new #GdaThreadWrapper object
  *
- * Returns: a new #GdaThreadWrapper object
+ * Returns: a new #GdaThreadWrapper object, or %NULL if threads are not supported/enabled
  *
  * Since: 4.2
  */
 GdaThreadWrapper *
 gda_thread_wrapper_new (void)
 {
+	if (! g_thread_supported ())
+		return NULL;
 	return (GdaThreadWrapper *) g_object_new (GDA_TYPE_THREAD_WRAPPER, NULL);
 }
 
@@ -539,7 +541,7 @@ gda_thread_wrapper_execute (GdaThreadWrapper *wrapper, GdaThreadWrapperFunc func
 	job->shared = shared_data_ref (td->shared);
 
 	id = job->job_id;
-	/*g_print ("... submitted job %d\n", id);*/
+	/* g_print ("... submitted job %d from thread %p\n", id, g_thread_self()); */
 
 	if (g_thread_self () == wrapper->priv->sub_thread) {
                 Result *res = g_new0 (Result, 1);
@@ -711,9 +713,6 @@ gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block)
  * Use this method to check if the execution of a function is finished. The function's execution must have
  * been requested using gda_thread_wrapper_execute().
  *
- * If @id is not %NULL, then it will contain the ID of the request if a function's execution has finished, or
- * %0 otherwise.
- *
  * Returns: the pointer returned by the execution, or %NULL if no result is available
  *
  * Since: 4.2
diff --git a/providers/postgres/gda-postgres-provider.c b/providers/postgres/gda-postgres-provider.c
index 8d3ccbe..fcee13d 100644
--- a/providers/postgres/gda-postgres-provider.c
+++ b/providers/postgres/gda-postgres-provider.c
@@ -479,9 +479,8 @@ get_connection_type_list (PostgresConnectionData *cdata)
 		/* main query to fetch infos about the data types */
 		query = g_strdup_printf (
                           "SELECT t.oid, t.typname, u.usename, pg_catalog.obj_description(t.oid), t.typinput "
-			  "FROM pg_catalog.pg_type t, pg_catalog.pg_user u, pg_catalog.pg_namespace n "
-			  "WHERE t.typowner=u.usesysid "
-			  "AND n.oid = t.typnamespace "
+			  "FROM pg_catalog.pg_type t LEFT JOIN pg_catalog.pg_user u ON (t.typowner=u.usesysid), pg_catalog.pg_namespace n "
+			  "WHERE n.oid = t.typnamespace "
 			  "AND pg_catalog.pg_type_is_visible(t.oid) "
 			  /*--AND (n.nspname = 'public' OR n.nspname = 'pg_catalog')*/
 			  "AND typname !~ '^_' "
diff --git a/samples/MetaStore/Makefile b/samples/MetaStore/Makefile
index ac54d74..ec09666 100644
--- a/samples/MetaStore/Makefile
+++ b/samples/MetaStore/Makefile
@@ -1,12 +1,15 @@
 CFLAGS = -Wall -g -DGDA_DISABLE_DEPRECATED `pkg-config --cflags libgda-4.0`
 LDFLAGS = `pkg-config --libs libgda-4.0`
 
-all: example
+all: example background
 
 example: example.c
 	$(CC) -o example example.c $(CFLAGS) $(LDFLAGS)
 
+background: background.c
+	$(CC) -o background background.c $(CFLAGS) $(LDFLAGS)
+
 clean:
 	rm -f *~
 	rm -f *.o
-	rm -f example
+	rm -f example background
diff --git a/samples/MetaStore/README b/samples/MetaStore/README
index 490b9b3..e693aca 100644
--- a/samples/MetaStore/README
+++ b/samples/MetaStore/README
@@ -5,14 +5,22 @@ Description:
 ------------
 
 The example in this directory illustrate how to get meta data information (information about
-the database's objects), and specifically the columns of some tables.
+the database's objects). There are 2 examples.
 
+example.c:
+----------
 The following is done:
 * display columns of the 'customers' table, there should be none as the meta data has not been
   yet updated
 * update the meta data for the 'customers' table, and show the columns now
 * update the meta data for all the tables, and this time show the columns of the 'products' table
 
+background.c:
+-------------
+This example executes a complete meta data update in the background (in a sub thread) using
+the GdaThreadWrapper object, while it displays some messages.
+
+
 Compiling and running:
 ----------------------
 
@@ -44,3 +52,23 @@ Tables' columns:
   name: gchararray
   price: gdouble
   wh_stored: gint
+
+
+> ./background
+Output:
+-------
+Running should produce the following output:
+
+Requesting a meta data update in the background.
+Doing some business here...
+Doing some business here...
+[...]
+Doing some business here...
+Doing some business here...
+Meta data has been updated!
+Tables' columns:
+  ref: gchararray
+  category: gint
+  name: gchararray
+  price: gdouble
+  wh_stored: gint
\ No newline at end of file
diff --git a/samples/MetaStore/background.c b/samples/MetaStore/background.c
new file mode 100644
index 0000000..a530997
--- /dev/null
+++ b/samples/MetaStore/background.c
@@ -0,0 +1,107 @@
+#include <libgda/libgda.h>
+#include <libgda/thread-wrapper/gda-thread-wrapper.h>
+
+/*
+ * Display the contents of the 'data' data model
+ */
+static void
+list_table_columns (GdaDataModel* data)
+{
+	gint nrows;
+
+	nrows = gda_data_model_get_n_rows (data);
+	if (nrows == 0)
+		g_print ("No column...\n");
+	else {
+		gint i;
+		const GValue* col_name;
+		const GValue* col_type;
+
+		g_print ("Tables' columns:\n");
+		for (i = 0; i < nrows; ++ i) {
+			col_name = gda_data_model_get_value_at (data, 0, i, NULL);
+			g_assert (col_name);
+			
+			col_type = gda_data_model_get_value_at (data, 2, i, NULL);
+			g_assert (col_type);
+			
+			printf("  %s: %s\n", g_value_get_string (col_name), g_value_get_string (col_type));
+		}
+	}
+}
+
+/* this function is executed in a sub thread */
+gboolean *
+sub_thread_update_meta_store (GdaConnection *cnc, GError **error)
+{
+	gboolean *result;
+	result = g_new (gboolean, 1);
+	*result = gda_connection_update_meta_store (cnc, NULL, error);
+	return result;
+}
+
+int
+main (int argc, char *argv[])
+{
+	GdaConnection* connection;
+	GdaDataModel* data;
+	GError* error = NULL;
+	GValue *value;
+
+	gda_init();
+	error = NULL;
+
+	/* open connection to the SalesTest data source, using the GDA_CONNECTION_OPTIONS_THREAD_SAFE flag
+	 * to make sure we can use the same connection from multiple threads at once */
+	connection = gda_connection_open_from_dsn ("SalesTest", NULL, GDA_CONNECTION_OPTIONS_THREAD_SAFE, &error);
+	if (!connection) {
+		fprintf (stderr, "%s\n", error->message);
+		return -1;
+	}
+
+	/* update meta data */
+	GdaThreadWrapper *wrapper;
+	guint job_id;
+
+	g_print ("Requesting a meta data update in the background.\n");
+	wrapper = gda_thread_wrapper_new ();
+	job_id = gda_thread_wrapper_execute (wrapper, (GdaThreadWrapperFunc) sub_thread_update_meta_store,
+					     connection, NULL, &error);
+	if (job_id == 0) {
+		fprintf (stderr, "Can't use thread wrapper: %s\n", error->message);
+		return -1;
+	}
+
+	while (1) {
+		gboolean *result;
+		g_print ("Doing some business here...\n");
+		g_usleep (100000);
+		result = (gboolean *) gda_thread_wrapper_fetch_result (wrapper, FALSE, job_id, &error);
+		if (result) {
+			gboolean meta_ok;
+
+			g_object_unref (wrapper);
+			meta_ok = *result;
+			g_free (result);
+			if (!meta_ok) {
+				fprintf (stderr, "Could not update meta data: %s\n", error->message);
+				return -1;
+			}
+			g_print ("Meta data has been updated!\n");
+			break;
+		}
+	}
+
+	/*
+	 * Get columns of the 'products' table
+	 */
+	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), "products");
+	data = gda_connection_get_meta_store_data (connection, GDA_CONNECTION_META_FIELDS, &error, 1,
+						   "name", value);
+	if (!data)
+		return -1;
+	list_table_columns (data);
+	g_object_unref (data);
+
+	return 0;
+}
diff --git a/samples/SqlBuilder/example.c b/samples/SqlBuilder/example.c
index 24ed28d..1b0cef2 100644
--- a/samples/SqlBuilder/example.c
+++ b/samples/SqlBuilder/example.c
@@ -15,9 +15,14 @@ main (int argc, char *argv[])
 
 	gda_sql_builder_set_table (b, "customers");
 
-	gda_sql_builder_add_field_value (b, "e", gda_sql_builder_param (b, 0, "p1", G_TYPE_STRING, FALSE));
-	gda_sql_builder_add_field_value (b, "f", gda_sql_builder_expr (b, 0, NULL, G_TYPE_INT, 15));
-	gda_sql_builder_add_field_value (b, "g", gda_sql_builder_expr (b, 0, NULL, G_TYPE_STRING, "joe"));
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "e"),
+				   gda_sql_builder_param (b, 0, "p1", G_TYPE_STRING, FALSE));
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "f"),
+				   gda_sql_builder_expr (b, 0, NULL, G_TYPE_INT, 15));
+	gda_sql_builder_add_field (b, gda_sql_builder_literal (b, 0, "g"),
+				   gda_sql_builder_expr (b, 0, NULL, G_TYPE_STRING, "joe"));
 	
 	render_as_sql (b);
 	g_object_unref (b);
@@ -27,19 +32,58 @@ main (int argc, char *argv[])
 	b = gda_sql_builder_new (GDA_SQL_STATEMENT_UPDATE);
 
 	gda_sql_builder_set_table (b, "products");
-	gda_sql_builder_add_field_value (b, "ref", gda_sql_builder_expr (b, 10, NULL, G_TYPE_STRING, "A0E'FESP"));
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "ref"),
+				   gda_sql_builder_expr (b, 10, NULL, G_TYPE_STRING, "A0E'FESP"));
 	gda_sql_builder_literal (b, 1, "id");
 	gda_sql_builder_expr (b, 2, NULL, G_TYPE_INT, 14);
-	gda_sql_builder_cond2 (b, 3, GDA_SQL_OPERATOR_TYPE_EQ, 1, 2);
+	gda_sql_builder_cond (b, 3, GDA_SQL_OPERATOR_TYPE_EQ, 1, 2, 0);
 	gda_sql_builder_set_where (b, 3);
 
 	render_as_sql (b);
 
 	/* reuse the same GdaSqlBuilder object to change the WHERE condition to: WHERE id = ##theid::int */
 	gda_sql_builder_set_where (b,
-				   gda_sql_builder_cond2 (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
-							  1,
-							  gda_sql_builder_param (b, 0, "theid", G_TYPE_INT, FALSE)));
+				   gda_sql_builder_cond (b, 0, GDA_SQL_OPERATOR_TYPE_EQ,
+							 1,
+							 gda_sql_builder_param (b, 0, "theid", G_TYPE_INT, FALSE),
+							 0));
+	render_as_sql (b);
+	g_object_unref (b);
+
+	/* DELETE FROM items WHERE id = ##theid::int */
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_DELETE);
+
+	gda_sql_builder_set_table (b, "items");
+	gda_sql_builder_literal (b, 1, "id");
+	gda_sql_builder_param (b, 2, "theid", G_TYPE_INT, FALSE);
+	gda_sql_builder_cond (b, 3, GDA_SQL_OPERATOR_TYPE_EQ, 1, 2, 0);
+	gda_sql_builder_set_where (b, 3);
+
+	render_as_sql (b);
+	g_object_unref (b);
+	
+	/* SELECT c.id, name, date AS person FROM customers as c, orders */
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	
+	gda_sql_builder_literal (b, 1, "customers");
+	gda_sql_builder_select_add_target (b, 1, 1, "c");
+	gda_sql_builder_select_add_target (b, 2,
+					   gda_sql_builder_literal (b, 0, "orders"),
+					   NULL);
+	gda_sql_builder_select_join_targets (b, 5, 1, 2, GDA_SQL_SELECT_JOIN_INNER, 0);
+
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "c.id"), 0);
+	gda_sql_builder_add_field (b,
+				   gda_sql_builder_literal (b, 0, "name"),
+				   gda_sql_builder_literal (b, 0, "person"));
+
+	render_as_sql (b);
+
+	/* reuse the same GdaSqlBuilder object to change the INNER join's condition */
+	gda_sql_builder_join_add_field (b, 5, "id");
+
 	render_as_sql (b);
 	g_object_unref (b);
 
diff --git a/tools/gda-sql.c b/tools/gda-sql.c
index bf7aa3b..ad4b3cd 100644
--- a/tools/gda-sql.c
+++ b/tools/gda-sql.c
@@ -1349,9 +1349,11 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
 	
 	info = gda_config_get_dsn_info (real_cnc);
 	if (info && !real_provider)
-		newcnc = gda_connection_open_from_dsn (real_cnc_string, real_auth_string, 0, error);
+		newcnc = gda_connection_open_from_dsn (real_cnc_string, real_auth_string,
+						       GDA_CONNECTION_OPTIONS_THREAD_SAFE, error);
 	else 
-		newcnc = gda_connection_open_from_string (NULL, real_cnc_string, real_auth_string, 0, error);
+		newcnc = gda_connection_open_from_string (NULL, real_cnc_string, real_auth_string,
+							  GDA_CONNECTION_OPTIONS_THREAD_SAFE, error);
 	
 	g_free (real_cnc_string);
 	g_free (real_cnc);



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