[libgda] Initial Web provider implementation



commit 6ead78dd2f0a35810139d3c67b245c96c3dad5c4
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sun Nov 15 22:56:01 2009 +0100

    Initial Web provider implementation
    
    this new provider opens a connection through a distant
    web server (tested with apache and lighthttpd) where some
    PHP scripts are executed

 configure.in                                       |    7 +
 doc/C/libgda-sections.txt                          |    3 +
 doc/C/tmpl/gda-connection.sgml                     |    1 +
 doc/C/tmpl/provider-support.sgml                   |   19 +
 libgda/gda-connection-internal.h                   |    1 +
 libgda/gda-connection-private.h                    |    6 +
 libgda/gda-connection.c                            |  162 ++-
 libgda/gda-connection.h                            |    3 +-
 libgda/gda-util.c                                  |   16 +-
 libgda/gda-value.c                                 |   11 +-
 libgda/libgda.symbols                              |    2 +
 libgda/sqlite/gda-sqlite-provider.c                |   14 +-
 libgda/sqlite/keywords_hash.h                      |    6 +
 libgda/thread-wrapper/gda-thread-provider.c        |   43 +-
 libgda/thread-wrapper/gda-thread-wrapper.c         |    6 +-
 po/POTFILES.in                                     |   11 +-
 providers/Makefile.am                              |    8 +-
 providers/postgres/Makefile.am                     |   39 +-
 providers/postgres/gda-postgres-meta.c             | 1760 -----------------
 providers/postgres/gda-postgres-provider.c         |  243 +---
 providers/postgres/gda-postgres-recordset.c        |    2 +-
 providers/postgres/gda-postgres-util.c             |   44 -
 providers/postgres/gda-postgres-util.h             |    8 +-
 providers/postgres/gda-postgres.h                  |   21 +-
 providers/reuseable/Makefile.am                    |   19 +
 providers/reuseable/gda-provider-reuseable.h       |   69 +
 providers/reuseable/postgres/Makefile.am           |   49 +
 providers/reuseable/postgres/gda-postgres-meta.c   | 2042 ++++++++++++++++++++
 .../{ => reuseable}/postgres/gda-postgres-meta.h   |    0
 .../{ => reuseable}/postgres/gda-postgres-parser.c |    0
 .../{ => reuseable}/postgres/gda-postgres-parser.h |    0
 .../reuseable/postgres/gda-postgres-reuseable.c    |  432 +++++
 .../reuseable/postgres/gda-postgres-reuseable.h    |   78 +
 providers/{ => reuseable}/postgres/gen_def.c       |    0
 .../{ => reuseable}/postgres/keywords_82.list      |    0
 .../{ => reuseable}/postgres/keywords_83.list      |    0
 .../{ => reuseable}/postgres/keywords_84.list      |    0
 providers/{ => reuseable}/postgres/parser.y        |    0
 providers/reuseable/reuse-all.c                    |   55 +
 providers/reuseable/reuse-all.h                    |   37 +
 providers/web/Makefile.am                          |   69 +
 providers/web/README                               |   63 +
 providers/web/gda-web-blob-op.c                    |  226 +++
 providers/web/gda-web-blob-op.h                    |   57 +
 providers/web/gda-web-ddl.c                        |  145 ++
 providers/web/gda-web-ddl.h                        |   34 +
 providers/web/gda-web-meta.c                       | 1382 +++++++++++++
 providers/web/gda-web-meta.h                       |  197 ++
 providers/web/gda-web-provider.c                   | 1686 ++++++++++++++++
 providers/web/gda-web-provider.h                   |   51 +
 providers/web/gda-web-pstmt.c                      |  143 ++
 providers/web/gda-web-pstmt.h                      |   57 +
 providers/web/gda-web-recordset.c                  |  631 ++++++
 providers/web/gda-web-recordset.h                  |   60 +
 providers/web/gda-web-util.c                       |  657 +++++++
 providers/web/gda-web-util.h                       |   59 +
 providers/web/gda-web.h                            |   67 +
 providers/web/libgda-web-4.0.pc.in                 |    9 +
 providers/web/libmain.c                            |  104 +
 providers/web/php/README                           |   35 +
 providers/web/php/gda-clean.php                    |   23 +
 providers/web/php/gda-config.php                   |   28 +
 providers/web/php/gda-exception.php                |    9 +
 providers/web/php/gda-front.php                    |   68 +
 providers/web/php/gda-meta.php                     |  648 +++++++
 providers/web/php/gda-setup.php                    |   57 +
 providers/web/php/gda-tester.php                   |   50 +
 providers/web/php/gda-utils.php                    |  107 +
 providers/web/php/gda-worker.php                   |  654 +++++++
 providers/web/protocol.dia                         |  Bin 0 -> 3480 bytes
 providers/web/web_specs_auth.xml.in                |    6 +
 providers/web/web_specs_dsn.xml.in                 |   11 +
 tools/gda-sql.c                                    |   30 +-
 73 files changed, 10526 insertions(+), 2114 deletions(-)
---
diff --git a/configure.in b/configure.in
index 3ccf941..567397d 100644
--- a/configure.in
+++ b/configure.in
@@ -9,6 +9,8 @@ AC_PREREQ(2.59)
 AC_CONFIG_SRCDIR(libgda/libgda.h.in)
 AM_INIT_AUTOMAKE(1.8 -Wall no-define dist-bzip2)
 
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
 
 AC_SUBST(GDA_VERSION, major.minor.micro)
 
@@ -1708,6 +1710,8 @@ libgda/sqlite/sqlite-src/Makefile
 libgda/sqlite/virtual/Makefile
 libgda/thread-wrapper/Makefile
 providers/Makefile
+providers/reuseable/Makefile
+providers/reuseable/postgres/Makefile
 providers/bdb/Makefile
 providers/bdb/libgda-bdb-4.0.pc
 providers/mdb/Makefile
@@ -1722,6 +1726,8 @@ providers/sqlite/Makefile
 providers/sqlite/libgda-sqlite-4.0.pc
 providers/jdbc/Makefile
 providers/jdbc/libgda-jdbc-4.0.pc
+providers/web/Makefile
+providers/web/libgda-web-4.0.pc
 providers/skel-implementation/Makefile
 providers/skel-implementation/capi/Makefile
 providers/skel-implementation/capi/libgda-capi-4.0.pc
@@ -1808,6 +1814,7 @@ dnl echo "      Sybase = `if test x$sybasedir != x; then echo yes; else echo no;
 dnl echo "      xBase (dBase, Clipper, FoxPro) = `if test x$xbasedir != x; then echo yes; else echo no; fi`"
 dnl echo "      LDAP = `if test x$ldapdir != x; then echo yes; else echo no; fi`"
 echo "      JDBC = `if test x$have_java = xyes; then echo yes; else echo no; fi`"
+echo "      WEB = `if test x$have_libsoup = xyes; then echo yes; else echo no; fi`"
 if test x"$br_cv_binreloc" != "xyes" -a x"$platform_win32" != "xyes"
 then
        echo "   Binreloc support is disabled: Libgda will not be relocatable. To enable binreloc support re-run with --enable-binreloc (see http://autopackage.org/docs/binreloc for more information)"
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index 114997a..33d1799 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1462,6 +1462,9 @@ gda_connection_internal_savepoint_added
 gda_connection_internal_savepoint_rolledback
 gda_connection_internal_savepoint_removed
 gda_connection_internal_change_transaction_state
+gda_connection_internal_reset_transaction_status
+<SUBSECTION>
+gda_connection_open_sqlite
 <SUBSECTION>
 gda_meta_store_modify
 gda_meta_store_modify_with_context
diff --git a/doc/C/tmpl/gda-connection.sgml b/doc/C/tmpl/gda-connection.sgml
index 98eb8fa..3515eaa 100644
--- a/doc/C/tmpl/gda-connection.sgml
+++ b/doc/C/tmpl/gda-connection.sgml
@@ -172,6 +172,7 @@ A connection to a database
 @GDA_CONNECTION_CANT_LOCK_ERROR: 
 @GDA_CONNECTION_TASK_NOT_FOUND_ERROR: 
 @GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR: 
+ GDA_CONNECTION_CLOSED_ERROR: 
 
 <!-- ##### MACRO GDA_CONNECTION_ERROR ##### -->
 <para>
diff --git a/doc/C/tmpl/provider-support.sgml b/doc/C/tmpl/provider-support.sgml
index 147ac38..937bd21 100644
--- a/doc/C/tmpl/provider-support.sgml
+++ b/doc/C/tmpl/provider-support.sgml
@@ -323,6 +323,25 @@ Methods dedicated to implementing providers
 @newstate: 
 
 
+<!-- ##### FUNCTION gda_connection_internal_reset_transaction_status ##### -->
+<para>
+
+</para>
+
+ cnc: 
+
+
+<!-- ##### FUNCTION gda_connection_open_sqlite ##### -->
+<para>
+
+</para>
+
+ directory: 
+ filename: 
+ auto_unlink: 
+ Returns: 
+
+
 <!-- ##### FUNCTION gda_meta_store_modify ##### -->
 <para>
 
diff --git a/libgda/gda-connection-internal.h b/libgda/gda-connection-internal.h
index 737eb5c..40edfeb 100644
--- a/libgda/gda-connection-internal.h
+++ b/libgda/gda-connection-internal.h
@@ -62,6 +62,7 @@ void _ThreadConnectionAsyncTask_free (ThreadConnectionAsyncTask *atd);
  */
 typedef struct {
         GdaConnection *sub_connection;
+	gboolean sub_connection_has_closed;
         GdaServerProvider *cnc_provider;
         GdaThreadWrapper *wrapper;
 	GArray *handlers_ids; /* array of gulong */
diff --git a/libgda/gda-connection-private.h b/libgda/gda-connection-private.h
index af5f7dd..f849f10 100644
--- a/libgda/gda-connection-private.h
+++ b/libgda/gda-connection-private.h
@@ -56,6 +56,7 @@ void gda_connection_internal_savepoint_rolledback (GdaConnection *cnc, const gch
 void gda_connection_internal_savepoint_removed (GdaConnection *cnc, const gchar *svp_name);
 void gda_connection_internal_change_transaction_state (GdaConnection *cnc,
 						       GdaTransactionStatusState newstate);
+void gda_connection_internal_reset_transaction_status (GdaConnection *cnc);
 
 /* 
  * prepared statements support
@@ -64,6 +65,11 @@ void      gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatemen
 void      gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt); 
 GdaPStmt *gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
 
+/*
+ * Open an SQLite connection even if the SQLite provider is not installed
+ */
+GdaConnection *gda_connection_open_sqlite (const gchar *directory, const gchar *filename, gboolean auto_unlink);
+
 G_END_DECLS
 
 #endif
diff --git a/libgda/gda-connection.c b/libgda/gda-connection.c
index baac0a1..359ce6f 100644
--- a/libgda/gda-connection.c
+++ b/libgda/gda-connection.c
@@ -48,6 +48,10 @@
 #include <libgda/thread-wrapper/gda-thread-provider.h>
 #include <libgda/gda-repetitive-statement.h>
 
+#include <glib/gstdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
 #define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
 
 struct _GdaConnectionPrivate {
@@ -1127,7 +1131,7 @@ _gda_open_internal_sqlite_connection (const gchar *cnc_string)
 	GdaServerProvider *prov = _gda_config_sqlite_provider;
 	gchar *user, *pass, *real_cnc, *real_provider;
 
-	g_print ("%s(%s)\n", __FUNCTION__, cnc_string);
+	/*g_print ("%s(%s)\n", __FUNCTION__, cnc_string);*/
 	gda_connection_string_split (cnc_string, &real_cnc, &real_provider, &user, &pass);
         if (!real_cnc) {
                 g_free (user);
@@ -1162,6 +1166,68 @@ _gda_open_internal_sqlite_connection (const gchar *cnc_string)
 	return cnc;
 }
 
+static void
+sqlite_connection_closed_cb (GdaConnection *cnc, gpointer data)
+{
+	gchar *filename;
+	filename = g_object_get_data (G_OBJECT (cnc), "__gda_fname");
+	g_assert (filename && *filename);
+	g_unlink (filename);
+}
+
+/**
+ * gda_connection_open_sqlite
+ * @cnc_string: a connection string
+ * @directory: the directory the database file will be in, or %NULL for the default TMP directory
+ * @filename: the database file name
+ * @auto_unlink: if %TRUE, then the database file will be removed afterwards
+ *
+ * Opens an SQLite connection even if the SQLite provider is not installed,
+ * to be used by database providers which need a temporary database to store
+ * some information.
+ *
+ * Returns: a new #GdaConnection, or %NULL if an error occurred
+ */
+GdaConnection *
+gda_connection_open_sqlite (const gchar *directory, const gchar *filename, gboolean auto_unlink)
+{
+	GdaConnection *cnc;
+	gchar *fname;
+	gint fd;
+
+	if (!directory)
+		directory = g_get_tmp_dir();
+	else
+		g_return_val_if_fail (*directory, NULL);
+	g_return_val_if_fail (filename && *filename, NULL);
+
+	fname = g_build_filename (directory, filename, NULL);
+	fd = g_open (fname, O_WRONLY | O_CREAT | O_NOCTTY | O_TRUNC,
+		     S_IRUSR | S_IWUSR);
+	if (fd == -1) {
+		g_free (fname);
+		return NULL;
+	}
+	close (fd);
+
+	gchar *tmp1, *tmp2, *cncstring;
+	tmp1 = gda_rfc1738_encode (directory);
+	tmp2 = gda_rfc1738_encode (filename);
+	cncstring = g_strdup_printf ("SQLite://DB_DIR=%s;DB_NAME=%s", tmp1, tmp2);
+	g_free (tmp1);
+	g_free (tmp2);
+	
+	cnc = _gda_open_internal_sqlite_connection (cncstring);
+	g_free (cncstring);
+
+	if (auto_unlink) {
+		g_object_set_data_full (G_OBJECT (cnc), "__gda_fname", fname, g_free);
+		g_signal_connect (cnc, "conn-closed",
+				  G_CALLBACK (sqlite_connection_closed_cb), NULL);
+	}
+	return cnc;
+}
+
 /**
  * gda_connection_open
  * @cnc: a #GdaConnection object
@@ -1251,12 +1317,15 @@ gda_connection_open (GdaConnection *cnc, GError **error)
 	/* try to open the connection */
 	auth = gda_quark_list_new_from_string (real_auth_string);
 
+	cnc->priv->is_open = TRUE; /* consider the connection opened to allow the open_connection()
+				    * virtual method to execute statements of needed */
 	if (PROV_CLASS (cnc->priv->provider_obj)->open_connection (cnc->priv->provider_obj, cnc, params, auth,
 								   NULL, NULL, NULL))
 		cnc->priv->is_open = TRUE;
 	else {
 		const GList *events;
 		
+		cnc->priv->is_open = FALSE;
 		events = gda_connection_get_events (cnc);
 		if (events) {
 			GList *l;
@@ -1341,6 +1410,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
 {
 	g_return_if_fail (GDA_IS_CONNECTION (cnc));
 
+	g_object_ref (cnc);
 	gda_connection_lock ((GdaLockable*) cnc);
 
 	if (cnc->priv->monitor_id > 0) {
@@ -1349,6 +1419,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
 	}
 
 	if (! cnc->priv->is_open) {
+		g_object_unref (cnc);
 		gda_connection_unlock ((GdaLockable*) cnc);
 		return;
 	}
@@ -1375,6 +1446,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
 		cnc->priv->provider_data = NULL;
 	}
 
+	gda_connection_unlock ((GdaLockable*) cnc);
 #ifdef GDA_DEBUG_signal
         g_print (">> 'CONN_CLOSED' from %s\n", __FUNCTION__);
 #endif
@@ -1382,8 +1454,7 @@ gda_connection_close_no_warning (GdaConnection *cnc)
 #ifdef GDA_DEBUG_signal
         g_print ("<< 'CONN_CLOSED' from %s\n", __FUNCTION__);
 #endif
-
-	gda_connection_unlock ((GdaLockable*) cnc);
+	g_object_unref (cnc);
 }
 
 
@@ -1913,6 +1984,7 @@ async_stmt_exec_cb (GdaServerProvider *provider, GdaConnection *cnc, guint task_
 	gint i;
 	gboolean is_completed;
 
+	g_object_ref ((GObject*) cnc);
 	gda_connection_lock (GDA_LOCKABLE (cnc));
 
 	i = get_task_index (cnc, task_id, &is_completed, TRUE);
@@ -1982,6 +2054,7 @@ async_stmt_exec_cb (GdaServerProvider *provider, GdaConnection *cnc, guint task_
 			   "ignored.\n", task_id);
 
 	gda_connection_unlock (GDA_LOCKABLE (cnc));
+	g_object_unref ((GObject*) cnc);
 }
 
 /**
@@ -2029,11 +2102,22 @@ gda_connection_async_statement_execute (GdaConnection *cnc, GdaStatement *stmt,
 	g_return_val_if_fail (PROV_CLASS (cnc->priv->provider_obj)->statement_execute, 0);
 
 
+	g_object_ref ((GObject*) cnc);
 	if (! gda_connection_trylock ((GdaLockable*) cnc)) {
 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CANT_LOCK_ERROR,
 			     _("Can't obtain connection lock"));
+		g_object_unref ((GObject*) cnc);
 		return 0;
 	}
+
+	if (!cnc->priv->is_open) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+			     _("Connection is closed"));
+		gda_connection_unlock (GDA_LOCKABLE (cnc));
+		g_object_unref ((GObject*) cnc);
+		return 0;
+	}
+
 	id = cnc->priv->next_task_id ++;
 	task = cnc_task_new (id, stmt, model_usage, col_types, params, need_last_insert_row);
 	g_array_append_val (cnc->priv->waiting_tasks, task);
@@ -2071,6 +2155,7 @@ gda_connection_async_statement_execute (GdaConnection *cnc, GdaStatement *stmt,
 	}
 
 	gda_connection_unlock ((GdaLockable*) cnc);
+	g_object_unref ((GObject*) cnc);
 
 	return id;
 }
@@ -2231,12 +2316,21 @@ gda_connection_statement_execute_v (GdaConnection *cnc, GdaStatement *stmt, GdaS
 	types = make_col_types_array (10, ap);
 	va_end (ap);
 
+	g_object_ref ((GObject*) cnc);
 	gda_connection_lock ((GdaLockable*) cnc);
 	if (last_inserted_row) 
 		*last_inserted_row = NULL;
 	if (cnc->priv->auto_clear_events_list)
 		_clear_events_list (cnc);
 
+	if (!cnc->priv->is_open) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+			     _("Connection is closed"));
+		gda_connection_unlock (GDA_LOCKABLE (cnc));
+		g_object_unref ((GObject*) cnc);
+		return NULL;
+	}
+
 	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
 	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
 		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2247,6 +2341,8 @@ gda_connection_statement_execute_v (GdaConnection *cnc, GdaStatement *stmt, GdaS
 								       NULL, NULL, NULL, error);
 	g_free (types);
 	gda_connection_unlock ((GdaLockable*) cnc);
+	g_object_unref ((GObject*) cnc);
+
 	return obj;
 }
 
@@ -2491,10 +2587,19 @@ gda_connection_statement_execute_select_fullv (GdaConnection *cnc, GdaStatement
 	types = make_col_types_array (10, ap);
 	va_end (ap);
 
+	g_object_ref ((GObject*) cnc);
 	gda_connection_lock ((GdaLockable*) cnc);
 	if (cnc->priv->auto_clear_events_list)
 		_clear_events_list (cnc);
 
+	if (!cnc->priv->is_open) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+			     _("Connection is closed"));
+		gda_connection_unlock (GDA_LOCKABLE (cnc));
+		g_object_unref ((GObject*) cnc);
+		return NULL;
+	}
+
 	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
 	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
 		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2505,6 +2610,7 @@ gda_connection_statement_execute_select_fullv (GdaConnection *cnc, GdaStatement
 											  types, NULL, NULL, 
 											  NULL, NULL, error);
 	gda_connection_unlock ((GdaLockable*) cnc);
+	g_object_unref ((GObject*) cnc);
 	g_free (types);
 	if (model && !GDA_IS_DATA_MODEL (model)) {
 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_STATEMENT_TYPE_ERROR,
@@ -2552,10 +2658,19 @@ gda_connection_statement_execute_select_full (GdaConnection *cnc, GdaStatement *
 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
 	g_return_val_if_fail (PROV_CLASS (cnc->priv->provider_obj)->statement_execute, NULL);
 
+	g_object_ref ((GObject*) cnc);
 	gda_connection_lock ((GdaLockable*) cnc);
 	if (cnc->priv->auto_clear_events_list)
 		_clear_events_list (cnc);
 	
+	if (!cnc->priv->is_open) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+			     _("Connection is closed"));
+		gda_connection_unlock (GDA_LOCKABLE (cnc));
+		g_object_unref ((GObject*) cnc);
+		return NULL;
+	}
+
 	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
 	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
 		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2566,6 +2681,8 @@ gda_connection_statement_execute_select_full (GdaConnection *cnc, GdaStatement *
 											  model_usage, col_types, NULL, 
 											  NULL, NULL, NULL, error);
 	gda_connection_unlock ((GdaLockable*) cnc);
+	g_object_unref ((GObject*) cnc);
+
 	if (model && !GDA_IS_DATA_MODEL (model)) {
 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_STATEMENT_TYPE_ERROR,
 			      "%s", _("Statement is not a selection statement"));
@@ -2612,8 +2729,17 @@ gda_connection_repetitive_statement_execute (GdaConnection *cnc, GdaRepetitiveSt
 	g_object_get (rstmt, "statement", &stmt, NULL);
 	g_return_val_if_fail (stmt, NULL);
 
+	g_object_ref ((GObject*) cnc);
 	gda_connection_lock ((GdaLockable*) cnc);
 
+	if (!cnc->priv->is_open) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+			     _("Connection is closed"));
+		gda_connection_unlock (GDA_LOCKABLE (cnc));
+		g_object_unref ((GObject*) cnc);
+		return NULL;
+	}
+
 	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
 	    ! (model_usage & GDA_STATEMENT_MODEL_CURSOR_FORWARD))
 		model_usage |= GDA_STATEMENT_MODEL_RANDOM_ACCESS;
@@ -2645,7 +2771,7 @@ gda_connection_repetitive_statement_execute (GdaConnection *cnc, GdaRepetitiveSt
 	g_slist_free (sets_list);
 
 	gda_connection_unlock ((GdaLockable*) cnc);
-
+	g_object_unref ((GObject*) cnc);
 	g_object_unref (stmt);
 
 	return g_slist_reverse (retlist);
@@ -4687,7 +4813,7 @@ gda_connection_internal_statement_executed (GdaConnection *cnc, GdaStatement *st
  * @cnc: a #GdaConnection
  * @newstate: the new state
  *
- * Internal functions to be called by database providers to force a transaction status
+ * Internal function to be called by database providers to force a transaction status
  * change.
  */
 void
@@ -4714,6 +4840,32 @@ gda_connection_internal_change_transaction_state (GdaConnection *cnc,
 	gda_connection_unlock ((GdaLockable*) cnc);
 }
 
+/**
+ * gda_connection_internal_reset_transaction_status
+ * @cnc: a #GdaConnection
+ *
+ * Internal function to be called by database providers to reset the transaction status.
+ */
+void
+gda_connection_internal_reset_transaction_status (GdaConnection *cnc)
+{
+	g_return_if_fail (GDA_IS_CONNECTION (cnc));
+
+	gda_connection_lock ((GdaLockable*) cnc);
+	if (cnc->priv->trans_status) {
+		g_object_unref (cnc->priv->trans_status);
+		cnc->priv->trans_status = NULL;
+#ifdef GDA_DEBUG_signal
+		g_print (">> 'TRANSACTION_STATUS_CHANGED' from %s\n", __FUNCTION__);
+#endif
+		g_signal_emit (G_OBJECT (cnc), gda_connection_signals[TRANSACTION_STATUS_CHANGED], 0);
+#ifdef GDA_DEBUG_signal
+		g_print ("<< 'TRANSACTION_STATUS_CHANGED' from %s\n", __FUNCTION__);
+#endif
+	}
+	gda_connection_unlock ((GdaLockable*) cnc);
+}
+
 /*
  * Prepared statements handling
  */
diff --git a/libgda/gda-connection.h b/libgda/gda-connection.h
index 134e65c..6aa531a 100644
--- a/libgda/gda-connection.h
+++ b/libgda/gda-connection.h
@@ -56,7 +56,8 @@ typedef enum {
 	GDA_CONNECTION_STATEMENT_TYPE_ERROR,
 	GDA_CONNECTION_CANT_LOCK_ERROR,
 	GDA_CONNECTION_TASK_NOT_FOUND_ERROR,
-	GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR
+	GDA_CONNECTION_UNSUPPORTED_THREADS_ERROR,
+	GDA_CONNECTION_CLOSED_ERROR
 } GdaConnectionError;
 #define GDA_CONNECTION_NONEXIST_DSN_ERROR GDA_CONNECTION_DSN_NOT_FOUND_ERROR
 
diff --git a/libgda/gda-util.c b/libgda/gda-util.c
index 3c30774..5b6e6e8 100644
--- a/libgda/gda-util.c
+++ b/libgda/gda-util.c
@@ -41,12 +41,9 @@
 
 #include <libgda/binreloc/gda-binreloc.h>
 
-/* we don't want to duplicate the symbols in <libgda/sqlite/keywords_hash.h>, so simply
- * declare them as external
- */
-extern const unsigned char UpperToLower[];
 #define charMap(X) UpperToLower[(unsigned char)(X)]
-extern int casecmp(const char *zLeft, const char *zRight, int N);
+#define KEYWORDS_HASH_NO_STATIC
+#include <libgda/sqlite/keywords_hash.h>
 #include "keywords_hash.c" /* this one is dynamically generated */
 
 extern gchar *gda_lang_locale;
@@ -547,6 +544,15 @@ gda_utility_holder_load_attributes (GdaHolder *holder, xmlNodePtr node, GSList *
 	str = xmlGetProp (node, BAD_CAST "source");
 	if (str) 
 		g_object_set_data_full (G_OBJECT (holder), "source", str, xmlFree);
+	str = xmlGetProp (node, BAD_CAST "plugin");
+	if (str) {
+		GValue *value;
+#define GDAUI_ATTRIBUTE_PLUGIN "__gdaui_attr_plugin"
+                value = gda_value_new_from_string ((gchar*) str, G_TYPE_STRING);
+		gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
+		gda_value_free (value);
+		xmlFree (str);
+	}
 
 	/* set restricting source if specified */
 	if (str && sources) {
diff --git a/libgda/gda-value.c b/libgda/gda-value.c
index 1dba034..c5dcc55 100644
--- a/libgda/gda-value.c
+++ b/libgda/gda-value.c
@@ -63,18 +63,11 @@ set_from_string (GValue *value, const gchar *as_string)
 	/* custom transform function */
 	retval = FALSE;
 	if (type == G_TYPE_BOOLEAN) {
-		if (((as_string[0] == 't') || (as_string[0] == 'T')) &&
-		    ((as_string[1] == 'r') || (as_string[1] == 'R')) &&
-		    ((as_string[2] == 'u') || (as_string[2] == 'U')) &&
-		    ((as_string[3] == 'e') || (as_string[3] == 'E'))) {
+		if ((as_string[0] == 't') || (as_string[0] == 'T')) {
 			g_value_set_boolean (value, TRUE);
 			retval = TRUE;
 		}
-		else if (((as_string[0] == 'f') || (as_string[0] == 'F')) &&
-			 ((as_string[1] == 'a') || (as_string[1] == 'A')) &&
-			 ((as_string[2] == 'l') || (as_string[2] == 'L')) &&
-			 ((as_string[3] == 's') || (as_string[3] == 'S')) &&
-			 ((as_string[4] == 'e') || (as_string[4] == 'E'))) {
+		else if ((as_string[0] == 'f') || (as_string[0] == 'F')) {
 			g_value_set_boolean (value, FALSE);
 			retval = TRUE;
 		}
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 2a77133..0aaab9c 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -134,6 +134,7 @@
 	gda_connection_get_type
 	gda_connection_internal_change_transaction_state
 	gda_connection_internal_get_provider_data
+	gda_connection_internal_reset_transaction_status
 	gda_connection_internal_savepoint_added
 	gda_connection_internal_savepoint_removed
 	gda_connection_internal_savepoint_rolledback
@@ -147,6 +148,7 @@
 	gda_connection_open
 	gda_connection_open_from_dsn
 	gda_connection_open_from_string
+	gda_connection_open_sqlite
 	gda_connection_options_get_type
 	gda_connection_perform_operation
 	gda_connection_quote_sql_identifier
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index 9cef424..fa11f11 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -646,11 +646,17 @@ gda_sqlite_provider_open_connection (GdaServerProvider *provider, GdaConnection
 
 	/* set SQLite library options */
 	GObject *obj;
+	GError *lerror = NULL;
 	obj = gda_connection_statement_execute (cnc, internal_stmt[INTERNAL_PRAGMA_EMPTY_RESULT],
-						NULL, GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, NULL);
-	if (!obj)
-		gda_connection_add_event_string (cnc, _("Could not set empty_result_callbacks SQLite option"));
-	g_object_unref (obj);
+						NULL, GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, &lerror);
+	if (!obj) {
+		gda_connection_add_event_string (cnc,
+						 _("Could not set empty_result_callbacks SQLite option: %s"),
+						 lerror && lerror->message ? lerror->message : _("no detail"));
+		g_clear_error (&lerror);
+	}
+	else
+		g_object_unref (obj);
 
 	/* make sure the internals are completely initialized now */
 	{
diff --git a/libgda/sqlite/keywords_hash.h b/libgda/sqlite/keywords_hash.h
index 091884c..1d0e5e2 100644
--- a/libgda/sqlite/keywords_hash.h
+++ b/libgda/sqlite/keywords_hash.h
@@ -10,6 +10,9 @@
 
 #include <string.h>
 
+#ifndef KEYWORDS_HASH_NO_STATIC
+static
+#endif
 const unsigned char UpperToLower[] = {
       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
      18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
@@ -28,6 +31,9 @@ const unsigned char UpperToLower[] = {
     252,253,254,255
 };
 #define charMap(X) UpperToLower[(unsigned char)X]
+#ifndef KEYWORDS_HASH_NO_STATIC
+static
+#endif
 int
 casecmp (const char *zLeft, const char *zRight, int N)
 {
diff --git a/libgda/thread-wrapper/gda-thread-provider.c b/libgda/thread-wrapper/gda-thread-provider.c
index 51e65d1..5c06789 100644
--- a/libgda/thread-wrapper/gda-thread-provider.c
+++ b/libgda/thread-wrapper/gda-thread-provider.c
@@ -286,6 +286,20 @@ _gda_thread_provider_get_type (void)
 	return type;
 }
 
+/*
+ * Check if the wrapped connection has been closed in the meanwhile
+ *
+ * Returns: %TRUE if @cnc is now also closed (WARNING: @cnc may not exist anymore it %TRUE is returned)
+ */
+static gboolean
+check_cnc_closed (GdaConnection *cnc, ThreadConnectionData *cdata)
+{
+	if (cdata->sub_connection_has_closed) {
+		gda_connection_close_no_warning (cnc);
+		return TRUE;
+	}
+	return FALSE;
+}
 
 /*
  * Get provider name request
@@ -516,13 +530,31 @@ sub_cnc_transaction_status_changed_cb (GdaThreadWrapper *wrapper, GdaConnection
 }
 
 static void
+sub_cnc_closed_cb (GdaThreadWrapper *wrapper, GdaConnection *sub_cnc, const gchar *signal,
+		   gint n_param_values, const GValue *param_values,
+		   gpointer gda_reserved, GdaConnection *wrapper_cnc)
+{
+	ThreadConnectionData *cdata;
+	cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (wrapper_cnc);
+	if (!cdata) 
+		return;
+	cdata->sub_connection_has_closed = TRUE;
+}
+
+static void
 setup_signals (GdaConnection *cnc, ThreadConnectionData *cdata)
 {
 	gulong hid;
 	hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection,
+					      "conn-closed", FALSE,
+					      (GdaThreadWrapperCallback) sub_cnc_closed_cb, cnc);
+	g_array_prepend_val (cdata->handlers_ids, hid);
+
+	hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection,
 					      "error", FALSE,
 					      (GdaThreadWrapperCallback) sub_cnc_error_cb, cnc);
 	g_array_prepend_val (cdata->handlers_ids, hid);
+
 	hid = gda_thread_wrapper_connect_raw (cdata->wrapper, cdata->sub_connection,
 					      "transaction-status-changed", FALSE,
 					      (GdaThreadWrapperCallback) sub_cnc_transaction_status_changed_cb,
@@ -1556,7 +1588,13 @@ gda_thread_provider_statement_execute (GdaServerProvider *provider, GdaConnectio
 
 	cdata = (ThreadConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata) 
-		return FALSE;
+		return NULL;
+
+	if (check_cnc_closed (cnc, cdata)) {
+		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_CLOSED_ERROR,
+			     _("Connection is closed"));
+		return NULL;
+	}
 	
 	if (async_cb) {
 		ExecuteStatementData *wdata;
@@ -1624,6 +1662,9 @@ gda_thread_provider_handle_async (GdaServerProvider *provider, GdaConnection *cn
 	if (!cdata) 
 		return FALSE;
 
+	if (check_cnc_closed (cnc, cdata))
+		return TRUE;
+
 	if (!cdata->async_tasks)
 		return TRUE;
 
diff --git a/libgda/thread-wrapper/gda-thread-wrapper.c b/libgda/thread-wrapper/gda-thread-wrapper.c
index 3c84034..bcbb675 100644
--- a/libgda/thread-wrapper/gda-thread-wrapper.c
+++ b/libgda/thread-wrapper/gda-thread-wrapper.c
@@ -759,16 +759,16 @@ gda_thread_wrapper_iterate (GdaThreadWrapper *wrapper, gboolean may_block)
 			/* run callback now */
 			SignalSpec *spec = job->u.signal.spec;
 
-			signal_spec_lock (spec);
 			if (spec->callback)
 				spec->callback (wrapper, spec->instance, ((GSignalQuery*)spec)->signal_name,
 						job->u.signal.n_param_values, job->u.signal.param_values, NULL,
 						spec->data);
 			else
 				g_print ("Not propagating signal %s\n", ((GSignalQuery*)spec)->signal_name);
-			signal_spec_unref (spec);
 			job->u.signal.spec = NULL;
 			job_free (job);
+			signal_spec_lock (spec);
+			signal_spec_unref (spec);
 			do_again = TRUE;
 		}
 		else
@@ -1119,7 +1119,7 @@ gda_thread_wrapper_disconnect (GdaThreadWrapper *wrapper, gulong id)
 	sigspec->instance = NULL;
 	sigspec->signal_id = 0;
 	g_async_queue_unref (sigspec->reply_queue);
-	sigspec->reply_queue = 0;
+	sigspec->reply_queue = NULL;
 	sigspec->callback = NULL;
 	sigspec->data = NULL;
 	signal_spec_unref (sigspec);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 665bbc3..970febd 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -164,7 +164,6 @@ providers/oracle/oracle_specs_create_table.xml.in
 providers/postgres/gda-postgres-blob-op.c
 providers/postgres/gda-postgres-ddl.c
 providers/postgres/gda-postgres-handler-bin.c
-providers/postgres/gda-postgres-meta.c
 providers/postgres/gda-postgres-provider.c
 providers/postgres/gda-postgres-pstmt.c
 providers/postgres/gda-postgres-recordset.c
@@ -182,6 +181,7 @@ providers/postgres/postgres_specs_drop_table.xml.in
 providers/postgres/postgres_specs_drop_view.xml.in
 providers/postgres/postgres_specs_dsn.xml.in
 providers/postgres/postgres_specs_rename_table.xml.in
+providers/reuseable/postgres/gda-postgres-meta.c
 providers/sqlite/libmain.c
 providers/sqlite/sqlite_specs_add_column.xml.in
 providers/sqlite/sqlite_specs_create_db.xml.in
@@ -194,6 +194,15 @@ providers/sqlite/sqlite_specs_drop_table.xml.in
 providers/sqlite/sqlite_specs_drop_view.xml.in
 providers/sqlite/sqlite_specs_dsn.xml.in
 providers/sqlite/sqlite_specs_rename_table.xml.in
+providers/web/gda-web-ddl.c
+providers/web/gda-web-provider.c
+providers/web/gda-web-recordset.c
+providers/web/gda-web-util.c
+providers/web/libmain.c
+providers/web/gda-web-meta.c
+providers/web/gda-web-provider.c
+providers/web/web_specs_auth.xml.in
+providers/web/web_specs_dsn.xml.in
 testing/gda-test-blob.c
 testing/gda-test-connection.c
 testing/gdaui-test-data-entries.c
diff --git a/providers/Makefile.am b/providers/Makefile.am
index cf5c18f..62967c7 100644
--- a/providers/Makefile.am
+++ b/providers/Makefile.am
@@ -54,7 +54,12 @@ if JAVA
 GDA_JAVA_SERVER=jdbc
 endif
 
+if LIBSOUP
+GDA_WEB_SERVER=web
+endif
+
 SUBDIRS = \
+	reuseable \
 	sqlite \
 	skel-implementation \
 	$(GDA_BDB_SERVER) \
@@ -62,7 +67,8 @@ SUBDIRS = \
 	$(GDA_POSTGRES_SERVER) \
 	$(GDA_MYSQL_SERVER) \
 	$(GDA_JAVA_SERVER) \
-	$(GDA_ORACLE_SERVER)
+	$(GDA_ORACLE_SERVER) \
+	$(GDA_WEB_SERVER)
 #	$(GDA_FREETDS_SERVER) \
 #	$(GDA_IBMDB2_SERVER) \
 #	$(GDA_FIREBIRD_SERVER) \
diff --git a/providers/postgres/Makefile.am b/providers/postgres/Makefile.am
index 52acf67..da6a176 100644
--- a/providers/postgres/Makefile.am
+++ b/providers/postgres/Makefile.am
@@ -5,31 +5,9 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/libgda \
 	-I$(top_builddir) \
+	-I$(top_srcdir)/providers/reuseable/postgres \
 	$(LIBGDA_CFLAGS) $(POSTGRES_CFLAGS) 
 
-keyword_files=keywords_82.list keywords_83.list keywords_84.list
-pkeyword_files=$(addprefix $(top_srcdir)/providers/postgres/,$(keyword_files))
-
-mkkeywordhash$(EXEEXT_FOR_BUILD): $(top_srcdir)/libgda/sqlite/mkkeywordhash.c
-	$(CC_FOR_BUILD) -g -o mkkeywordhash$(EXEEXT_FOR_BUILD) $(GDA_DEBUG_FLAGS) $<
-
-keywords_hash.c: mkkeywordhash$(EXEEXT_FOR_BUILD) $(pkeyword_files)
-	./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/postgres/keywords_82.list V82 > keywords_hash.c
-	./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/postgres/keywords_83.list V83 >> keywords_hash.c
-	./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/postgres/keywords_84.list V84 >> keywords_hash.c
-
-# parser generation
-parser.c parser.h: parser.y $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD)
-	- $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD) -q -d $(srcdir)/parser.y $(top_srcdir)/libgda/sql-parser/lempar.c
-
-gen_def$(EXEEXT_FOR_BUILD): gen_def.c
-	$(CC_FOR_BUILD) -o gen_def$(EXEEXT_FOR_BUILD) -DIMPOSED_HEADER=\""$(top_builddir)/libgda/sql-parser/token_types.h"\" $(srcdir)/gen_def.c
-
-postgres_token_types.h: gen_def$(EXEEXT_FOR_BUILD) parser.h
-	./gen_def$(EXEEXT_FOR_BUILD) > postgres_token_types.h
-
-$(OBJECTS) $(libgda_postgres_la_OBJECTS): postgres_token_types.h keywords_hash.c
-
 libgda_postgres_la_SOURCES = \
 	gda-postgres-blob-op.c \
 	gda-postgres-blob-op.h \
@@ -37,27 +15,21 @@ libgda_postgres_la_SOURCES = \
 	gda-postgres-ddl.h \
 	gda-postgres-handler-bin.c \
 	gda-postgres-handler-bin.h \
-	gda-postgres-parser.c \
-	gda-postgres-parser.h \
 	gda-postgres-provider.c \
 	gda-postgres-provider.h \
 	gda-postgres-pstmt.c \
 	gda-postgres-pstmt.h \
-	gda-postgres-meta.c \
-	gda-postgres-meta.h \
 	gda-postgres-recordset.c \
 	gda-postgres-recordset.h \
 	gda-postgres-util.c \
 	gda-postgres-util.h \
 	gda-postgres.h \
-	libmain.c \
-	parser.h \
-        parser.c \
-        postgres_token_types.h
+	libmain.c
 
 libgda_postgres_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED) $(LIBTOOL_PROV_EXPORT_OPTIONS)
 libgda_postgres_la_LIBADD = \
 	$(top_builddir)/libgda/libgda-4.0.la \
+	$(top_builddir)/providers/reuseable/postgres/libgda-postgres.la \
 	$(LIBGDA_LIBS) $(POSTGRES_LIBS)
 
 xmldir   = $(datadir)/libgda-4.0
@@ -82,8 +54,5 @@ xml_DATA = $(xml_in_files:.xml.in=.xml)
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libgda-postgres-4.0.pc
 
-EXTRA_DIST = $(xml_in_files) libgda-postgres-4.0.pc.in parser.y gen_def.c $(keyword_files)
+EXTRA_DIST = $(xml_in_files) libgda-postgres-4.0.pc.in
 DISTCLEANFILES = $(xml_DATA)
-
-CLEANFILES = parser.h parser.c parser.out postgres_token_types.h gen_def$(EXEEXT_FOR_BUILD) \
-	mkkeywordhash$(EXEEXT_FOR_BUILD) keywords_hash.c
diff --git a/providers/postgres/gda-postgres-provider.c b/providers/postgres/gda-postgres-provider.c
index a58737b..73de91b 100644
--- a/providers/postgres/gda-postgres-provider.c
+++ b/providers/postgres/gda-postgres-provider.c
@@ -392,185 +392,6 @@ pq_notice_processor (PostgresConnectionData *cdata, const char *message)
         gda_connection_add_event (cdata->cnc, error);
 }
 
-static GType
-postgres_name_to_g_type (const gchar *name, const gchar *conv_func_name)
-{
-	/* default built in data types */
-	if (!strcmp (name, "bool"))
-		return G_TYPE_BOOLEAN;
-	else if (!strcmp (name, "int8"))
-		return G_TYPE_INT64;
-	else if (!strcmp (name, "int4") || !strcmp (name, "abstime"))
-		return G_TYPE_INT;
-	else if (!strcmp (name, "int2"))
-		return GDA_TYPE_SHORT;
-	else if (!strcmp (name, "float4"))
-		return G_TYPE_FLOAT;
-	else if (!strcmp (name, "float8"))
-		return G_TYPE_DOUBLE;
-	else if (!strcmp (name, "numeric"))
-		return GDA_TYPE_NUMERIC;
-	else if (!strncmp (name, "timestamp", 9))
-		return GDA_TYPE_TIMESTAMP;
-	else if (!strcmp (name, "date"))
-		return G_TYPE_DATE;
-	else if (!strncmp (name, "time", 4))
-		return GDA_TYPE_TIME;
-	else if (!strcmp (name, "point"))
-		return GDA_TYPE_GEOMETRIC_POINT;
-	else if (!strcmp (name, "oid"))
-		return GDA_TYPE_BLOB;
-	else if (!strcmp (name, "bytea"))
-		return GDA_TYPE_BINARY;
-
-	/* other data types, using the conversion function name as a hint */
-	if (!conv_func_name)
-		return G_TYPE_STRING;
-
-	if (!strncmp (conv_func_name, "int2", 4))
-		return GDA_TYPE_SHORT;
-	if (!strncmp (conv_func_name, "int4", 4))
-		return G_TYPE_INT;
-	if (!strncmp (conv_func_name, "int8", 4))
-		return G_TYPE_INT64;
-	if (!strncmp (conv_func_name, "float4", 6))
-		return G_TYPE_FLOAT;
-	if (!strncmp (conv_func_name, "float8", 6))
-		return G_TYPE_DOUBLE;
-	if (!strncmp (conv_func_name, "timestamp", 9))
-		return GDA_TYPE_TIMESTAMP;
-	if (!strncmp (conv_func_name, "time", 4))
-		return GDA_TYPE_TIME;
-	if (!strncmp (conv_func_name, "date", 4))
-		return G_TYPE_DATE;
-	if (!strncmp (conv_func_name, "bool", 4))
-		return G_TYPE_BOOLEAN;
-	if (!strncmp (conv_func_name, "oid", 3))
-		return GDA_TYPE_BLOB;
-	if (!strncmp (conv_func_name, "bytea", 5))
-		return GDA_TYPE_BINARY;
-	return G_TYPE_STRING;
-}
-
-static int
-get_connection_type_list (PostgresConnectionData *cdata)
-{
-	GHashTable *h_table;
-	GdaPostgresTypeOid *td;
-	PGresult *pg_res, *pg_res_avoid, *pg_res_anyoid = NULL;
-	gint nrows, i;
-	gchar *avoid_types = NULL;
-	GString *string;
-
-	if (cdata->version_float < 7.3) {
-		gchar *query;
-		avoid_types = "'SET', 'cid', 'oid', 'int2vector', 'oidvector', 'regproc', 'smgr', 'tid', 'unknown', 'xid'";
-		/* main query to fetch infos about the data types */
-		query = g_strdup_printf ("SELECT pg_type.oid, typname, usename, obj_description(pg_type.oid) "
-					 "FROM pg_type, pg_user "
-					 "WHERE typowner=usesysid AND typrelid = 0 AND typname !~ '^_' "
-					 "AND  typname not in (%s) "
-					 "ORDER BY typname", avoid_types);
-		pg_res = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
-		g_free (query);
-
-		/* query to fetch non returned data types */
-		query = g_strdup_printf ("SELECT pg_type.oid FROM pg_type WHERE typname in (%s)", avoid_types);
-		pg_res_avoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
-		g_free (query);
-	}
-	else {
-		gchar *query;
-		avoid_types = "'any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid'";
-
-		/* 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 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 !~ '^_' "
-			  "AND (t.typrelid = 0 OR "
-			  "(SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
-			  "AND t.typname not in (%s) "
-			  "ORDER BY typname", avoid_types);
-		pg_res = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
-		g_free (query);
-
-		/* query to fetch non returned data types */
-		query = g_strdup_printf ("SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname in (%s)",
-					 avoid_types);
-		pg_res_avoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn, query);
-		g_free (query);
-
-		/* query to fetch the oid of the 'any' data type */
-		pg_res_anyoid = _gda_postgres_PQexec_wrap (cdata->cnc, cdata->pconn,
-							   "SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname = 'any'");
-	}
-
-	if (!pg_res || (PQresultStatus (pg_res) != PGRES_TUPLES_OK) ||
-	    !pg_res_avoid || (PQresultStatus (pg_res_avoid) != PGRES_TUPLES_OK) ||
-	    ((cdata->version_float >= 7.3) &&
-	     (!pg_res_anyoid || (PQresultStatus (pg_res_anyoid) != PGRES_TUPLES_OK)))) {
-		if (pg_res)
-			PQclear (pg_res);
-		if (pg_res_avoid)
-			PQclear (pg_res_avoid);
-		if (pg_res_anyoid)
-			PQclear (pg_res_anyoid);
-		return -1;
-	}
-
-	/* Data types returned */
-	nrows = PQntuples (pg_res);
-	td = g_new (GdaPostgresTypeOid, nrows);
-	h_table = g_hash_table_new (g_direct_hash, g_direct_equal);
-	if (nrows == 0)
-		g_warning ("PostgreSQL provider did not find any data type (expect some mis-behaviours) please report the error to bugzilla.gnome.org");
-	for (i = 0; i < nrows; i++) {
-		gchar *conv_func_name = NULL;
-		if (PQnfields (pg_res) >= 5)
-			conv_func_name = PQgetvalue (pg_res, i, 4);
-		td[i].name = g_strdup (PQgetvalue (pg_res, i, 1));
-		td[i].oid = atoi (PQgetvalue (pg_res, i, 0));
-		td[i].type = postgres_name_to_g_type (td[i].name, conv_func_name);
-		td[i].comments = g_strdup (PQgetvalue (pg_res, i, 3));
-		td[i].owner = g_strdup (PQgetvalue (pg_res, i, 2));
-		g_hash_table_insert (h_table, GUINT_TO_POINTER (td[i].oid), &(td[i].type));
-	}
-
-	PQclear (pg_res);
-	cdata->ntypes = nrows;
-	cdata->type_data = td;
-	cdata->h_table = h_table;
-
-	/* Make a string of data types internal to postgres and not returned, for future queries */
-	string = NULL;
-	nrows = PQntuples (pg_res_avoid);
-	for (i = 0; i < nrows; i++) {
-		if (!string)
-			string = g_string_new (PQgetvalue (pg_res_avoid, i, 0));
-		else
-			g_string_append_printf (string, ", %s", PQgetvalue (pg_res_avoid, i, 0));
-	}
-	PQclear (pg_res_avoid);
-	cdata->avoid_types = avoid_types;
-	cdata->avoid_types_oids = string->str;
-	g_string_free (string, FALSE);
-
-	/* make a string of the oid of type 'any' */
-	cdata->any_type_oid = g_strdup("");
-	if (pg_res_anyoid) {
-		if (PQntuples (pg_res_anyoid) == 1) {
-			g_free(cdata->any_type_oid);
-			cdata->any_type_oid = g_strdup (PQgetvalue (pg_res_anyoid, 0, 0));
-		}
-		PQclear (pg_res_anyoid);
-	}
-	return 0;
-}
-
 /*
  * Open connection request
  *
@@ -597,7 +418,6 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
 	}
 
 	/* Check for connection parameters */
-	/* TO_ADD: your own connection parameters */
 	const gchar *pq_host;
 	const gchar *pq_db;
         const gchar *pg_searchpath;
@@ -707,9 +527,6 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
 		return FALSE;
 	}
         cdata->version = g_strdup (PQgetvalue(pg_res, 0, 0));
-        cdata->version_float = get_pg_version_float (cdata->version);
-	cdata->short_version = g_strdup_printf ("%d%d", (int) cdata->version_float,
-						(int) ((cdata->version_float - (int) cdata->version_float) * 10));
         PQclear (pg_res);
 
 	/*
@@ -732,10 +549,22 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
 	}
         PQclear (pg_res);
 
+	/* attach connection data */
+	gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_postgres_free_cnc_data);
+
+	/* handle LibPQ's notices */
+        PQsetNoticeProcessor (pconn, (PQnoticeProcessor) pq_notice_processor, cdata);
+
+	/* handle the reuseable part */
+	GdaProviderReuseableOperations *ops;
+	ops = _gda_postgres_reuseable_get_ops ();
+	cdata->reuseable = (GdaPostgresReuseable*) ops->re_new_data (NULL, NULL);
+	_gda_postgres_compute_types (cnc, cdata->reuseable);
+
 	/*
          * Set the search_path
          */
-        if ((cdata->version_float >= 7.3) && pg_searchpath) {
+        if ((cdata->reuseable->version_float >= 7.3) && pg_searchpath) {
                 const gchar *ptr;
                 gboolean path_valid = TRUE;
 
@@ -755,6 +584,7 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
                                 gda_connection_add_event_string (cnc, _("Could not set search_path to %s"), pg_searchpath);
                                 PQclear (pg_res);
 				gda_postgres_free_cnc_data (cdata);
+				gda_connection_internal_set_provider_data (cnc, NULL, NULL);
                                 return FALSE;
                         }
                         PQclear (pg_res);
@@ -762,23 +592,11 @@ gda_postgres_provider_open_connection (GdaServerProvider *provider, GdaConnectio
                 else {
 			gda_connection_add_event_string (cnc, _("Search path %s is invalid"), pg_searchpath);
 			gda_postgres_free_cnc_data (cdata);
+			gda_connection_internal_set_provider_data (cnc, NULL, NULL);
                         return FALSE;
                 }
         }
 
-	/* attach connection data */
-	gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_postgres_free_cnc_data);
-
-
-	/* handle LibPQ's notices */
-        PQsetNoticeProcessor (pconn, (PQnoticeProcessor) pq_notice_processor, cdata);
-
-        if (get_connection_type_list (cdata) != 0) {
-                _gda_postgres_make_error (cnc, pconn, NULL, NULL);
-		gda_postgres_free_cnc_data (cdata);
-                return FALSE;
-        }
-
 	return TRUE;
 }
 
@@ -1150,9 +968,9 @@ gda_postgres_provider_begin_transaction (GdaServerProvider *provider, GdaConnect
         gchar *isolation_level = NULL;
 	GdaStatement *stmt = NULL;
 
-	if (cdata->version_float >= 6.5){
+	if (cdata->reuseable->version_float >= 6.5){
                 if (gda_connection_get_options (cnc) & GDA_CONNECTION_OPTIONS_READ_ONLY) {
-                        if (cdata->version_float >= 7.4)
+                        if (cdata->reuseable->version_float >= 7.4)
                                 write_option = "READ ONLY";
                         else {
 				g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
@@ -1444,7 +1262,7 @@ gda_postgres_provider_supports_feature (GdaServerProvider *provider, GdaConnecti
 			cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
 			if (!cdata)
 				return FALSE;
-                        if (cdata->version_float >= 7.3)
+                        if (cdata->reuseable->version_float >= 7.3)
                                 return TRUE;
                 }
                 else
@@ -2578,7 +2396,8 @@ gda_postgresql_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc
                         return NULL;
         }
 
-        kwfunc = _gda_postgres_get_reserved_keyword_func (cdata);
+        kwfunc = _gda_postgres_reuseable_get_reserved_keywords_func
+		(cdata ? (GdaProviderReuseable*) cdata->reuseable : NULL);
 
 	if (for_meta_store) {
 		gchar *tmp, *ptr;
@@ -2653,23 +2472,13 @@ gda_postgres_free_cnc_data (PostgresConnectionData *cdata)
 	if (cdata->pconn)
                 PQfinish (cdata->pconn);
 
-	if (cdata->type_data) {
-		gint i;
-		for (i = 0; i < cdata->ntypes; i++) {
-			g_free (cdata->type_data[i].name);
-			g_free (cdata->type_data[i].comments);
-			g_free (cdata->type_data[i].owner);
-		}
-		g_free (cdata->type_data);
-	}
-
-	if (cdata->h_table)
-		g_hash_table_destroy (cdata->h_table);
-
 	g_free (cdata->version);
-	g_free (cdata->short_version);
-	g_free (cdata->avoid_types_oids);
-	g_free (cdata->any_type_oid);
+
+	if (cdata->reuseable) {
+		GdaProviderReuseable *rdata = (GdaProviderReuseable*)cdata->reuseable;
+		rdata->operations->re_reset_data (rdata);
+		g_free (cdata->reuseable);
+	}
 
 	g_free (cdata);
 }
diff --git a/providers/postgres/gda-postgres-recordset.c b/providers/postgres/gda-postgres-recordset.c
index 088ebb9..5843988 100644
--- a/providers/postgres/gda-postgres-recordset.c
+++ b/providers/postgres/gda-postgres-recordset.c
@@ -293,7 +293,7 @@ finish_prep_stmt_init (PostgresConnectionData *cdata, GdaPostgresPStmt *ps, PGre
 			postgres_type = PQftype (pg_res, i);
 			gtype = _GDA_PSTMT (ps)->types [i];
 			if (gtype == 0) {
-				gtype = _gda_postgres_type_oid_to_gda (cdata, postgres_type);
+				gtype = _gda_postgres_type_oid_to_gda (cdata->cnc, cdata->reuseable, postgres_type);
 				_GDA_PSTMT (ps)->types [i] = gtype;
 			}
 			_GDA_PSTMT (ps)->types [i] = gtype;
diff --git a/providers/postgres/gda-postgres-util.c b/providers/postgres/gda-postgres-util.c
index 4190177..b558a2e 100644
--- a/providers/postgres/gda-postgres-util.c
+++ b/providers/postgres/gda-postgres-util.c
@@ -25,8 +25,6 @@
 #include <glib/gi18n-lib.h>
 #include "gda-postgres-util.h"
 
-#include <libgda/sqlite/keywords_hash.h>
-#include "keywords_hash.c" /* this one is dynamically generated */
 
 static GdaConnectionEventCode
 gda_postgres_sqlsate_to_gda_code (const gchar *sqlstate)
@@ -125,45 +123,3 @@ _gda_postgres_PQexec_wrap (GdaConnection *cnc, PGconn *pconn, const char *query)
 
         return PQexec (pconn, query);
 }
-
-GType
-_gda_postgres_type_oid_to_gda (PostgresConnectionData *cdata, Oid postgres_type)
-{
-	GType *type;
-	type = g_hash_table_lookup (cdata->h_table, GUINT_TO_POINTER (postgres_type));
-	if (type)
-		return *type;
-	else
-		return G_TYPE_STRING;
-}
-
-#ifdef GDA_DEBUG
-void
-_gda_postgres_test_keywords (void)
-{
-        V82test_keywords();
-        V83test_keywords();
-        V84test_keywords();
-}
-#endif
-
-GdaSqlReservedKeywordsFunc
-_gda_postgres_get_reserved_keyword_func (PostgresConnectionData *cdata)
-{
-	if (cdata && cdata->short_version) {
-		switch (*cdata->short_version) {
-                case '8':
-                        if (cdata->short_version[1] == '2')
-                                return V82is_keyword;
-                        if (cdata->short_version[1] == '3')
-                                return V83is_keyword;
-                        if (cdata->short_version[1] == '4')
-                                return V84is_keyword;
-                        return V84is_keyword;
-                default:
-                        return V84is_keyword;
-                break;
-                }
-	}
-        return V84is_keyword;
-}
diff --git a/providers/postgres/gda-postgres-util.h b/providers/postgres/gda-postgres-util.h
index e961f12..1ecd1f9 100644
--- a/providers/postgres/gda-postgres-util.h
+++ b/providers/postgres/gda-postgres-util.h
@@ -1,5 +1,5 @@
 /* GDA postgres provider
- * Copyright (C) 1998 - 2008 The GNOME Foundation.
+ * Copyright (C) 1998 - 2009 The GNOME Foundation.
  *
  * AUTHORS:
  *         Vivien Malerba <malerba gnome-db org>
@@ -32,12 +32,6 @@ GdaConnectionEvent *_gda_postgres_make_error               (GdaConnection *cnc,
 PGresult           *_gda_postgres_PQexec_wrap              (GdaConnection *cnc, PGconn *pconn, const char *query);
 
 int                 _gda_postgres_get_connection_type_list (GdaConnection *cnc, PostgresConnectionData *cdata);
-GType               _gda_postgres_type_oid_to_gda          (PostgresConnectionData *cdata, Oid postgres_type);
-
-#ifdef GDA_DEBUG
-void                _gda_postgres_test_keywords (void);
-#endif
-GdaSqlReservedKeywordsFunc _gda_postgres_get_reserved_keyword_func (PostgresConnectionData *cdata);
 
 G_END_DECLS
 
diff --git a/providers/postgres/gda-postgres.h b/providers/postgres/gda-postgres.h
index dbaf7d6..fc70a11 100644
--- a/providers/postgres/gda-postgres.h
+++ b/providers/postgres/gda-postgres.h
@@ -33,6 +33,7 @@
 #include <libgda/libgda.h>
 #include <libpq-fe.h>
 #include <libpq/libpq-fs.h>
+#include <gda-postgres-reuseable.h>
 
 /*
  * Postgres type identification
@@ -49,22 +50,14 @@ typedef struct {
  * Provider's specific connection data
  */
 typedef struct {
-	GdaConnection      *cnc;
-        PGconn             *pconn;
-	gboolean            pconn_is_busy;
-        gint                ntypes;
-        GdaPostgresTypeOid *type_data;
-        GHashTable         *h_table;
+	GdaPostgresReuseable *reuseable;
+	GdaConnection        *cnc;
+        PGconn               *pconn;
+	gboolean              pconn_is_busy;
 
         /* Version of the backend to which we are connected */
-        gchar              *version;
-        gfloat              version_float;
-        gchar              *short_version;
-
-        /* Internal data types not returned */
-        gchar              *avoid_types;
-        gchar              *avoid_types_oids;
-        gchar              *any_type_oid; /* oid for the 'any' data type, used to fetch aggregates and functions */
+        gchar                *version;
+        gchar                *short_version;
 } PostgresConnectionData;
 
 #endif
diff --git a/providers/reuseable/Makefile.am b/providers/reuseable/Makefile.am
new file mode 100644
index 0000000..6ce70fe
--- /dev/null
+++ b/providers/reuseable/Makefile.am
@@ -0,0 +1,19 @@
+noinst_LTLIBRARIES = libreuseable.la
+
+SUBDIRS = \
+	postgres
+
+AM_CPPFLAGS = \
+        -I$(top_srcdir) \
+        -I$(top_srcdir)/libgda \
+        -I$(top_srcdir)/libgda/sqlite \
+        -I$(top_builddir) \
+        $(LIBGDA_CFLAGS)
+
+libreuseable_la_SOURCES = \
+	gda-provider-reuseable.h \
+	reuse-all.c \
+	reuse-all.h
+
+libreuseable_la_LIBADD = \
+	postgres/libgda-postgres.la
diff --git a/providers/reuseable/gda-provider-reuseable.h b/providers/reuseable/gda-provider-reuseable.h
new file mode 100644
index 0000000..e279a43
--- /dev/null
+++ b/providers/reuseable/gda-provider-reuseable.h
@@ -0,0 +1,69 @@
+/* GDA 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_PROVIDER_REUSEABLE_H__
+#define __GDA_PROVIDER_REUSEABLE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libgda/gda-decl.h>
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdaProviderReuseable GdaProviderReuseable;
+
+typedef GdaProviderReuseable *(*GdaProviderReuseable_NewDataFunc) (const gchar *major, const gchar *minor);
+typedef void (*GdaProviderReuseable_ResetDataFunc) (GdaProviderReuseable *rdata);
+typedef GType (*GdaProviderReuseable_GetGTypeFunc) (GdaConnection *cnc, GdaProviderReuseable *rdata, const gchar *db_type);
+typedef GdaSqlReservedKeywordsFunc (*GdaProviderReuseable_GetReservedKeywordsFunc) (GdaProviderReuseable *rdata);
+typedef GdaSqlParser *(*GdaProviderReuseable_CreateParserFunc) (GdaProviderReuseable *rdata);
+
+/**
+ * GdaProviderReuseable: Reuseable provider part
+ *
+ * Defines the interface which a database provider has to implement to be reused
+ */
+typedef struct {
+	GdaProviderReuseable_NewDataFunc             re_new_data;
+	GdaProviderReuseable_ResetDataFunc           re_reset_data;
+
+	GdaProviderReuseable_GetGTypeFunc            re_get_type; /* Returns GDA_TYPE_NULL if conversion failed */
+	GdaProviderReuseable_GetReservedKeywordsFunc re_get_reserved_keyword_func;
+
+	GdaProviderReuseable_CreateParserFunc        re_create_parser;
+	GdaServerProviderMeta                        re_meta_funcs;
+} GdaProviderReuseableOperations;
+
+/*
+ * Declaration for provider's abstract operations
+ */
+struct _GdaProviderReuseable {
+	GdaProviderReuseableOperations *operations;
+	gchar                          *version_major;
+	gchar                          *version_minor;
+};
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/reuseable/postgres/Makefile.am b/providers/reuseable/postgres/Makefile.am
new file mode 100644
index 0000000..88e3132
--- /dev/null
+++ b/providers/reuseable/postgres/Makefile.am
@@ -0,0 +1,49 @@
+noinst_LTLIBRARIES = libgda-postgres.la
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libgda \
+	-I$(top_builddir) \
+	$(LIBGDA_CFLAGS) $(POSTGRES_CFLAGS) 
+
+keyword_files=keywords_82.list keywords_83.list keywords_84.list
+pkeyword_files=$(addprefix $(top_srcdir)/providers/reuseable/postgres/,$(keyword_files))
+
+mkkeywordhash$(EXEEXT_FOR_BUILD): $(top_srcdir)/libgda/sqlite/mkkeywordhash.c
+	$(CC_FOR_BUILD) -g -o mkkeywordhash$(EXEEXT_FOR_BUILD) $(GDA_DEBUG_FLAGS) $<
+
+keywords_hash.c: mkkeywordhash$(EXEEXT_FOR_BUILD) $(pkeyword_files)
+	./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/reuseable/postgres/keywords_82.list V82 > keywords_hash.c
+	./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/reuseable/postgres/keywords_83.list V83 >> keywords_hash.c
+	./mkkeywordhash$(EXEEXT_FOR_BUILD) $(top_srcdir)/providers/reuseable/postgres/keywords_84.list V84 >> keywords_hash.c
+
+# parser generation
+parser.c parser.h: parser.y $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD)
+	- $(top_builddir)/libgda/sql-parser/lemon$(EXEEXT_FOR_BUILD) -q -d $(srcdir)/parser.y $(top_srcdir)/libgda/sql-parser/lempar.c
+
+gen_def$(EXEEXT_FOR_BUILD): gen_def.c
+	$(CC_FOR_BUILD) -o gen_def$(EXEEXT_FOR_BUILD) -DIMPOSED_HEADER=\""$(top_builddir)/libgda/sql-parser/token_types.h"\" $(srcdir)/gen_def.c
+
+postgres_token_types.h: gen_def$(EXEEXT_FOR_BUILD) parser.h
+	./gen_def$(EXEEXT_FOR_BUILD) > postgres_token_types.h
+
+$(OBJECTS) $(libgda_postgres_la_OBJECTS): postgres_token_types.h keywords_hash.c
+
+libgda_postgres_la_SOURCES = \
+	gda-postgres-reuseable.c \
+	gda-postgres-reuseable.h \
+	gda-postgres-parser.c \
+	gda-postgres-parser.h \
+	gda-postgres-meta.c \
+	gda-postgres-meta.h \
+	parser.h \
+        parser.c \
+        postgres_token_types.h
+
+libgda_postgres_la_LIBADD = \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(LIBGDA_LIBS) $(POSTGRES_LIBS)
+
+EXTRA_DIST = parser.y gen_def.c $(keyword_files)
+CLEANFILES = parser.h parser.c parser.out postgres_token_types.h gen_def$(EXEEXT_FOR_BUILD) \
+	mkkeywordhash$(EXEEXT_FOR_BUILD) keywords_hash.c
diff --git a/providers/reuseable/postgres/gda-postgres-meta.c b/providers/reuseable/postgres/gda-postgres-meta.c
new file mode 100644
index 0000000..eee8196
--- /dev/null
+++ b/providers/reuseable/postgres/gda-postgres-meta.c
@@ -0,0 +1,2042 @@
+/* GDA postgres provider
+ * Copyright (C) 2008 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-postgres-meta.h"
+#include <libgda/libgda.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/gda-connection-private.h>
+#include <libgda/providers-support/gda-meta-column-types.h>
+
+#include "gda-postgres-reuseable.h"
+#include "gda-postgres-parser.h"
+
+/*
+ * predefined statements' IDs
+ */
+typedef enum {
+        I_STMT_CATALOG,
+	I_STMT_BTYPES,
+	I_STMT_SCHEMAS,
+	I_STMT_SCHEMAS_ALL,
+	I_STMT_SCHEMA_NAMED,
+	I_STMT_TABLES,
+	I_STMT_TABLES_ALL,
+	I_STMT_TABLE_NAMED,
+	I_STMT_VIEWS,
+	I_STMT_VIEWS_ALL,
+	I_STMT_VIEW_NAMED,
+	I_STMT_COLUMNS_OF_TABLE,
+	I_STMT_COLUMNS_ALL,
+	I_STMT_TABLES_CONSTRAINTS,
+	I_STMT_TABLES_CONSTRAINTS_ALL,
+	I_STMT_TABLES_CONSTRAINT_NAMED,
+	I_STMT_REF_CONSTRAINTS,
+	I_STMT_REF_CONSTRAINTS_ALL,
+	I_STMT_KEY_COLUMN_USAGE,
+	I_STMT_KEY_COLUMN_USAGE_ALL,
+	I_STMT_CHECK_COLUMN_USAGE,
+	I_STMT_CHECK_COLUMN_USAGE_ALL,
+	I_STMT_UDT,
+	I_STMT_UDT_ALL,
+	I_STMT_UDT_COLUMNS,
+	I_STMT_UDT_COLUMNS_ALL,
+	I_STMT_DOMAINS,
+	I_STMT_DOMAINS_ALL,
+	I_STMT_DOMAINS_CONSTRAINTS,
+	I_STMT_DOMAINS_CONSTRAINTS_ALL,
+	I_STMT_VIEWS_COLUMNS,
+	I_STMT_VIEWS_COLUMNS_ALL,
+	I_STMT_TRIGGERS,
+	I_STMT_TRIGGERS_ALL,
+	I_STMT_EL_TYPES_COL,
+	I_STMT_EL_TYPES_DOM,
+	I_STMT_EL_TYPES_UDT,
+	I_STMT_EL_TYPES_ROUT_PAR,
+	I_STMT_EL_TYPES_ROUT_COL,
+	I_STMT_EL_TYPES_ALL,
+	I_STMT_ROUTINES_ALL,
+	I_STMT_ROUTINES,
+	I_STMT_ROUTINES_ONE,
+	I_STMT_ROUTINE_PAR_ALL,
+	I_STMT_ROUTINE_PAR,
+	I_STMT_ROUTINE_COL_ALL,
+	I_STMT_ROUTINE_COL
+} InternalStatementItem;
+
+
+/*
+ * predefined statements' SQL
+ */
+static gchar *internal_sql[] = {
+	/* I_STMT_CATALOG */
+	"SELECT pg_catalog.current_database()",
+
+	/* I_STMT_BTYPES */
+	"SELECT t.typname, 'pg_catalog.' || t.typname, 'gchararray', pg_catalog.obj_description(t.oid), NULL, CASE WHEN t.typname ~ '^_' THEN TRUE WHEN typtype = 'p' THEN TRUE WHEN t.typname in ('any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid', 'oid', 'aclitem') THEN TRUE ELSE FALSE END, CAST (t.oid AS varchar) 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 AND pg_catalog.pg_type_is_visible(t.oid) AND (typtype='b' OR typtype='p') AND typelem = 0",
+
+	/* I_STMT_SCHEMAS */
+	"SELECT current_database() AS catalog_name, n.nspname AS schema_name, u.rolname AS schema_owner, CASE WHEN n.nspname ~ '^pg_' THEN TRUE WHEN n.nspname = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid AND current_database() = ##cat::string",
+
+	/* I_STMT_SCHEMAS_ALL */
+	"SELECT current_database() AS catalog_name, n.nspname AS schema_name, u.rolname AS schema_owner, CASE WHEN n.nspname ~ '^pg_' THEN TRUE WHEN n.nspname = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid",
+
+	/* I_STMT_SCHEMA_NAMED */
+	"SELECT current_database() AS catalog_name, n.nspname AS schema_name, u.rolname AS schema_owner, CASE WHEN n.nspname ~ '^pg_' THEN TRUE WHEN n.nspname = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid AND current_database() = ##cat::string AND n.nspname = ##name::string",
+
+	/* I_STMT_TABLES */
+	"SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY' WHEN c.relkind = 'r' THEN 'BASE TABLE' WHEN c.relkind = 'v' THEN 'VIEW' ELSE NULL END AS table_type, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.obj_description(c.oid), CASE WHEN pg_catalog.pg_table_is_visible(c.oid) IS TRUE AND nc.nspname!='pg_catalog' THEN c.relname ELSE coalesce (nc.nspname || '.', '') || c.relname END, coalesce (nc.nspname || '.', '') || c.relname, o.rolname FROM pg_namespace nc, pg_class c, pg_roles o WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND (c.relkind = ANY (ARRAY['r', 'v'])) AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') O
 R has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER')) AND o.oid=c.relowner",
+
+	/* I_STMT_TABLES_ALL */
+	"SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY' WHEN c.relkind = 'r' THEN 'BASE TABLE' WHEN c.relkind = 'v' THEN 'VIEW' ELSE NULL END AS table_type, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.obj_description(c.oid), CASE WHEN pg_catalog.pg_table_is_visible(c.oid) IS TRUE AND nc.nspname!='pg_catalog' THEN c.relname ELSE coalesce (nc.nspname || '.', '') || c.relname END, coalesce (nc.nspname || '.', '') || c.relname, o.rolname FROM pg_namespace nc, pg_class c, pg_roles o WHERE c.relnamespace = nc.oid AND (c.relkind = ANY (ARRAY['r', 'v'])) AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 
 'TRIGGER')) AND o.oid=c.relowner",
+
+	/* I_STMT_TABLE_NAMED */
+	"SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY' WHEN c.relkind = 'r' THEN 'BASE TABLE' WHEN c.relkind = 'v' THEN 'VIEW' ELSE NULL END AS table_type, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.obj_description(c.oid), CASE WHEN pg_catalog.pg_table_is_visible(c.oid) IS TRUE AND nc.nspname!='pg_catalog' THEN c.relname ELSE coalesce (nc.nspname || '.', '') || c.relname END, coalesce (nc.nspname || '.', '') || c.relname, o.rolname FROM pg_namespace nc, pg_class c, pg_roles o WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND (c.relkind = ANY (ARRAY['r', 'v'])) AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') O
 R has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER')) AND o.oid=c.relowner AND c.relname = ##name::string",
+
+	/* I_STMT_VIEWS */
+	"SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, pg_catalog.pg_get_viewdef(c.oid, TRUE), NULL, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END FROM pg_namespace nc, pg_class c WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND c.relkind = 'v' AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER'))",
+
+	/* I_STMT_VIEWS_ALL */
+	"SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, pg_catalog.pg_get_viewdef(c.oid, TRUE), NULL, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END FROM pg_namespace nc, pg_class c WHERE c.relnamespace = nc.oid AND c.relkind = 'v' AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER'))",
+
+	/* I_STMT_VIEW_NAMED */
+	"SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, pg_catalog.pg_get_viewdef(c.oid, TRUE), NULL, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END FROM pg_namespace nc, pg_class c WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relnamespace = nc.oid AND c.relkind = 'v' AND NOT pg_is_other_temp_schema(nc.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TRIGGER')) AND c.relname = ##name::string",
+
+	/* I_STMT_COLUMNS_OF_TABLE */
+	"SELECT current_database(), nc.nspname, c.relname, a.attname, a.attnum, pg_get_expr(ad.adbin, ad.adrelid), CASE WHEN a.attnotnull OR t.typtype = 'd' AND t.typnotnull THEN FALSE ELSE TRUE END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), info
 rmation_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), NULL, NULL, NULL, NULL, NULL, NULL, CASE WHEN pg_get_expr(ad.adbin, ad.adrelid) LIKE 'nextval(%' THEN '" GDA_EXTRA_AUTO_INCREMENT "' ELSE NULL END, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.col_description(c.oid, a.attnum), CAST (t.oid AS varchar) FROM pg_attribute a LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd' AND t.typbasetype = bt.oid WHERE current_database() = ##cat::string AND nc.nspname = ##schema::string AND c.relname = ##name::string AND a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['
 r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES'))",
+
+	/* I_STMT_COLUMNS_ALL */
+	"SELECT current_database(), nc.nspname, c.relname, a.attname, a.attnum, pg_get_expr(ad.adbin, ad.adrelid), CASE WHEN a.attnotnull OR t.typtype = 'd' AND t.typnotnull THEN FALSE ELSE TRUE END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), info
 rmation_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), NULL, NULL, NULL, NULL, NULL, NULL, CASE WHEN pg_get_expr(ad.adbin, ad.adrelid) LIKE 'nextval(%' THEN '" GDA_EXTRA_AUTO_INCREMENT "' ELSE NULL END, CASE WHEN c.relkind = 'r' THEN TRUE ELSE FALSE END, pg_catalog.col_description(c.oid, a.attnum), CAST (t.oid AS varchar) FROM pg_attribute a LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd' AND t.typbasetype = bt.oid WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_pr
 ivilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES'))",
+
+	/* I_STMT_TABLES_CONSTRAINTS */
+	"SELECT current_database() AS constraint_catalog, nc.nspname AS constraint_schema, c.conname AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, CASE c.contype WHEN 'c' THEN 'CHECK' WHEN 'f' THEN 'FOREIGN KEY' WHEN 'p' THEN 'PRIMARY KEY' WHEN 'u' THEN 'UNIQUE' ELSE NULL END AS constraint_type, CASE c.contype WHEN 'c' THEN c.consrc ELSE NULL END, CASE WHEN c.condeferrable THEN TRUE ELSE FALSE END AS is_deferrable, CASE WHEN c.condeferred THEN TRUE ELSE FALSE END AS initially_deferred FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'T
 RIGGER')) AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string "
+	"UNION SELECT current_database() AS constraint_catalog, nr.nspname AS constraint_schema, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, 'CHECK' AS constraint_type, a.attname || ' IS NOT NULL', FALSE AS is_deferrable, FALSE AS initially_deferred FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string",
+
+	/* I_STMT_TABLES_CONSTRAINTS_ALL */
+	"SELECT current_database() AS constraint_catalog, nc.nspname AS constraint_schema, c.conname AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, CASE c.contype WHEN 'c' THEN 'CHECK' WHEN 'f' THEN 'FOREIGN KEY' WHEN 'p' THEN 'PRIMARY KEY' WHEN 'u' THEN 'UNIQUE' ELSE NULL END AS constraint_type, CASE c.contype WHEN 'c' THEN c.consrc ELSE NULL END, CASE WHEN c.condeferrable THEN TRUE ELSE FALSE END AS is_deferrable, CASE WHEN c.condeferred THEN TRUE ELSE FALSE END AS initially_deferred FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'T
 RIGGER')) "
+	"UNION SELECT current_database() AS constraint_catalog, nr.nspname AS constraint_schema, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, 'CHECK' AS constraint_type, a.attname || ' IS NOT NULL', FALSE AS is_deferrable, FALSE AS initially_deferred FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER'))",
+
+	/* I_STMT_TABLES_CONSTRAINT_NAMED */
+	"SELECT constraint_catalog, constraint_schema, constraint_name, table_catalog, table_schema, table_name, constraint_type, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.table_constraints WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+
+	/* I_STMT_REF_CONSTRAINTS */
+	"SELECT current_database(), nt.nspname, t.relname, c.conname, current_database(), nref.nspname, ref.relname, pkc.conname, CASE c.confmatchtype WHEN 'f' THEN 'FULL' WHEN 'p' THEN 'PARTIAL' WHEN 'u' THEN 'NONE' ELSE NULL END AS match_option, CASE c.confupdtype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS update_rule, CASE c.confdeltype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS delete_rule FROM pg_constraint c INNER JOIN pg_class t ON (c.conrelid=t.oid) INNER JOIN pg_namespace nt ON (nt.oid=t.relnamespace) INNER JOIN pg_class ref ON (c.confrelid=ref.oid) INNER JOIN pg_namespace nref ON (nref.oid=ref.relnamespace) INNER JOIN pg_constraint pkc ON (c.confrelid = pkc.conrelid AND information_schema._pg_keysequal(c.confkey, pkc.conkey) AND pkc.contype='p') WHERE c.contype = 'f' AND 
 current_database() = ##cat::string AND nt.nspname = ##schema::string AND t.relname = ##name::string AND c.conname = ##name2::string",
+
+	/* I_STMT_REF_CONSTRAINTS_ALL */
+	"SELECT current_database(), nt.nspname, t.relname, c.conname, current_database(), nref.nspname, ref.relname, pkc.conname, CASE c.confmatchtype WHEN 'f' THEN 'FULL' WHEN 'p' THEN 'PARTIAL' WHEN 'u' THEN 'NONE' ELSE NULL END AS match_option, CASE c.confupdtype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS update_rule, CASE c.confdeltype WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' WHEN 'r' THEN 'RESTRICT' WHEN 'a' THEN 'NO ACTION' ELSE NULL END AS delete_rule FROM pg_constraint c INNER JOIN pg_class t ON (c.conrelid=t.oid) INNER JOIN pg_namespace nt ON (nt.oid=t.relnamespace) INNER JOIN pg_class ref ON (c.confrelid=ref.oid) INNER JOIN pg_namespace nref ON (nref.oid=ref.relnamespace) INNER JOIN pg_constraint pkc ON (c.confrelid = pkc.conrelid AND information_schema._pg_keysequal(c.confkey, pkc.conkey) AND pkc.contype='p') WHERE c.contype = 'f'",
+
+	/* I_STMT_KEY_COLUMN_USAGE */
+	"SELECT table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM information_schema.key_column_usage WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
+
+	/* I_STMT_KEY_COLUMN_USAGE_ALL */
+	"SELECT table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM information_schema.key_column_usage",
+
+	/* I_STMT_CHECK_COLUMN_USAGE */
+	"SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, c.conname AS constraint_name,a.attname FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r, pg_attribute a, (SELECT sc.oid, information_schema._pg_expandarray (sc.conkey) as x FROM pg_constraint sc WHERE sc.contype = 'c') ss WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND c.contype = 'c' AND ss.oid = c.oid AND a.attrelid = r.oid AND a.attnum = (ss.x).x AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string AND c.conname = ##name2::string "
+	"UNION SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, a.attname FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND current_database() = ##cat::string AND nr.nspname = ##schema::string AND r.relname = ##name::string AND (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') = ##name2::string",
+
+	/* I_STMT_CHECK_COLUMN_USAGE_ALL */
+	"SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, c.conname AS constraint_name,a.attname FROM pg_namespace nc, pg_namespace nr, pg_constraint c, pg_class r, pg_attribute a, (SELECT sc.oid, information_schema._pg_expandarray (sc.conkey) as x FROM pg_constraint sc WHERE sc.contype = 'c') ss WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace AND c.conrelid = r.oid AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER')) AND c.contype = 'c' AND ss.oid = c.oid AND a.attrelid = r.oid AND a.attnum = (ss.x).x "
+	"UNION SELECT current_database() AS table_catalog, nr.nspname AS table_schema, r.relname AS table_name, (((((nr.oid || '_') || r.oid) || '_') || a.attnum) || '_not_null') AS constraint_name, a.attname FROM pg_namespace nr, pg_class r, pg_attribute a WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND a.attnotnull AND a.attnum > 0 AND NOT a.attisdropped AND r.relkind = 'r' AND NOT pg_is_other_temp_schema(nr.oid) AND (pg_has_role(r.relowner, 'USAGE') OR has_table_privilege(r.oid, 'SELECT') OR has_table_privilege(r.oid, 'INSERT') OR has_table_privilege(r.oid, 'UPDATE') OR has_table_privilege(r.oid, 'DELETE') OR has_table_privilege(r.oid, 'REFERENCES') OR has_table_privilege(r.oid, 'TRIGGER'))",
+
+	/* I_STMT_UDT */
+	"SELECT pg_catalog.current_database() as cat, n.nspname, t.typname, 'gchararray', pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (n.nspname || '.', '') || t.typname END, coalesce (n.nspname || '.', '') || t.typname, CASE WHEN t.typname ~ '^_' THEN TRUE WHEN t.typname in ('any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid', 'oid', 'aclitem') THEN TRUE ELSE FALSE END, o.rolname FROM pg_catalog.pg_type t, pg_catalog.pg_user u, pg_catalog.pg_namespace n , pg_roles o WHERE t.typowner=u.usesysid AND n.oid = t.typnamespace AND pg_catalog.pg_type_is_visible(t.oid) AND (t.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) AND o.oid=t.typowner AND 
 pg_catalog.current_database() = ##cat::string AND n.nspname = ##schema::string",
+
+	/* I_STMT_UDT_ALL */
+	"SELECT pg_catalog.current_database(), n.nspname, t.typname, 'gchararray', pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (n.nspname || '.', '') || t.typname END, coalesce (n.nspname || '.', '') || t.typname, CASE WHEN t.typname ~ '^_' THEN TRUE WHEN t.typname in ('any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid', 'oid', 'aclitem') THEN TRUE ELSE FALSE END, o.rolname FROM pg_catalog.pg_type t, pg_catalog.pg_user u, pg_catalog.pg_namespace n , pg_roles o WHERE t.typowner=u.usesysid AND n.oid = t.typnamespace AND pg_catalog.pg_type_is_visible(t.oid) AND (t.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) AND o.oid=t.typowner",
+
+	/* I_STMT_UDT_COLUMNS */
+	"select pg_catalog.current_database(), n.nspname, udt.typname, a.attname, a.attnum, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum ELSE NULL END, information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, 
 t.*)), NULL, NULL , NULL, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid) WHERE udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid) AND pg_catalog.current_database() = ##cat::string AND n.nspname = ##schema::string AND udt.typname = ##name::string",
+
+	/* I_STMT_UDT_COLUMNS_ALL */
+	"select pg_catalog.current_database(), n.nspname, udt.typname, a.attname, a.attnum, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum ELSE NULL END, information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*)), information_schema._pg_datetime_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, 
 t.*)), NULL, NULL , NULL, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid) WHERE udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid)",
+
+	/* I_STMT_DOMAINS */
+	"SELECT pg_catalog.current_database(), nt.nspname, t.typname, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nbt.nspname || '.', '') || bt.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(t.typbasetype, t.typtypmod), information_schema._pg_char_octet_length(t.typbasetype, t.typtypmod),  NULL, NULL, NULL, NULL, NULL, NULL,  information_schema._pg_numeric_precision(t.typbasetype, t.typtypmod), information_schema._pg_numeric_scale(t.typbasetype, t.typtypmod), t.typdefault, pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (nt.nspname || '.', '') || t.typname END, coalesce (nt.nspname || '.', '') || t.typname, FALSE, o.rolname FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt, pg_roles o WHERE t.typnamespace = nt.oid AND t.typbaset
 ype = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND o.oid=t.typowner AND pg_catalog.current_database() = ##cat::string AND nt.nspname = ##schema::string", 
+
+	/* I_STMT_DOMAINS_ALL */
+	"SELECT pg_catalog.current_database(), nt.nspname, t.typname, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN NULL ELSE coalesce (nbt.nspname || '.', '') || bt.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname ELSE NULL END, 'gchararray', information_schema._pg_char_max_length(t.typbasetype, t.typtypmod), information_schema._pg_char_octet_length(t.typbasetype, t.typtypmod),  NULL, NULL, NULL, NULL, NULL, NULL,  information_schema._pg_numeric_precision(t.typbasetype, t.typtypmod), information_schema._pg_numeric_scale(t.typbasetype, t.typtypmod), t.typdefault, pg_catalog.obj_description(t.oid), CASE WHEN pg_catalog.pg_type_is_visible(t.oid) IS TRUE THEN t.typname ELSE coalesce (nt.nspname || '.', '') || t.typname END, coalesce (nt.nspname || '.', '') || t.typname, FALSE, o.rolname FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt, pg_roles o WHERE t.typnamespace = nt.oid AND t.typbaset
 ype = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND o.oid=t.typowner",
+
+	/* I_STMT_DOMAINS_CONSTRAINTS */
+	"SELECT constraint_catalog, constraint_schema, constraint_name, domain_catalog, domain_schema, domain_name, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.domain_constraints WHERE domain_catalog = ##cat::string AND domain_schema = ##schema::string AND domain_name = ##name::string",
+
+	/* I_STMT_DOMAINS_CONSTRAINTS_ALL */
+	"SELECT constraint_catalog, constraint_schema, constraint_name, domain_catalog, domain_schema, domain_name, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.domain_constraints",
+
+	/* I_STMT_VIEWS_COLUMNS */
+	"SELECT view_catalog, view_schema, view_name, table_catalog, table_schema, table_name, column_name FROM information_schema.view_column_usage WHERE view_catalog = ##cat::string AND view_schema = ##schema::string AND view_name = ##name::string",
+
+	/* I_STMT_VIEWS_COLUMNS_ALL */
+	"SELECT view_catalog, view_schema, view_name, table_catalog, table_schema, table_name, column_name FROM information_schema.view_column_usage",
+
+	/* I_STMT_TRIGGERS */
+	"SELECT current_database(), n.nspname, t.tgname, em.text, current_database(), n.nspname, c.relname, \"substring\"(pg_get_triggerdef(t.oid), \"position\"(\"substring\"(pg_get_triggerdef(t.oid), 48), 'EXECUTE PROCEDURE') + 47) AS action_statement, CASE WHEN (t.tgtype & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS action_orientation, CASE WHEN (t.tgtype & 2) = 2 THEN 'BEFORE' ELSE 'AFTER' END AS condition_timing, pg_catalog.obj_description(t.oid), t.tgname, t.tgname FROM pg_namespace n, pg_class c, pg_trigger t, (( SELECT 4, 'INSERT' UNION ALL SELECT 8, 'DELETE') UNION ALL SELECT 16, 'UPDATE') em(num, text) WHERE n.oid = c.relnamespace AND c.oid = t.tgrelid AND (t.tgtype & em.num) <> 0 AND NOT t.tgisconstraint AND NOT pg_is_other_temp_schema(n.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TR
 IGGER')) AND current_database() = ##cat::string AND n.nspname = ##schema::string AND c.relname = ##name::string",
+	
+	/* I_STMT_TRIGGERS_ALL */
+	"SELECT current_database(), n.nspname, t.tgname, em.text, current_database(), n.nspname, c.relname, \"substring\"(pg_get_triggerdef(t.oid), \"position\"(\"substring\"(pg_get_triggerdef(t.oid), 48), 'EXECUTE PROCEDURE') + 47) AS action_statement, CASE WHEN (t.tgtype & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS action_orientation, CASE WHEN (t.tgtype & 2) = 2 THEN 'BEFORE' ELSE 'AFTER' END AS condition_timing, pg_catalog.obj_description(t.oid), t.tgname, t.tgname FROM pg_namespace n, pg_class c, pg_trigger t, (( SELECT 4, 'INSERT' UNION ALL SELECT 8, 'DELETE') UNION ALL SELECT 16, 'UPDATE') em(num, text) WHERE n.oid = c.relnamespace AND c.oid = t.tgrelid AND (t.tgtype & em.num) <> 0 AND NOT t.tgisconstraint AND NOT pg_is_other_temp_schema(n.oid) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'DELETE') OR has_table_privilege(c.oid, 'REFERENCES') OR has_table_privilege(c.oid, 'TR
 IGGER'))",
+
+	/* I_STMT_EL_TYPES_COL */
+	"SELECT 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum, current_database(), nc.nspname, c.relname, 'TABLE_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_attribute a, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typelem = bt.oid WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES')) AND t.typelem <> 0 AND t.typlen = -1 AND 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum = ##name::string",
+
+	/* I_STMT_EL_TYPES_DOM */
+	"SELECT 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname, current_database(), nt.nspname, t.typname, 'DOMAIN', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt WHERE t.typnamespace = nt.oid AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND t.typlen = -1 AND 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname = ##name::string",
+
+	/* I_STMT_EL_TYPES_UDT */
+	"SELECT 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum, pg_catalog.current_database(), n.nspname, udt.typname, 'UDT_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid), pg_type bt, pg_namespace nbt where udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid) AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typlen = -1 AND 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum = ##name::string",
+
+	/* I_STMT_EL_TYPES_ROUT_PAR */
+	"SELECT 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_PAR', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_type bt, pg_namespace nbt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND bt.oid= t.typelem AND bt.typnamespace = nbt.oid AND t.typelem <> 0 AND t.typlen = -1 AND 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n = ##name::string", 
+
+	/* I_STMT_EL_TYPES_ROUT_COL */
+	"SELECT 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_COL', CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'array_spec' ELSE coalesce (ant.nspname || '.', '') || at.typname END, CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'ARR' || at.typelem ELSE NULL END, NULL, NULL FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss, pg_type at, pg_namespace ant WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND at.oid = t.typelem AND at.typnamespace = ant.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n] = 'b'
 ) AND 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n = ##name::string",
+
+	/* I_STMT_EL_TYPES_ALL */
+	"SELECT 'UDT' || current_database() || '.' || n.nspname || '.' || udt.typname || '.' || a.attnum, pg_catalog.current_database(), n.nspname, udt.typname, 'UDT_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type udt INNER JOIN pg_namespace n ON (udt.typnamespace=n.oid) INNER JOIN pg_attribute a ON (a.attrelid=udt.typrelid) INNER JOIN pg_type t ON (a.atttypid=t.oid) INNER JOIN pg_namespace nt ON (t.typnamespace = nt.oid), pg_type bt, pg_namespace nbt where udt.typrelid != 0 AND (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = udt.typrelid) AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typlen = -1 "
+	"UNION SELECT 'DOM' || current_database() || '.' || nt.nspname || '.' || t.typname, current_database(), nt.nspname, t.typname, 'DOMAIN', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_namespace nt, pg_type bt, pg_namespace nbt WHERE t.typnamespace = nt.oid AND t.typelem = bt.oid AND bt.typnamespace = nbt.oid AND t.typtype = 'd' AND t.typlen = -1 "
+	"UNION SELECT 'COL' || current_database() || '.' || nc.nspname || '.' || c.relname || '.' || a.attnum, current_database(), nc.nspname, c.relname, 'TABLE_COL', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_attribute a, pg_class c, pg_namespace nc, pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typelem = bt.oid WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND nc.oid = c.relnamespace AND NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r', 'v'])) AND (pg_has_role(c.relowner, 'USAGE') OR has_table_privilege(c.oid, 'SELECT') OR has_table_privilege(c.oid, 'INSERT') OR has_table_privilege(c.oid, 'UPDATE') OR has_table_privilege(c.oid, 'REFERENCES')) AND t.typelem <> 0 AND t.typlen = -1 "
+	"UNION SELECT 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_PAR', coalesce (nbt.nspname || '.', '') || bt.typname, NULL, NULL, NULL FROM pg_type t, pg_type bt, pg_namespace nbt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND bt.oid= t.typelem AND bt.typnamespace = nbt.oid AND t.typelem <> 0 AND t.typlen = -1 "
+	"UNION SELECT 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n, current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), 'ROUTINE_COL', CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'array_spec' ELSE coalesce (ant.nspname || '.', '') || at.typname END, CASE WHEN at.typelem <> 0 AND at.typlen = -1 THEN 'ARR' || at.typelem ELSE NULL END, NULL, NULL FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss, pg_type at, pg_namespace ant WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND at.oid = t.typelem AND at.typnamespace = ant.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n]
  = 'b')",
+
+	/* I_STMT_ROUTINES_ALL */
+	"SELECT current_database(), n.nspname, ((p.proname || '_') || p.oid), current_database(), n.nspname, p.proname, CASE WHEN p.proisagg THEN 'AGGREGATE' ELSE 'FUNCTION' END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || n.nspname || '.' || p.proname || '.' || p.oid ELSE coalesce (nt.nspname || '.', '') || t.typname END AS rettype, p.proretset, p.pronargs, CASE WHEN l.lanname = 'sql' THEN 'SQL' ELSE 'EXTERNAL' END, CASE WHEN pg_has_role(p.proowner, 'USAGE') THEN p.prosrc ELSE NULL END, CASE WHEN l.lanname = 'c' THEN p.prosrc ELSE NULL END, upper(l.lanname) AS external_language, 'GENERAL' AS parameter_style, CASE WHEN p.provolatile = 'i' THEN TRUE ELSE FALSE END, 'MODIFIES' AS sql_data_access, CASE WHEN p.proisstrict THEN TRUE ELSE FALSE END, pg_catalog.obj_description(p.oid), CASE WHEN pg_catalog.pg_function_is_visible(p.oid) IS TRUE THEN p.proname ELSE coalesce (n.nspname || '.', '') || p.proname END, coalesce (n.nspname || '.', '') || 
 p.proname, o.rolname FROM pg_namespace n, pg_proc p, pg_language l, pg_type t, pg_namespace nt, pg_roles o WHERE n.oid = p.pronamespace AND p.prolang = l.oid AND p.prorettype = t.oid AND t.typnamespace = nt.oid AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE')) AND o.oid=p.proowner",
+
+	/* I_STMT_ROUTINES */
+	"SELECT current_database(), n.nspname, ((p.proname || '_') || p.oid), current_database(), n.nspname, p.proname, CASE WHEN p.proisagg THEN 'AGGREGATE' ELSE 'FUNCTION' END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || n.nspname || '.' || p.proname || '.' || p.oid ELSE coalesce (nt.nspname || '.', '') || t.typname END AS rettype, p.proretset, p.pronargs, CASE WHEN l.lanname = 'sql' THEN 'SQL' ELSE 'EXTERNAL' END, CASE WHEN pg_has_role(p.proowner, 'USAGE') THEN p.prosrc ELSE NULL END, CASE WHEN l.lanname = 'c' THEN p.prosrc ELSE NULL END, upper(l.lanname) AS external_language, 'GENERAL' AS parameter_style, CASE WHEN p.provolatile = 'i' THEN TRUE ELSE FALSE END, 'MODIFIES' AS sql_data_access, CASE WHEN p.proisstrict THEN TRUE ELSE FALSE END, pg_catalog.obj_description(p.oid), CASE WHEN pg_catalog.pg_function_is_visible(p.oid) IS TRUE THEN p.proname ELSE coalesce (n.nspname || '.', '') || p.proname END, coalesce (n.nspname || '.', '') || 
 p.proname, o.rolname FROM pg_namespace n, pg_proc p, pg_language l, pg_type t, pg_namespace nt, pg_roles o WHERE current_database() = ##cat::string AND n.nspname = ##schema::string AND n.oid = p.pronamespace AND p.prolang = l.oid AND p.prorettype = t.oid AND t.typnamespace = nt.oid AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE')) AND o.oid=p.proowner",
+
+	/* I_STMT_ROUTINES_ONE */
+	"SELECT current_database(), n.nspname, ((p.proname || '_') || p.oid), current_database(), n.nspname, p.proname, CASE WHEN p.proisagg THEN 'AGGREGATE' ELSE 'FUNCTION' END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || n.nspname || '.' || p.proname || '.' || p.oid ELSE coalesce (nt.nspname || '.', '') || t.typname END AS rettype, p.proretset, p.pronargs, CASE WHEN l.lanname = 'sql' THEN 'SQL' ELSE 'EXTERNAL' END, CASE WHEN pg_has_role(p.proowner, 'USAGE') THEN p.prosrc ELSE NULL END, CASE WHEN l.lanname = 'c' THEN p.prosrc ELSE NULL END, upper(l.lanname) AS external_language, 'GENERAL' AS parameter_style, CASE WHEN p.provolatile = 'i' THEN TRUE ELSE FALSE END, 'MODIFIES' AS sql_data_access, CASE WHEN p.proisstrict THEN TRUE ELSE FALSE END, pg_catalog.obj_description(p.oid), CASE WHEN pg_catalog.pg_function_is_visible(p.oid) IS TRUE THEN p.proname ELSE coalesce (n.nspname || '.', '') || p.proname END, coalesce (n.nspname || '.', '') || 
 p.proname, o.rolname FROM pg_namespace n, pg_proc p, pg_language l, pg_type t, pg_namespace nt, pg_roles o WHERE current_database() = ##cat::string AND n.nspname = ##schema::string AND ((p.proname || '_') || p.oid) = ##name::string AND n.oid = p.pronamespace AND p.prolang = l.oid AND p.prorettype = t.oid AND t.typnamespace = nt.oid AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE')) AND o.oid=p.proowner",
+
+	/* I_STMT_ROUTINE_PAR_ALL */
+	"SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), (ss.x).n, CASE WHEN ss.proargmodes IS NULL THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'i' THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN ss.proargmodes[(ss.x).n] = 'b' THEN 'INOUT' ELSE NULL END, NULLIF(ss.proargnames[(ss.x).n], ''), CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.o
 id = (ss.x).x AND t.typnamespace = nt.oid",
+
+	/* I_STMT_ROUTINE_PAR */
+	"SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), (ss.x).n, CASE WHEN ss.proargmodes IS NULL THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'i' THEN 'IN' WHEN ss.proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN ss.proargmodes[(ss.x).n] = 'b' THEN 'INOUT' ELSE NULL END, NULLIF(ss.proargnames[(ss.x).n], ''), CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUP' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.o
 id = (ss.x).x AND t.typnamespace = nt.oid AND current_database() = ##cat::string AND ss.n_nspname = ##schema::string AND ((ss.proname || '_') || ss.p_oid) = ##name::string",
+
+	/* I_STMT_ROUTINE_COL_ALL */
+	"SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), NULLIF(ss.proargnames[(ss.x).n], ''), (ss.x).n, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n] = 'b') ORDER BY 1, 2, 3, 4, 5",
+
+	/* I_STMT_ROUTINE_COL */
+	"SELECT current_database(), ss.n_nspname, ((ss.proname || '_') || ss.p_oid), NULLIF(ss.proargnames[(ss.x).n], ''), (ss.x).n, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'array_spec' ELSE coalesce (nt.nspname || '.', '') || t.typname END, CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ROUC' || current_database() || '.' || ss.n_nspname || '.' || ((ss.proname || '_') || ss.p_oid) || '.' || (ss.x).n ELSE NULL END FROM pg_type t, pg_namespace nt, ( SELECT n.nspname AS n_nspname, p.proname, p.oid AS p_oid, p.proargnames, p.proargmodes, information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x FROM pg_namespace n, pg_proc p WHERE n.oid = p.pronamespace AND (pg_has_role(p.proowner, 'USAGE') OR has_function_privilege(p.oid, 'EXECUTE'))) ss WHERE t.oid = (ss.x).x AND t.typnamespace = nt.oid AND (ss.proargmodes[(ss.x).n] = 'o' OR ss.proargmodes[(ss.x).n] = 'b') AND current_database() = ##cat::string AND ss.n_nspname = ##schema::string AND ((ss.pro
 name || '_') || ss.p_oid) = ##name::string ORDER BY 1, 2, 3, 4, 5"
+
+};
+
+/*
+ * global static values, and
+ * predefined statements' GdaStatement, all initialized in _gda_postgres_provider_meta_init()
+ */
+static GdaStatement **internal_stmt;
+static GdaSet       *i_set;
+
+/*
+ * Meta initialization
+ */
+void
+_gda_postgres_provider_meta_init (GdaServerProvider *provider)
+{
+	static GStaticMutex init_mutex = G_STATIC_MUTEX_INIT;
+	InternalStatementItem i;
+	static GdaSqlParser *parser = NULL;
+
+	g_static_mutex_lock (&init_mutex);
+
+	if (provider)
+		parser = gda_server_provider_internal_get_parser (provider);
+	else
+		parser = GDA_SQL_PARSER (g_object_new (GDA_TYPE_POSTGRES_PARSER, NULL));
+        internal_stmt = g_new0 (GdaStatement *, sizeof (internal_sql) / sizeof (gchar*));
+        for (i = I_STMT_CATALOG; i < sizeof (internal_sql) / sizeof (gchar*); i++) {
+                internal_stmt[i] = gda_sql_parser_parse_string (parser, internal_sql[i], NULL, NULL);
+                if (!internal_stmt[i])
+                        g_error ("Could not parse internal statement: %s\n", internal_sql[i]);
+        }
+	if (!provider)
+		g_object_unref (parser);
+
+	i_set = gda_set_new_inline (4, "cat", G_TYPE_STRING, "", 
+				    "name", G_TYPE_STRING, "",
+				    "schema", G_TYPE_STRING, "",
+				    "name2", G_TYPE_STRING, "");
+
+	g_static_mutex_unlock (&init_mutex);
+
+#ifdef GDA_DEBUG
+	_gda_postgres_test_keywords ();
+#endif
+}
+
+#define GDA_POSTGRES_GET_REUSEABLE_DATA(cdata) (* ((GdaPostgresReuseable**) (cdata)))
+
+gboolean
+_gda_postgres_meta__info (GdaServerProvider *prov, GdaConnection *cnc, 
+			  GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_CATALOG],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_information_schema_catalog_name, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, 
+						   _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model, *proxy;
+	gboolean retval = TRUE;
+	gint i, nrows;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	/* use a prepared statement for the "base" model */
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_BTYPES],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_builtin_data_types, error);
+	if (!model)
+		return FALSE;
+
+	/* use a proxy to customize @model */
+	proxy = (GdaDataModel*) gda_data_proxy_new (model);
+	g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *value;
+		GType type;
+		value = gda_data_model_get_value_at (model, 6, i, error);
+		if (!value) {
+			retval = FALSE;
+			break;
+		}
+		
+		guint oid = (guint) g_ascii_strtoull (g_value_get_string (value), NULL, 10);
+		type = _gda_postgres_type_oid_to_gda (cnc, rdata, oid);
+		if (type != G_TYPE_STRING) {
+			GValue *v;
+			g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
+			retval = gda_data_model_set_value_at (proxy, 2, i, v, error);
+			gda_value_free (v);
+			if (!retval)
+				break;
+		}
+	}
+
+	/* modify meta store with @proxy */
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, 
+							   _gda_postgres_reuseable_get_reserved_keywords_func
+							   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, proxy, NULL, error, NULL);
+	}
+	g_object_unref (proxy);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean 
+_gda_postgres_meta__udt (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_UDT_ALL],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_udt, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta_udt (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			const GValue *udt_catalog, const GValue *udt_schema)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), udt_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), udt_schema, error))
+		return FALSE;
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_UDT],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_udt, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__udt_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_UDT_COLUMNS_ALL],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_udt_columns, error);
+
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (!gda_holder_set_value (gda_set_get_holder (i_set, "cat"), udt_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), udt_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), udt_name, error))
+		return FALSE;
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_UDT_COLUMNS_ALL],
+							      i_set, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_udt_columns, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__enums (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float >= 8.3)
+		TO_IMPLEMENT;
+
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_meta_enums (GdaServerProvider *prov, GdaConnection *cnc, 
+			  GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			  const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float >= 8.3)
+		TO_IMPLEMENT;
+
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__domains (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_DOMAINS_ALL],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_domains, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta_domains (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			    const GValue *domain_catalog, const GValue *domain_schema)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), domain_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), domain_schema, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_DOMAINS],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_domains, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc, 
+				     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_DOMAINS_CONSTRAINTS_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_domain_constraints, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), domain_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), domain_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), domain_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_DOMAINS_CONSTRAINTS],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_domain_constraints, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__el_types (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_EL_TYPES_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_element_types, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta_el_types (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			     const GValue *specific_name)
+{
+	const gchar *cstr;
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), specific_name, error))
+		return FALSE;
+	cstr = g_value_get_string (specific_name);
+	if (*cstr == 'C') {
+		/* check correct postgres server version */
+		if (rdata->version_float < 8.2) {
+			/* nothing for this version of PostgreSQL */
+			return TRUE;
+		}
+		
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_EL_TYPES_COL],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_element_types, error);
+	}
+	else if (*cstr == 'D')
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_EL_TYPES_DOM],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_element_types, error);
+	else if (*cstr == 'U')
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_EL_TYPES_UDT],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_element_types, error);
+	else if (!strcmp (cstr, "ROUTINE_PAR"))
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_EL_TYPES_ROUT_PAR],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_element_types, error);
+	else if (!strcmp (cstr, "ROUTINE_COL"))
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_EL_TYPES_ROUT_COL],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_element_types, error);
+	else
+		TO_IMPLEMENT;
+	
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__collations (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	/* nothing to do */
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	/* nothing to do */
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__character_sets (GdaServerProvider *prov, GdaConnection *cnc, 
+				    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	/* nothing to do */
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	/* nothing to do */
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__schemata (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_SCHEMAS_ALL],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_schemata, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+	
+	return retval;
+}
+
+gboolean 
+_gda_postgres_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+			     const GValue *catalog_name, const GValue *schema_name_n)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), catalog_name, error))
+		return FALSE;
+	if (!schema_name_n) {
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_SCHEMAS],
+								      i_set, 
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_schemata, error);
+		if (!model)
+			return FALSE;
+
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, model, NULL, error, NULL);
+	}
+	else {
+		if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), schema_name_n, error))
+			return FALSE;
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_SCHEMA_NAMED],
+								      i_set, 
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_schemata, error);
+		if (!model)
+			return FALSE;
+		
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, model, "schema_name = ##name::string", error, 
+						"name", schema_name_n, NULL);
+	}
+	g_object_unref (model);
+	
+	return retval;
+}
+
+/* get the float version of a Postgres version which looks like:
+ * PostgreSQL 7.2.2 on i686-pc-linux-gnu, compiled by GCC 2.96 => returns 7.22
+ * PostgreSQL 7.3 on i686-pc-linux-gnu, compiled by GCC 2.95.3 => returns 7.3
+ * WARNING: no serious test is made on the validity of the string
+ */
+static gfloat
+get_pg_version_float (const gchar *str)
+{
+        gfloat retval = 0.;
+        const gchar *ptr;
+        gfloat div = 1;
+
+        if (!str)
+                return retval;
+
+        /* go on  the first digit of version number */
+        ptr = str;
+        while (*ptr != ' ')
+                ptr++;
+        ptr++;
+
+        /* elaborate the real version number */
+        while (*ptr != ' ') {
+                if (*ptr != '.') {
+                        retval += (*ptr - '0')/div;
+                        div *= 10;
+                }
+                ptr++;
+        }
+
+        return retval;
+}
+
+static gboolean
+compute_pg_version (GdaConnection *cnc, GdaPostgresReuseable *rdata, GError **error)
+{
+	GdaSqlBuilder *b;
+	GdaStatement *stmt;
+	GdaDataModel *model;
+
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+        gda_sql_builder_add_function (b, 1, "version", 0);
+        gda_sql_builder_add_field (b, 1, 0);
+	stmt = gda_sql_builder_get_statement (b, NULL);
+	g_object_unref (b);
+	g_assert (stmt);
+
+	model = gda_connection_statement_execute_select (cnc, stmt, NULL, error);
+	g_object_unref (stmt);
+	if (!model)
+		return FALSE;
+	
+	const GValue *cvalue;
+	cvalue = gda_data_model_get_value_at (model, 0, 0, NULL);
+	if (!cvalue) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+                             GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+                             _("Can't import data from web server"));
+		g_object_unref (model);
+		return FALSE;
+	}
+
+	const gchar *str;
+	str = g_value_get_string (cvalue);
+	rdata->version_float = get_pg_version_float (str);
+	((GdaProviderReuseable*)rdata)->version_major =
+		g_strdup_printf ("%d", (int) rdata->version_float);
+	((GdaProviderReuseable*)rdata)->version_minor =
+		g_strdup_printf ("%d", (int) ((rdata->version_float - (int) rdata->version_float) * 10));
+
+	g_object_unref (model);
+
+	/*g_print ("VERSIONS: [%f] [%s] [%s]\n", rdata->version_float,
+		 ((GdaProviderReuseable*)rdata)->version_major,
+		 ((GdaProviderReuseable*)rdata)->version_minor);*/
+	return TRUE;
+}
+
+gboolean
+_gda_postgres_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc, 
+				  GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *tables_model, *views_model;
+	gboolean retval = TRUE;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if ((rdata->version_float == 0) && ! compute_pg_version (cnc, rdata, error))
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	tables_model = gda_connection_statement_execute_select_full (cnc,
+								     internal_stmt[I_STMT_TABLES_ALL],
+								     NULL, 
+								     GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								     _col_types_tables, error);
+	if (!tables_model)
+		return FALSE;
+	views_model = gda_connection_statement_execute_select_full (cnc,
+								    internal_stmt[I_STMT_VIEWS_ALL],
+								    NULL, 
+								    GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								    _col_types_views, error);
+	if (!views_model) {
+		g_object_unref (tables_model);
+		return FALSE;
+	}
+
+	GdaMetaContext c2;
+	c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+	if (retval) {
+		c2.table_name = "_tables";
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+	}
+	if (retval) {
+		c2.table_name = "_views";
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+	}
+	g_object_unref (tables_model);
+	g_object_unref (views_model);
+
+
+	return retval;
+}
+
+gboolean 
+_gda_postgres_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)
+{
+	GdaDataModel *tables_model, *views_model;
+	gboolean retval = TRUE;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (!table_name_n) {
+		tables_model = gda_connection_statement_execute_select_full (cnc,
+									     internal_stmt[I_STMT_TABLES],
+									     i_set, 
+									     GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+									     _col_types_tables, error);
+		if (!tables_model)
+			return FALSE;
+		views_model = gda_connection_statement_execute_select_full (cnc,
+									    internal_stmt[I_STMT_VIEWS],
+									    i_set, 
+									    GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+									    _col_types_views, error);
+		if (!views_model) {
+			g_object_unref (tables_model);
+			return FALSE;
+		}
+	}
+	else {
+		if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name_n, error))
+			return FALSE;
+		tables_model = gda_connection_statement_execute_select_full (cnc,
+									     internal_stmt[I_STMT_TABLE_NAMED],
+									     i_set, 
+									     GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+									     _col_types_tables, error);
+		if (!tables_model)
+			return FALSE;
+		views_model = gda_connection_statement_execute_select_full (cnc,
+									    internal_stmt[I_STMT_VIEW_NAMED],
+									    i_set, 
+									    GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+									    _col_types_views, error);
+		if (!views_model) {
+			g_object_unref (tables_model);
+			return FALSE;
+		}
+	}
+
+	GdaMetaContext c2;
+	c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+	if (retval) {
+		c2.table_name = "_tables";
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+	}
+	if (retval) {
+		c2.table_name = "_views";
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+	}
+	g_object_unref (tables_model);
+	g_object_unref (views_model);
+
+	return retval;
+}
+
+gboolean _gda_postgres_meta__columns (GdaServerProvider *prov, GdaConnection *cnc, 
+				      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model, *proxy;
+	gboolean retval = TRUE;
+	gint i, nrows;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	/* use a prepared statement for the "base" model */
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_COLUMNS_ALL],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_columns, error);
+	if (!model)
+		return FALSE;
+
+	/* use a proxy to customize @model */
+	proxy = (GdaDataModel*) gda_data_proxy_new (model);
+	g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *value;
+		GType type;
+
+		/* GType */
+		value = gda_data_model_get_value_at (model, 24, i, error);
+		if (!value) {
+			retval = FALSE;
+			break;
+		}
+		
+		guint oid = (guint) g_ascii_strtoull (g_value_get_string (value), NULL, 10);
+		type = _gda_postgres_type_oid_to_gda (cnc, rdata, oid);
+		if (type != G_TYPE_STRING) {
+			GValue *v;
+			g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
+			retval = gda_data_model_set_value_at (proxy, 9, i, v, error);
+			gda_value_free (v);
+			if (!retval)
+				break;
+		}
+
+		/* column default: remove the datatype cast on strings:
+		 * 'abd'::character varying  => 'abd' */
+		value = gda_data_model_get_value_at (model, 5, i, error);
+		if (!value) {
+			retval = FALSE;
+			break;
+		}
+		
+		if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
+			const gchar *cstr;
+			cstr = g_value_get_string (value);
+			if (cstr && (*cstr == '\'')) {
+				gint len;
+				len = strlen (cstr);
+				if (cstr [len-1] != '\'') {
+					gchar *tmp = g_strdup (cstr);
+					gint k;
+					for (k = len - 1; k > 0; k--) {
+						if (tmp [k] == '\'') {
+							tmp [k+1] = 0;
+							break;
+						}
+					}
+					GValue *v;
+					g_value_take_string (v = gda_value_new (G_TYPE_STRING), tmp);
+					retval = gda_data_model_set_value_at (proxy, 5, i, v, error);
+					gda_value_free (v);
+					if (!retval)
+						break;
+				}
+			}
+		}
+	}
+
+	/* modify meta store with @proxy */
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, context, proxy, error);
+	}
+	g_object_unref (proxy);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+			    const GValue *table_catalog, const GValue *table_schema, const GValue *table_name)
+{
+	GdaDataModel *model, *proxy;
+	gboolean retval = TRUE;
+	gint i, nrows;
+	GdaPostgresReuseable *rdata;
+
+	/* check correct postgres server version */
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	/* use a prepared statement for the "base" model */
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+		return FALSE;
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_COLUMNS_OF_TABLE],
+							      i_set, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_columns, error);
+	if (!model)
+		return FALSE;
+
+	/* use a proxy to customize @model */
+	proxy = (GdaDataModel*) gda_data_proxy_new (model);
+	g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *value;
+		GType type;
+
+		/* GType */
+		value = gda_data_model_get_value_at (model, 24, i, error);
+		if (!value) {
+			retval = FALSE;
+			break;
+		}
+
+		guint oid = (guint) g_ascii_strtoull (g_value_get_string (value), NULL, 10);
+		type = _gda_postgres_type_oid_to_gda (cnc, rdata, oid);
+		if (type != G_TYPE_STRING) {
+			GValue *v;
+			g_value_set_string (v = gda_value_new (G_TYPE_STRING), g_type_name (type));
+			retval = gda_data_model_set_value_at (proxy, 9, i, v, error);
+			gda_value_free (v);
+			if (!retval)
+				break;
+		}
+
+		/* column default: remove the datatype cast on strings:
+		 * 'abd'::character varying  => 'abd' */
+		value = gda_data_model_get_value_at (model, 5, i, error);
+		if (!value) {
+			retval = FALSE;
+			break;
+		}
+		
+		if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
+			const gchar *cstr;
+			cstr = g_value_get_string (value);
+			if (cstr && (*cstr == '\'')) {
+				gint len;
+				len = strlen (cstr);
+				if (cstr [len-1] != '\'') {
+					gchar *tmp = g_strdup (cstr);
+					gint k;
+					for (k = len - 1; k > 0; k--) {
+						if (tmp [k] == '\'') {
+							tmp [k+1] = 0;
+							break;
+						}
+					}
+					GValue *v;
+					g_value_take_string (v = gda_value_new (G_TYPE_STRING), tmp);
+					retval = gda_data_model_set_value_at (proxy, 5, i, v, error);
+					gda_value_free (v);
+					if (!retval)
+						break;
+				}
+			}
+		}
+	}
+
+	/* modify meta store with @proxy */
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, proxy, 
+						"table_schema = ##schema::string AND table_name = ##name::string", error, 
+						"schema", table_schema, "name", table_name, NULL);
+	}
+	g_object_unref (proxy);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__view_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_VIEWS_COLUMNS_ALL],
+							      NULL, 
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_view_column_usage, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+	
+	return retval;
+}
+
+gboolean 
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), view_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), view_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), view_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_VIEWS_COLUMNS],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_view_column_usage, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
+				     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_TABLES_CONSTRAINTS_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_table_constraints, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+		return FALSE;
+
+	if (!constraint_name_n) {
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_TABLES_CONSTRAINTS],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_table_constraints, error);
+		if (!model)
+			return FALSE;
+		if (retval) {
+			gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+			retval = gda_meta_store_modify (store, context->table_name, model, 
+							"table_schema = ##schema::string AND table_name = ##name::string", 
+							error, 
+							"schema", table_schema, "name", table_name, NULL);
+		}
+
+	}
+	else {
+		if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name_n, error))
+			return FALSE;
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_TABLES_CONSTRAINT_NAMED],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_table_constraints, error);
+		if (!model)
+			return FALSE;
+		if (retval) {
+			gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+			retval = gda_meta_store_modify (store, context->table_name, model, 
+							"table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string", error, 
+							"schema", table_schema, "name", table_name, "name2", constraint_name_n, NULL);
+		}
+	}
+
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean 
+_gda_postgres_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
+				     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_REF_CONSTRAINTS_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							     _col_types_referential_constraints, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_REF_CONSTRAINTS],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							     _col_types_referential_constraints, error);
+	if (!model)
+		return FALSE;
+
+
+	/* modify meta store */
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, model, 
+						"table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string", 
+						error, 
+						"schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+	}
+	g_object_unref (model);
+
+	return retval;
+}
+
+gboolean 
+_gda_postgres_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+				 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_KEY_COLUMN_USAGE_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							     _col_types_key_column_usage, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean 
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_KEY_COLUMN_USAGE],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_key_column_usage, error);
+	if (!model)
+		return FALSE;
+
+
+	/* modify meta store */
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, model, 
+						"table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string", 
+						error, 
+						"schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+	}
+	g_object_unref (model);
+
+	return retval;	
+}
+
+gboolean
+_gda_postgres_meta__check_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+				   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_CHECK_COLUMN_USAGE_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_check_column_usage, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	GdaPostgresReuseable *rdata;
+
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_CHECK_COLUMN_USAGE],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_check_column_usage, error);
+	if (!model)
+		return FALSE;
+
+
+	/* modify meta store */
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify (store, context->table_name, model, 
+						"table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string", 
+						error, 
+						"schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+	}
+	g_object_unref (model);
+
+	return retval;	
+}
+
+gboolean
+_gda_postgres_meta__triggers (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_TRIGGERS_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_triggers, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta_triggers (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			     const GValue *table_catalog, const GValue *table_schema, 
+			     const GValue *table_name)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_TRIGGERS],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_triggers, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__routines (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_ROUTINES_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_routines, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), routine_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), routine_schema, error))
+		return FALSE;
+	if (routine_name_n) {
+		if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), routine_name_n, error))
+			return FALSE;
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_ROUTINES_ONE],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_routines, error);
+	}
+	else 
+		model = gda_connection_statement_execute_select_full (cnc,
+								      internal_stmt[I_STMT_ROUTINES],
+								      i_set,
+								      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+								      _col_types_routines, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__routine_col (GdaServerProvider *prov, GdaConnection *cnc, 
+				 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model, *proxy;
+	gint ordinal_pos, i, nrows;
+	const GValue *spname = NULL;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_ROUTINE_COL_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_routine_columns, error);
+	if (!model)
+		return FALSE;
+
+	/* use a proxy to customize @model */
+	proxy = (GdaDataModel*) gda_data_proxy_new (model);
+	g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *cvalue;
+		GValue *v;
+
+		cvalue = gda_data_model_get_value_at (model, 2, i, error);
+		if (!cvalue) {
+			retval = FALSE;
+			break;
+		}
+		if (!spname || gda_value_compare (spname, cvalue))
+			/* reinit ordinal position */
+			ordinal_pos = 1;
+		spname = cvalue;
+
+		g_value_set_int ((v = gda_value_new (G_TYPE_INT)), ordinal_pos++);
+		retval = gda_data_model_set_value_at (proxy, 4, i, v, error);
+		gda_value_free (v);
+		if (!retval)
+			break;
+	}
+
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, context, proxy, error);
+	}
+	g_object_unref (model);
+	g_object_unref (proxy);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model, *proxy;
+	gint ordinal_pos, i, nrows;
+	const GValue *spname = NULL;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), rout_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), rout_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), rout_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_ROUTINE_COL],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_routine_columns, error);
+	if (!model)
+		return FALSE;
+
+	/* use a proxy to customize @model */
+	proxy = (GdaDataModel*) gda_data_proxy_new (model);
+	g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, "sample-size", 0, NULL);
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		const GValue *cvalue;
+		GValue *v;
+
+		cvalue = gda_data_model_get_value_at (model, 2, i, error);
+		if (!cvalue) {
+			retval = FALSE;
+			break;
+		}
+
+		if (!spname || gda_value_compare (spname, cvalue))
+			/* reinit ordinal position */
+			ordinal_pos = 1;
+		spname = cvalue;
+
+		g_value_set_int ((v = gda_value_new (G_TYPE_INT)), ordinal_pos++);
+		retval = gda_data_model_set_value_at (proxy, 4, i, v, error);
+		gda_value_free (v);
+		if (!retval)
+			break;
+	}
+
+	if (retval) {
+		gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+		retval = gda_meta_store_modify_with_context (store, context, proxy, error);
+	}
+	g_object_unref (model);
+	g_object_unref (proxy);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_meta__routine_par (GdaServerProvider *prov, GdaConnection *cnc, 
+				 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_ROUTINE_PAR_ALL],
+							      NULL,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_parameters, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_postgres_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)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+
+	/* check correct postgres server version */
+	GdaPostgresReuseable *rdata;
+	rdata = GDA_POSTGRES_GET_REUSEABLE_DATA (gda_connection_internal_get_provider_data (cnc));
+	if (!rdata)
+		return FALSE;
+	if (rdata->version_float < 8.2) {
+		/* nothing for this version of PostgreSQL */
+		return TRUE;
+	}
+
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "cat"), rout_catalog, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "schema"), rout_schema, error))
+		return FALSE;
+	if (! gda_holder_set_value (gda_set_get_holder (i_set, "name"), rout_name, error))
+		return FALSE;
+
+	model = gda_connection_statement_execute_select_full (cnc,
+							      internal_stmt[I_STMT_ROUTINE_PAR],
+							      i_set,
+							      GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+							      _col_types_parameters, error);
+	if (!model)
+		return FALSE;
+
+	gda_meta_store_set_reserved_keywords_func (store, _gda_postgres_reuseable_get_reserved_keywords_func
+						   ((GdaProviderReuseable*) rdata));
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
diff --git a/providers/postgres/gda-postgres-meta.h b/providers/reuseable/postgres/gda-postgres-meta.h
similarity index 100%
rename from providers/postgres/gda-postgres-meta.h
rename to providers/reuseable/postgres/gda-postgres-meta.h
diff --git a/providers/postgres/gda-postgres-parser.c b/providers/reuseable/postgres/gda-postgres-parser.c
similarity index 100%
rename from providers/postgres/gda-postgres-parser.c
rename to providers/reuseable/postgres/gda-postgres-parser.c
diff --git a/providers/postgres/gda-postgres-parser.h b/providers/reuseable/postgres/gda-postgres-parser.h
similarity index 100%
rename from providers/postgres/gda-postgres-parser.h
rename to providers/reuseable/postgres/gda-postgres-parser.h
diff --git a/providers/reuseable/postgres/gda-postgres-reuseable.c b/providers/reuseable/postgres/gda-postgres-reuseable.c
new file mode 100644
index 0000000..75d8972
--- /dev/null
+++ b/providers/reuseable/postgres/gda-postgres-reuseable.c
@@ -0,0 +1,432 @@
+/* GDA postgres provider
+ * Copyright (C) 1998 - 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *         Vivien Malerba <malerba gnome-db org>
+ *         Rodrigo Moya <rodrigo gnome-db org>
+ *         Gonzalo Paniagua Javier <gonzalo gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include "gda-postgres-reuseable.h"
+#include "gda-postgres-parser.h"
+
+#include <libgda/sqlite/keywords_hash.h>
+#include "keywords_hash.c" /* this one is dynamically generated */
+
+/*
+ * Reuseable interface entry point
+ */
+static GdaProviderReuseableOperations
+_gda_postgres_reuseable = {
+	_gda_postgres_reuseable_new_data,
+	_gda_postgres_reuseable_reset_data,
+	_gda_postgres_reuseable_get_g_type,
+	_gda_postgres_reuseable_get_reserved_keywords_func,
+	_gda_postgres_reuseable_create_parser,
+	{
+		._info = _gda_postgres_meta__info,
+		._btypes = _gda_postgres_meta__btypes,
+		._udt = _gda_postgres_meta__udt,
+		.udt = _gda_postgres_meta_udt,
+		._udt_cols = _gda_postgres_meta__udt_cols,
+		.udt_cols = _gda_postgres_meta_udt_cols,
+		._enums = _gda_postgres_meta__enums,
+		.enums = _gda_postgres_meta_enums,
+		._domains = _gda_postgres_meta__domains,
+		.domains = _gda_postgres_meta_domains,
+		._constraints_dom = _gda_postgres_meta__constraints_dom,
+		.constraints_dom = _gda_postgres_meta_constraints_dom,
+		._el_types = _gda_postgres_meta__el_types,
+		.el_types = _gda_postgres_meta_el_types,
+		._collations = _gda_postgres_meta__collations,
+		.collations = _gda_postgres_meta_collations,
+		._character_sets = _gda_postgres_meta__character_sets,
+		.character_sets = _gda_postgres_meta_character_sets,
+		._schemata = _gda_postgres_meta__schemata,
+		.schemata = _gda_postgres_meta_schemata,
+		._tables_views = _gda_postgres_meta__tables_views,
+		.tables_views = _gda_postgres_meta_tables_views,
+		._columns = _gda_postgres_meta__columns,
+		.columns = _gda_postgres_meta_columns,
+		._view_cols = _gda_postgres_meta__view_cols,
+		.view_cols = _gda_postgres_meta_view_cols,
+		._constraints_tab = _gda_postgres_meta__constraints_tab,
+		.constraints_tab = _gda_postgres_meta_constraints_tab,
+		._constraints_ref = _gda_postgres_meta__constraints_ref,
+		.constraints_ref = _gda_postgres_meta_constraints_ref,
+		._key_columns = _gda_postgres_meta__key_columns,
+		.key_columns = _gda_postgres_meta_key_columns,
+		._check_columns = _gda_postgres_meta__check_columns,
+		.check_columns = _gda_postgres_meta_check_columns,
+		._triggers = _gda_postgres_meta__triggers,
+		.triggers = _gda_postgres_meta_triggers,
+		._routines = _gda_postgres_meta__routines,
+		.routines = _gda_postgres_meta_routines,
+		._routine_col = _gda_postgres_meta__routine_col,
+		.routine_col = _gda_postgres_meta_routine_col,
+		._routine_par = _gda_postgres_meta__routine_par,
+		.routine_par = _gda_postgres_meta_routine_par
+	}
+};
+
+GdaProviderReuseableOperations *
+_gda_postgres_reuseable_get_ops (void)
+{
+	return &_gda_postgres_reuseable;
+}
+
+/*
+ * Postgres type identification
+ */
+typedef struct {
+        gchar              *name;
+        unsigned int        oid; /* <=> to Postgres's Oid type */
+        GType               type;
+        gchar              *comments;
+        gchar              *owner;
+} GdaPostgresTypeOid;
+
+static void
+gda_postgres_type_oid_free (GdaPostgresTypeOid *typedata)
+{
+	g_free (typedata->name);
+	g_free (typedata->comments);
+	g_free (typedata->owner);
+	g_free (typedata);
+}
+
+#ifdef GDA_DEBUG
+void
+_gda_postgres_test_keywords (void)
+{
+        V82test_keywords();
+        V83test_keywords();
+        V84test_keywords();
+}
+#endif
+
+GdaProviderReuseable *
+_gda_postgres_reuseable_new_data (const gchar *major, const gchar *minor)
+{
+	GdaPostgresReuseable *reuseable;
+	reuseable = g_new0 (GdaPostgresReuseable, 1);
+	reuseable->types_oid_hash = NULL;
+	reuseable->types_dbtype_hash = NULL;
+	_gda_postgres_provider_meta_init (NULL);
+
+	((GdaProviderReuseable*)reuseable)->operations = &_gda_postgres_reuseable;
+
+	return (GdaProviderReuseable*) reuseable;
+}
+
+void
+_gda_postgres_reuseable_reset_data (GdaProviderReuseable *rdata)
+{
+	GdaPostgresReuseable *reuseable;
+	reuseable = (GdaPostgresReuseable*) rdata;
+	if (reuseable->types_dbtype_hash)
+		g_hash_table_destroy (reuseable->types_dbtype_hash);
+	if (reuseable->types_oid_hash)
+		g_hash_table_destroy (reuseable->types_oid_hash);
+
+	/* don't free reuseable->avoid_types */
+        g_free (reuseable->avoid_types_oids);
+        g_free (reuseable->any_type_oid);
+	memset (reuseable, 0, sizeof (GdaPostgresReuseable));
+}
+
+static GdaDataModel *
+execute_select (GdaConnection *cnc, GdaPostgresReuseable *rdata, const gchar *sql)
+{
+	GdaSqlParser *parser;
+	GdaStatement *stmt;
+	GdaDataModel *model;
+	parser = _gda_postgres_reuseable_create_parser ((GdaProviderReuseable*) rdata);
+	
+	stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+	g_object_unref (parser);
+	g_assert (stmt);
+
+	model = gda_connection_statement_execute_select (cnc, stmt, NULL, NULL);
+	g_object_unref (stmt);
+
+	return model;
+}
+
+static GType
+postgres_name_to_g_type (const gchar *name, const gchar *conv_func_name)
+{
+	/* default built in data types */
+	if (!strcmp (name, "bool"))
+		return G_TYPE_BOOLEAN;
+	else if (!strcmp (name, "int8"))
+		return G_TYPE_INT64;
+	else if (!strcmp (name, "int4") || !strcmp (name, "abstime"))
+		return G_TYPE_INT;
+	else if (!strcmp (name, "int2"))
+		return GDA_TYPE_SHORT;
+	else if (!strcmp (name, "float4"))
+		return G_TYPE_FLOAT;
+	else if (!strcmp (name, "float8"))
+		return G_TYPE_DOUBLE;
+	else if (!strcmp (name, "numeric"))
+		return GDA_TYPE_NUMERIC;
+	else if (!strncmp (name, "timestamp", 9))
+		return GDA_TYPE_TIMESTAMP;
+	else if (!strcmp (name, "date"))
+		return G_TYPE_DATE;
+	else if (!strncmp (name, "time", 4))
+		return GDA_TYPE_TIME;
+	else if (!strcmp (name, "point"))
+		return GDA_TYPE_GEOMETRIC_POINT;
+	else if (!strcmp (name, "oid"))
+		return GDA_TYPE_BLOB;
+	else if (!strcmp (name, "bytea"))
+		return GDA_TYPE_BINARY;
+
+	/* other data types, using the conversion function name as a hint */
+	if (!conv_func_name)
+		return G_TYPE_STRING;
+
+	if (!strncmp (conv_func_name, "int2", 4))
+		return GDA_TYPE_SHORT;
+	if (!strncmp (conv_func_name, "int4", 4))
+		return G_TYPE_INT;
+	if (!strncmp (conv_func_name, "int8", 4))
+		return G_TYPE_INT64;
+	if (!strncmp (conv_func_name, "float4", 6))
+		return G_TYPE_FLOAT;
+	if (!strncmp (conv_func_name, "float8", 6))
+		return G_TYPE_DOUBLE;
+	if (!strncmp (conv_func_name, "timestamp", 9))
+		return GDA_TYPE_TIMESTAMP;
+	if (!strncmp (conv_func_name, "time", 4))
+		return GDA_TYPE_TIME;
+	if (!strncmp (conv_func_name, "date", 4))
+		return G_TYPE_DATE;
+	if (!strncmp (conv_func_name, "bool", 4))
+		return G_TYPE_BOOLEAN;
+	if (!strncmp (conv_func_name, "oid", 3))
+		return GDA_TYPE_BLOB;
+	if (!strncmp (conv_func_name, "bytea", 5))
+		return GDA_TYPE_BINARY;
+	return G_TYPE_STRING;
+}
+
+void
+_gda_postgres_compute_types (GdaConnection *cnc, GdaPostgresReuseable *rdata)
+{
+	if (rdata->types_oid_hash)
+		return;
+
+	rdata->types_oid_hash = g_hash_table_new_full (g_int_hash, g_int_equal,
+						       NULL, (GDestroyNotify) gda_postgres_type_oid_free);
+	rdata->types_dbtype_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+	GdaDataModel *model, *model_avoid, *model_anyoid = NULL;
+	gint ncols, nrows, i;
+	gchar *avoid_types = NULL;
+	GString *string;
+
+	if (rdata->version_float < 7.3) {
+		gchar *query;
+		avoid_types = "'SET', 'cid', 'oid', 'int2vector', 'oidvector', 'regproc', 'smgr', 'tid', 'unknown', 'xid'";
+		/* main query to fetch infos about the data types */
+		query = g_strdup_printf ("SELECT pg_type.oid, typname, usename, obj_description(pg_type.oid) "
+					 "FROM pg_type, pg_user "
+					 "WHERE typowner=usesysid AND typrelid = 0 AND typname !~ '^_' "
+					 "AND  typname not in (%s) "
+					 "ORDER BY typname", avoid_types);
+		model = execute_select (cnc, rdata, query);
+		g_free (query);
+
+		/* query to fetch non returned data types */
+		query = g_strdup_printf ("SELECT pg_type.oid FROM pg_type WHERE typname in (%s)", avoid_types);
+		model_avoid = execute_select (cnc, rdata, query);
+		g_free (query);
+	}
+	else {
+		gchar *query;
+		avoid_types = "'any', 'anyarray', 'anyelement', 'cid', 'cstring', 'int2vector', 'internal', 'language_handler', 'oidvector', 'opaque', 'record', 'refcursor', 'regclass', 'regoper', 'regoperator', 'regproc', 'regprocedure', 'regtype', 'SET', 'smgr', 'tid', 'trigger', 'unknown', 'void', 'xid'";
+
+		/* 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 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 !~ '^_' "
+			  "AND (t.typrelid = 0 OR "
+			  "(SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
+			  "AND t.typname not in (%s) "
+			  "ORDER BY typname", avoid_types);
+		model = execute_select (cnc, rdata, query);
+		g_free (query);
+
+		/* query to fetch non returned data types */
+		query = g_strdup_printf ("SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname in (%s)",
+					 avoid_types);
+		model_avoid = execute_select (cnc, rdata, query);
+		g_free (query);
+
+		/* query to fetch the oid of the 'any' data type */
+		model_anyoid = execute_select (cnc, rdata,
+					       "SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname = 'any'");
+	}
+
+	if (!model || !model_avoid ||
+	    ((rdata->version_float >= 7.3) && !model_anyoid)) {
+		if (model)
+			g_object_unref (model);
+		if (model_avoid)
+			g_object_unref (model_avoid);
+		if (model_anyoid)
+			g_object_unref (model_anyoid);
+		return;
+	}
+
+	/* Data types returned to the Gda client */
+	nrows = gda_data_model_get_n_rows (model);
+	ncols = gda_data_model_get_n_columns (model);
+	if (nrows == 0)
+		g_warning ("PostgreSQL provider did not find any data type (expect some mis-behaviours) please report the error to bugzilla.gnome.org");
+	for (i = 0; i < nrows; i++) {
+		const GValue *conv_func_name = NULL;
+		const GValue *values[4];
+		gint j;
+		gboolean allread = TRUE;
+		if (ncols >= 5)
+			conv_func_name = gda_data_model_get_value_at (model, 4, i, NULL);
+		for (j = 0; j < 4; j++) {
+			values[j] = gda_data_model_get_value_at (model, j, i, NULL);
+			if (!values [j]) {
+				allread = FALSE;
+				break;
+			}
+		}
+		if (allread && (G_VALUE_TYPE (values[1]) == G_TYPE_STRING)) {
+			GdaPostgresTypeOid *td;
+			td = g_new0 (GdaPostgresTypeOid, 1);
+			td->name = g_value_dup_string (values [1]);
+			td->oid = (guint) g_ascii_strtoull (g_value_get_string (values[0]), NULL, 10);
+			td->type = postgres_name_to_g_type (td->name,
+							   conv_func_name ? g_value_get_string (conv_func_name) : NULL);
+			if (G_VALUE_TYPE (values[3]) == G_TYPE_STRING)
+				td->comments = g_value_dup_string (values [3]);
+			if (G_VALUE_TYPE (values[2]) == G_TYPE_STRING)
+				td->owner = g_value_dup_string (values [2]);
+
+			g_hash_table_insert (rdata->types_oid_hash, &(td->oid), td);
+			g_hash_table_insert (rdata->types_dbtype_hash, &(td->name), td);
+		}
+	}
+
+	/* Make a string of data types internal to postgres and not returned, for future queries */
+        string = NULL;
+        nrows = gda_data_model_get_n_rows (model_avoid);
+        for (i = 0; i < nrows; i++) {
+		const GValue *cvalue;
+
+		cvalue = gda_data_model_get_value_at (model_avoid, 0, i, NULL);
+		if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING)) {
+			if (!string)
+				string = g_string_new (g_value_get_string (cvalue));
+			else {
+				g_string_append (string, ", ");
+				g_string_append (string, g_value_get_string (cvalue));
+			}
+		}
+        }
+        rdata->avoid_types = avoid_types;
+	if (string)
+		rdata->avoid_types_oids = g_string_free (string, FALSE);
+
+	g_object_unref (model);
+	g_object_unref (model_avoid);
+
+        /* make a string of the oid of type 'any' */
+        rdata->any_type_oid = NULL;
+        if (model_anyoid) {
+                if (gda_data_model_get_n_rows (model_anyoid) == 1) {
+			const GValue *cvalue;
+			cvalue = gda_data_model_get_value_at (model_anyoid, 0, 0, NULL);
+			if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
+				rdata->any_type_oid = g_value_dup_string (cvalue);
+                }
+		g_object_unref (model_anyoid);
+        }
+}
+
+GType
+_gda_postgres_type_oid_to_gda (GdaConnection *cnc, GdaPostgresReuseable *rdata, unsigned int postgres_oid)
+{
+	GdaPostgresTypeOid *type;
+	guint id;
+	id = postgres_oid;
+
+	_gda_postgres_compute_types (cnc, rdata);
+	type = g_hash_table_lookup (rdata->types_oid_hash, &id);
+	if (type)
+		return type->type;
+	else
+		return G_TYPE_STRING;
+}
+
+GType
+_gda_postgres_reuseable_get_g_type (GdaConnection *cnc, GdaProviderReuseable *rdata, const gchar *db_type)
+{
+	GdaPostgresTypeOid *type;
+	g_return_val_if_fail (db_type, GDA_TYPE_NULL);
+
+	_gda_postgres_compute_types (cnc, (GdaPostgresReuseable*)rdata);
+	type = g_hash_table_lookup (((GdaPostgresReuseable*)rdata)->types_dbtype_hash, db_type);
+	if (type)
+		return type->type;
+	else
+		return GDA_TYPE_NULL;
+}
+
+
+GdaSqlReservedKeywordsFunc
+_gda_postgres_reuseable_get_reserved_keywords_func (GdaProviderReuseable *rdata)
+{
+	if (rdata && rdata->version_minor) {
+		switch (*rdata->version_minor) {
+                case '8':
+                        if (rdata->version_minor[1] == '2')
+                                return V82is_keyword;
+                        if (rdata->version_minor[1] == '3')
+                                return V83is_keyword;
+                        if (rdata->version_minor[1] == '4')
+                                return V84is_keyword;
+                        return V84is_keyword;
+                default:
+                        return V84is_keyword;
+                break;
+                }
+	}
+        return V84is_keyword;
+}
+
+GdaSqlParser *
+_gda_postgres_reuseable_create_parser (GdaProviderReuseable *rdata)
+{
+	return GDA_SQL_PARSER (g_object_new (GDA_TYPE_POSTGRES_PARSER, NULL));
+}
diff --git a/providers/reuseable/postgres/gda-postgres-reuseable.h b/providers/reuseable/postgres/gda-postgres-reuseable.h
new file mode 100644
index 0000000..7765e13
--- /dev/null
+++ b/providers/reuseable/postgres/gda-postgres-reuseable.h
@@ -0,0 +1,78 @@
+/* GDA 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_POSTGRES_REUSEABLE_H__
+#define __GDA_POSTGRES_REUSEABLE_H__
+
+#include "../gda-provider-reuseable.h"
+#include "gda-postgres-meta.h"
+
+G_BEGIN_DECLS
+
+/*
+ * Specific structure for PostgreSQL
+ *
+ * Note: each GdaPostgresTypeOid is managed by the types_oid_hash hash table, and not by the types_dbtype_hash
+ * even though it's also referenced there.
+ */
+typedef struct {
+	GdaProviderReuseable parent;
+	
+	gfloat               version_float;
+	GHashTable          *types_oid_hash; /* key = an unsigned int * (for OID) , value = a #GdaPostgresTypeOid */
+	GHashTable          *types_dbtype_hash; /* key = a gchar *, value = a #GdaPostgresTypeOid */
+
+	/* Internal data types hidden from the exterior */
+        gchar               *avoid_types; /* may be %NULL */
+        gchar               *avoid_types_oids;
+        gchar               *any_type_oid; /* oid for the 'any' data type, used to fetch aggregates and functions, may be %NULL */
+} GdaPostgresReuseable;
+
+/*
+ * Reuseable implementation
+ */
+GdaProviderReuseable *_gda_postgres_reuseable_new_data (const gchar *major, const gchar *minor);
+void _gda_postgres_reuseable_reset_data (GdaProviderReuseable *rdata);
+GType _gda_postgres_reuseable_get_g_type (GdaConnection *cnc, GdaProviderReuseable *rdata, const gchar *db_type);
+GdaSqlReservedKeywordsFunc _gda_postgres_reuseable_get_reserved_keywords_func (GdaProviderReuseable *rdata);
+GdaSqlParser *_gda_postgres_reuseable_create_parser (GdaProviderReuseable *rdata);
+
+/*
+ * entry point
+ */
+GdaProviderReuseableOperations *_gda_postgres_reuseable_get_ops (void);
+
+/*
+ * Specific API
+ */
+void                _gda_postgres_compute_types (GdaConnection *cnc, GdaPostgresReuseable *rdata);
+
+GType               _gda_postgres_type_oid_to_gda (GdaConnection *cnc, GdaPostgresReuseable *rdata,
+						   unsigned int postgres_oid);
+
+#ifdef GDA_DEBUG
+void                _gda_postgres_test_keywords (void);
+#endif
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/postgres/gen_def.c b/providers/reuseable/postgres/gen_def.c
similarity index 100%
rename from providers/postgres/gen_def.c
rename to providers/reuseable/postgres/gen_def.c
diff --git a/providers/postgres/keywords_82.list b/providers/reuseable/postgres/keywords_82.list
similarity index 100%
rename from providers/postgres/keywords_82.list
rename to providers/reuseable/postgres/keywords_82.list
diff --git a/providers/postgres/keywords_83.list b/providers/reuseable/postgres/keywords_83.list
similarity index 100%
rename from providers/postgres/keywords_83.list
rename to providers/reuseable/postgres/keywords_83.list
diff --git a/providers/postgres/keywords_84.list b/providers/reuseable/postgres/keywords_84.list
similarity index 100%
rename from providers/postgres/keywords_84.list
rename to providers/reuseable/postgres/keywords_84.list
diff --git a/providers/postgres/parser.y b/providers/reuseable/postgres/parser.y
similarity index 100%
rename from providers/postgres/parser.y
rename to providers/reuseable/postgres/parser.y
diff --git a/providers/reuseable/reuse-all.c b/providers/reuseable/reuse-all.c
new file mode 100644
index 0000000..332fe47
--- /dev/null
+++ b/providers/reuseable/reuse-all.c
@@ -0,0 +1,55 @@
+/* GDA 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 "reuse-all.h"
+
+#include "postgres/gda-postgres-reuseable.h"
+
+/**
+ * _gda_provider_reuseable_new
+ * @provider_name: a database provider name
+ * @version_major: the major version number, or %NULL
+ * @version_minor: the minor version number, or %NULL
+ *
+ * Entry point to get provider's reuseable features for a provider.
+ *
+ * Returns: a new GdaProviderReuseable pointer, or %NULL
+ */
+GdaProviderReuseable *
+_gda_provider_reuseable_new (const gchar *provider_name,
+			     const gchar *version_major,
+			     const gchar *version_minor)
+{
+	GdaProviderReuseable *reuseable = NULL;
+	GdaProviderReuseableOperations *ops = NULL;
+	g_return_val_if_fail (provider_name && *provider_name, NULL);
+	if (!strcmp (provider_name, "PostgreSQL"))
+		ops = _gda_postgres_reuseable_get_ops ();
+
+	if (ops) {
+		reuseable = ops->re_new_data (version_major, version_minor);
+		g_assert (reuseable->operations == ops);
+	}
+
+	return reuseable;
+}
diff --git a/providers/reuseable/reuse-all.h b/providers/reuseable/reuse-all.h
new file mode 100644
index 0000000..d3c79a3
--- /dev/null
+++ b/providers/reuseable/reuse-all.h
@@ -0,0 +1,37 @@
+/* GDA 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_REUSEABLE_H__
+#define __GDA_REUSEABLE_H__
+
+#include "gda-provider-reuseable.h"
+
+G_BEGIN_DECLS
+
+GdaProviderReuseable *_gda_provider_reuseable_new (const gchar *provider_name,
+						   const gchar *version_major,
+						   const gchar *version_minor);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/Makefile.am b/providers/web/Makefile.am
new file mode 100644
index 0000000..2dbb2fb
--- /dev/null
+++ b/providers/web/Makefile.am
@@ -0,0 +1,69 @@
+providerdir=$(libdir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/providers
+provider_LTLIBRARIES = libgda-web.la
+
+#Rem: WEB_CFLAGS and WEB_LIBS are the compile and link flags necessary to use the
+# C API. It is specific to the API and should be computed in the configure.in script.
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libgda \
+	-I$(top_builddir) \
+	$(LIBGDA_CFLAGS) $(LIBSOUP_CFLAGS) 
+
+libgda_web_la_SOURCES = \
+	gda-web-blob-op.c \
+	gda-web-blob-op.h \
+	gda-web-ddl.c \
+	gda-web-ddl.h \
+	gda-web-provider.c \
+	gda-web-provider.h \
+	gda-web-pstmt.h \
+	gda-web-pstmt.c \
+	gda-web-meta.c \
+	gda-web-meta.h \
+	gda-web-recordset.c \
+	gda-web-recordset.h \
+	gda-web-util.h \
+	gda-web-util.c \
+	gda-web.h \
+	$(top_srcdir)/libgda/global.h \
+	$(top_srcdir)/libgda/md5.h \
+	$(top_srcdir)/libgda/md5c.c \
+	libmain.c
+
+libgda_web_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED) $(LIBTOOL_PROV_EXPORT_OPTIONS)
+libgda_web_la_LIBADD = \
+	$(top_builddir)/providers/reuseable/libreuseable.la \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(LIBGDA_LIBS) $(LIBSOUP_LIBS)
+
+xmldir   = $(datadir)/libgda-4.0
+xml_in_files = \
+	web_specs_dsn.xml.in \
+	web_specs_auth.xml.in
+
+ INTLTOOL_XML_RULE@
+
+xml_DATA = $(xml_in_files:.xml.in=.xml) 
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgda-web-4.0.pc
+
+phpdir = $(datadir)/libgda-4.0/php
+php_DATA = \
+	php/README \
+	php/gda-clean.php \
+	php/gda-config.php \
+	php/gda-exception.php \
+	php/gda-front.php \
+	php/gda-meta.php \
+	php/gda-setup.php \
+	php/gda-tester.php \
+	php/gda-utils.php \
+	php/gda-worker.php
+
+
+EXTRA_DIST = $(xml_in_files) libgda-web-4.0.pc.in \
+	$(php_DATA)
+
+DISTCLEANFILES = $(xml_DATA)
diff --git a/providers/web/README b/providers/web/README
new file mode 100644
index 0000000..86d6437
--- /dev/null
+++ b/providers/web/README
@@ -0,0 +1,63 @@
+GDA Web provider
+================
+
+Unlike most other provider, this provider does not link to a database
+specific library but rather acts as a web client for a web server
+running some PHP scripts which acutally make the web server connect
+to a database.
+
+This provider enables a Libgda application to connect to a database
+which is accessible only behind a web server (as many hosting databases
+are) which can execute some PHP scripts and can connect to the
+database. It is necessary to install some specific PHP scripts on the
+web server which handle the connection and implement the protocol
+described hereafter. Also the PHP interpreter need to be compiled with
+the SimpleXML extension.
+
+It is recommended to use an SSL/TLS web server to avoid clear text
+messages being read by third parties, but even with non SSL/TLS
+connections, the protocol features a reasonnable security by:
+* including a message hash in each message to avoid man in the middle
+  message contents tampering
+* making the server generate a challenge which has to be encoded in the
+  next request by the client to authenticate the client
+* avoiding sending any database name, server name, user name or password
+  in any message
+* having to define, on the web server side, connections which can be opened
+  through the provider; each connection is composed of an arbitrary name, 
+  an arbitrary password, and the real connection string the web server
+  will use to actually open the connection to the database. 
+
+The exchanges between the provider (client) and the web server are based
+on XML as each message has the following structure:
+
+"
+[message hash]
+<?xml version="1.0"?>
+...
+"
+
+where the [message hash] is a hash for the remaining part of the
+message which is a valid XML message, compiled using the HMAC MD5
+algorithm (see http://en.wikipedia.org/wiki/HMAC) using (except for
+bootstrapping the authentication) a key which is defined as:
+"
+[connection name]/AND/[password]
+"
+where [connection name] and [password] have to represent a connection
+described on the web server side.
+
+For example opening a connection using the web provider from the gda-sql
+tool can be achieved by the shell command:
+
+[prompt]> gda-sql "web://none:MyPass1 HOST=example com;PATH=libgda-php;DB_NAME=cnc1;PORT=8081;SECRET=MySecret"
+
+for:
+* the example.com web server
+* on port 8081
+* with a server secret: MySecret
+* the /libgda-php script location
+* the defined connection cnc1
+* the password MyPass1 (note that the gda-sql command requires a username when a password is specified which is
+  the reason for the dummy "none" user name here to avoid any user name prompting, or use the -p command line option
+  otherwise)
diff --git a/providers/web/gda-web-blob-op.c b/providers/web/gda-web-blob-op.c
new file mode 100644
index 0000000..89f0002
--- /dev/null
+++ b/providers/web/gda-web-blob-op.c
@@ -0,0 +1,226 @@
+/* GDA Web provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      TO_ADD: your name and email
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <libgda/libgda.h>
+#include "gda-web.h"
+#include "gda-web-blob-op.h"
+
+struct _GdaWebBlobOpPrivate {
+	GdaConnection *cnc;
+	/* TO_ADD: specific information describing a Blob in the C API */
+};
+
+static void gda_web_blob_op_class_init (GdaWebBlobOpClass *klass);
+static void gda_web_blob_op_init       (GdaWebBlobOp *blob,
+					 GdaWebBlobOpClass *klass);
+static void gda_web_blob_op_finalize   (GObject *object);
+
+static glong gda_web_blob_op_get_length (GdaBlobOp *op);
+static glong gda_web_blob_op_read       (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size);
+static glong gda_web_blob_op_write      (GdaBlobOp *op, GdaBlob *blob, glong offset);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * Object init and finalize
+ */
+GType
+gda_web_blob_op_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (GdaWebBlobOpClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gda_web_blob_op_class_init,
+			NULL,
+			NULL,
+			sizeof (GdaWebBlobOp),
+			0,
+			(GInstanceInitFunc) gda_web_blob_op_init
+		};
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaWebBlobOp", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void
+gda_web_blob_op_init (GdaWebBlobOp *op,
+			   GdaWebBlobOpClass *klass)
+{
+	g_return_if_fail (GDA_IS_WEB_BLOB_OP (op));
+
+	op->priv = g_new0 (GdaWebBlobOpPrivate, 1);
+
+	/* initialize specific structure */
+	TO_IMPLEMENT;
+}
+
+static void
+gda_web_blob_op_class_init (GdaWebBlobOpClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GdaBlobOpClass *blob_class = GDA_BLOB_OP_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = gda_web_blob_op_finalize;
+	blob_class->get_length = gda_web_blob_op_get_length;
+	blob_class->read = gda_web_blob_op_read;
+	blob_class->write = gda_web_blob_op_write;
+}
+
+static void
+gda_web_blob_op_finalize (GObject * object)
+{
+	GdaWebBlobOp *bop = (GdaWebBlobOp *) object;
+
+	g_return_if_fail (GDA_IS_WEB_BLOB_OP (bop));
+
+	/* free specific information */
+	TO_IMPLEMENT;
+
+	g_free (bop->priv);
+	bop->priv = NULL;
+
+	parent_class->finalize (object);
+}
+
+GdaBlobOp *
+gda_web_blob_op_new (GdaConnection *cnc)
+{
+	GdaWebBlobOp *bop;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+
+	bop = g_object_new (GDA_TYPE_WEB_BLOB_OP, NULL);
+	bop->priv->cnc = cnc;
+	
+	return GDA_BLOB_OP (bop);
+}
+
+/*
+ * Get length request
+ */
+static glong
+gda_web_blob_op_get_length (GdaBlobOp *op)
+{
+	GdaWebBlobOp *bop;
+
+	g_return_val_if_fail (GDA_IS_WEB_BLOB_OP (op), -1);
+	bop = GDA_WEB_BLOB_OP (op);
+	g_return_val_if_fail (bop->priv, -1);
+	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
+
+	TO_IMPLEMENT;
+	return -1;
+}
+
+/*
+ * Blob read request
+ */
+static glong
+gda_web_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
+{
+	GdaWebBlobOp *bop;
+	GdaBinary *bin;
+
+	g_return_val_if_fail (GDA_IS_WEB_BLOB_OP (op), -1);
+	bop = GDA_WEB_BLOB_OP (op);
+	g_return_val_if_fail (bop->priv, -1);
+	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
+	if (offset >= G_MAXINT)
+		return -1;
+	g_return_val_if_fail (blob, -1);
+
+	bin = (GdaBinary *) blob;
+	if (bin->data) 
+		g_free (bin->data);
+	bin->data = g_new0 (guchar, size);
+	bin->binary_length = 0;
+
+	/* fetch blob data using C API into bin->data, and set bin->binary_length */
+	TO_IMPLEMENT;
+
+	return bin->binary_length;
+}
+
+/*
+ * Blob write request
+ */
+static glong
+gda_web_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
+{
+	GdaWebBlobOp *bop;
+	GdaBinary *bin;
+	glong nbwritten = -1;
+
+	g_return_val_if_fail (GDA_IS_WEB_BLOB_OP (op), -1);
+	bop = GDA_WEB_BLOB_OP (op);
+	g_return_val_if_fail (bop->priv, -1);
+	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
+	g_return_val_if_fail (blob, -1);
+
+	if (blob->op && (blob->op != op)) {
+		/* use data through blob->op */
+		#define buf_size 16384
+		gint nread = 0;
+		GdaBlob *tmpblob = g_new0 (GdaBlob, 1);
+		gda_blob_set_op (tmpblob, blob->op);
+
+		nbwritten = 0;
+
+		for (nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size);
+		     nread > 0;
+		     nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size)) {
+			glong tmp_written;
+
+			tmp_written = -1; TO_IMPLEMENT;
+			
+			if (tmp_written < 0) {
+				/* treat error */
+				gda_blob_free ((gpointer) tmpblob);
+				return -1;
+			}
+			nbwritten += tmp_written;
+			if (nread < buf_size)
+				/* nothing more to read */
+				break;
+		}
+		gda_blob_free ((gpointer) tmpblob);
+	}
+	else {
+		/* write blob using bin->data and bin->binary_length */
+		bin = (GdaBinary *) blob;
+		nbwritten = -1; TO_IMPLEMENT;
+	}
+
+	return nbwritten;
+}
diff --git a/providers/web/gda-web-blob-op.h b/providers/web/gda-web-blob-op.h
new file mode 100644
index 0000000..7cb4efd
--- /dev/null
+++ b/providers/web/gda-web-blob-op.h
@@ -0,0 +1,57 @@
+/* GDA Web provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      TO_ADD: your name and email
+ *
+ * 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_WEB_BLOB_OP_H__
+#define __GDA_WEB_BLOB_OP_H__
+
+#include <libgda/gda-blob-op.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_WEB_BLOB_OP            (gda_web_blob_op_get_type())
+#define GDA_WEB_BLOB_OP(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_BLOB_OP, GdaWebBlobOp))
+#define GDA_WEB_BLOB_OP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_BLOB_OP, GdaWebBlobOpClass))
+#define GDA_IS_WEB_BLOB_OP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_WEB_BLOB_OP))
+#define GDA_IS_WEB_BLOB_OP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_WEB_BLOB_OP))
+
+typedef struct _GdaWebBlobOp        GdaWebBlobOp;
+typedef struct _GdaWebBlobOpClass   GdaWebBlobOpClass;
+typedef struct _GdaWebBlobOpPrivate GdaWebBlobOpPrivate;
+
+struct _GdaWebBlobOp {
+	GdaBlobOp             parent;
+	GdaWebBlobOpPrivate *priv;
+};
+
+struct _GdaWebBlobOpClass {
+	GdaBlobOpClass        parent_class;
+};
+
+GType         gda_web_blob_op_get_type     (void) G_GNUC_CONST;
+GdaBlobOp    *gda_web_blob_op_new          (GdaConnection *cnc);
+
+/* TO_ADD: more convenient API to create a GdaBlobOp with some specific information as argument */
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web-ddl.c b/providers/web/gda-web-ddl.c
new file mode 100644
index 0000000..4ee9138
--- /dev/null
+++ b/providers/web/gda-web-ddl.c
@@ -0,0 +1,145 @@
+/* GDA Web Provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      TO_ADD: your name and email
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <libgda/libgda.h>
+#include "gda-web-ddl.h"
+
+gchar *
+gda_web_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc, 
+			      GdaServerOperation *op, GError **error)
+{
+	GString *string;
+	const GValue *value;
+	gboolean allok = TRUE;
+	gboolean hasfields = FALSE;
+	gint nrows;
+	gint i;
+	gboolean first;
+	GSList *pkfields = NULL; /* list of GValue* composing the pkey */
+	gint nbpkfields = 0;
+	gchar *tmp;
+
+	/* CREATE TABLE */
+	string = g_string_new ("CREATE TABLE ");
+
+	tmp = gda_server_operation_get_sql_identifier_at (op, cnc, provider, "/TABLE_DEF_P/TABLE_NAME");
+	g_string_append (string, tmp);
+	g_free (tmp);
+	g_string_append (string, " (");
+		
+	/* FIELDS */
+	if (allok) {
+		GdaServerOperationNode *node;
+
+		node = gda_server_operation_get_node_info (op, "/FIELDS_A");
+		g_assert (node);
+
+		/* finding if there is a composed primary key */
+		nrows = gda_data_model_get_n_rows (node->model);
+		for (i = 0; i < nrows; i++) {
+			value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_PKEY/%d", i);
+			if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value)) {
+				tmp = gda_server_operation_get_sql_identifier_at (op, cnc, provider,
+										  "/FIELDS_A/@COLUMN_NAME/%d", i);
+				pkfields = g_slist_append (pkfields, tmp);
+				nbpkfields++;
+			}
+		}
+
+		/* manually defined fields */
+		first = TRUE;
+		for (i = 0; i < nrows; i++) {
+			hasfields = TRUE;
+			if (first) 
+				first = FALSE;
+			else
+				g_string_append (string, ", ");
+				
+			tmp = gda_server_operation_get_sql_identifier_at (op, cnc, provider,
+									  "/FIELDS_A/@COLUMN_NAME/%d", i);
+			g_string_append (string, tmp);
+			g_free (tmp);
+			g_string_append_c (string, ' ');
+				
+			value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_TYPE/%d", i);
+			g_string_append (string, g_value_get_string (value));
+				
+			value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_DEFAULT/%d", i);
+			if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
+				const gchar *str = g_value_get_string (value);
+				if (str && *str) {
+					g_string_append (string, " DEFAULT ");
+					g_string_append (string, str);
+				}
+			}
+				
+			value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_NNUL/%d", i);
+			if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
+				g_string_append (string, " NOT NULL");
+
+			value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_UNIQUE/%d", i);
+			if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
+				g_string_append (string, " UNIQUE");
+				
+			if (nbpkfields == 1) {
+				value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_PKEY/%d", i);
+				if (value && G_VALUE_HOLDS (value, G_TYPE_BOOLEAN) && g_value_get_boolean (value))
+					g_string_append (string, " PRIMARY KEY");
+			}
+				
+			value = gda_server_operation_get_value_at (op, "/FIELDS_A/@COLUMN_CHECK/%d", i);
+			if (value && G_VALUE_HOLDS (value, G_TYPE_STRING)) {
+				const gchar *str = g_value_get_string (value);
+				if (str && *str) {
+					g_string_append (string, " CHECK (");
+					g_string_append (string, str);
+					g_string_append_c (string, ')');
+				}
+			}
+		}
+	}
+
+	/* composed primary key */
+	if (nbpkfields > 1) {
+		GSList *list;
+
+		g_string_append (string, ", PRIMARY KEY (");
+		for (list = pkfields; list; list = list->next) {
+			if (list != pkfields)
+				g_string_append (string, ", ");
+			g_string_append (string, (gchar*) list->data);
+		}
+		g_string_append_c (string, ')');
+	}
+	g_slist_foreach (pkfields, (GFunc) g_free, NULL);
+	g_slist_free (pkfields);
+
+	g_string_append (string, ")");
+
+	if (!hasfields) {
+		allok = FALSE;
+		g_set_error (error, 0, 0, "%s", _("Table to create must have at least one row"));
+	}
+
+	return g_string_free (string, FALSE);
+}
diff --git a/providers/web/gda-web-ddl.h b/providers/web/gda-web-ddl.h
new file mode 100644
index 0000000..6d3bd28
--- /dev/null
+++ b/providers/web/gda-web-ddl.h
@@ -0,0 +1,34 @@
+/* GDA Web provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      TO_ADD: your name and email
+ *
+ * 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_WEB_DDL_H__
+#define __GDA_WEB_DDL_H__
+
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+gchar *gda_web_render_CREATE_TABLE (GdaServerProvider *provider, GdaConnection *cnc, 
+				     GdaServerOperation *op, GError **error);
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web-meta.c b/providers/web/gda-web-meta.c
new file mode 100644
index 0000000..e4bc0a3
--- /dev/null
+++ b/providers/web/gda-web-meta.c
@@ -0,0 +1,1382 @@
+/* GDA 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-web.h"
+#include "gda-web-meta.h"
+#include "gda-web-provider.h"
+#include <libgda/gda-meta-store.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/gda-connection-private.h>
+#include <libgda/gda-data-model-array.h>
+#include <libgda/gda-set.h>
+#include <libgda/gda-holder.h>
+
+#include "gda-web-util.h"
+
+/*
+ * Meta initialization
+ */
+void
+_gda_web_provider_meta_init (GdaServerProvider *provider)
+{
+}
+
+/*
+ * ... is a list of (arg name, arg value) as strings, terminated with NULL
+ */
+static GdaDataModel *
+run_meta_command_args (GdaConnection *cnc, WebConnectionData *cdata, const gchar *type, GError **error, ...)
+{
+	/* send message */
+	xmlDocPtr doc;
+	gchar status;
+	gchar *tmp, *token;
+	GString *string;
+	va_list ap;
+#define MSG__TEMPL "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
+		"<request>\n"						\
+		"  <token>%s</token>\n"					\
+		"  <cmd type=\"%s\">META%s</cmd>\n"			\
+		"</request>"
+
+	string = g_string_new ("");
+	va_start (ap, error);
+	for (tmp = va_arg (ap, gchar*); tmp; tmp = va_arg (ap, gchar*)) {
+		gchar *argval;
+		xmlChar *xargval;
+		argval = va_arg (ap, gchar*);
+		xargval = xmlEncodeSpecialChars (NULL, BAD_CAST argval);
+		g_string_append_printf (string, "<arg name=\"%s\">%s</arg>", tmp, (gchar*) xargval);
+		xmlFree (xargval);
+	}
+	va_end (ap);
+
+	token = _gda_web_compute_token (cdata);
+	tmp = g_strdup_printf (MSG__TEMPL, token, type, string->str);
+	g_string_free (string, TRUE);
+	g_free (token);
+	
+	doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_META, tmp, cdata->key, &status);
+	g_free (tmp);
+	if (!doc)
+		return FALSE;
+	if (status != 'O') {
+		_gda_web_set_connection_error_from_xmldoc (cnc, doc, error);
+		xmlFreeDoc (doc);
+		return FALSE;
+	}
+
+	/* compute returned data model */
+	xmlNodePtr root, node;
+	GdaDataModel *model = NULL;
+	root = xmlDocGetRootElement (doc);
+        for (node = root->children; node; node = node->next) {
+		if (!strcmp ((gchar*) node->name, "gda_array")) {
+			model = gda_data_model_import_new_xml_node (node);
+			break;
+                }
+	}
+	xmlFreeDoc (doc);
+	/*gda_data_model_dump (model, NULL);*/
+
+	if (! model)
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+                             GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+                             _("Can't import data from web server"));
+	return model;
+}
+
+static GdaDataModel *
+run_meta_command (GdaConnection *cnc, WebConnectionData *cdata, const gchar *type, GError **error)
+{
+	return run_meta_command_args (cnc, cdata, type, error, NULL);
+}
+
+gboolean
+_gda_web_meta__info (GdaServerProvider *prov, GdaConnection *cnc, 
+		     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._info)
+			return cdata->reuseable->operations->re_meta_funcs._info (NULL, cnc, store,
+										  context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */	
+	model = run_meta_command (cnc, cdata, "info", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__btypes (GdaServerProvider *prov, GdaConnection *cnc, 
+		       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._btypes)
+			return cdata->reuseable->operations->re_meta_funcs._btypes (NULL, cnc, store,
+										    context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */	
+	model = run_meta_command (cnc, cdata, "btypes", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__udt (GdaServerProvider *prov, GdaConnection *cnc, 
+		    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._udt)
+			return cdata->reuseable->operations->re_meta_funcs._udt (NULL, cnc, store,
+										 context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta_udt (GdaServerProvider *prov, GdaConnection *cnc, 
+		   GdaMetaStore *store, GdaMetaContext *context, GError **error,
+		   const GValue *udt_catalog, const GValue *udt_schema)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.udt)
+			return cdata->reuseable->operations->re_meta_funcs.udt (NULL, cnc, store,
+										context, error, udt_catalog,
+										udt_schema);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+
+gboolean
+_gda_web_meta__udt_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._udt_cols)
+			return cdata->reuseable->operations->re_meta_funcs._udt_cols (NULL, cnc, store,
+										      context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.udt_cols)
+			return cdata->reuseable->operations->re_meta_funcs.udt_cols (NULL, cnc, store,
+										     context, error,
+										     udt_catalog, udt_schema,
+										     udt_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__enums (GdaServerProvider *prov, GdaConnection *cnc, 
+		      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._enums)
+			return cdata->reuseable->operations->re_meta_funcs._enums (NULL, cnc, store,
+										   context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta_enums (GdaServerProvider *prov, GdaConnection *cnc, 
+		     GdaMetaStore *store, GdaMetaContext *context, GError **error,
+		     const GValue *udt_catalog, const GValue *udt_schema, const GValue *udt_name)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.enums)
+			return cdata->reuseable->operations->re_meta_funcs.enums (NULL, cnc, store,
+										  context, error,
+										  udt_catalog, udt_schema,
+										  udt_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+
+gboolean
+_gda_web_meta__domains (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._domains)
+			return cdata->reuseable->operations->re_meta_funcs._domains (NULL, cnc, store,
+										     context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta_domains (GdaServerProvider *prov, GdaConnection *cnc, 
+		       GdaMetaStore *store, GdaMetaContext *context, GError **error,
+		       const GValue *domain_catalog, const GValue *domain_schema)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.domains)
+			return cdata->reuseable->operations->re_meta_funcs.domains (NULL, cnc, store,
+										    context, error,
+										    domain_catalog, domain_schema);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._constraints_dom)
+			return cdata->reuseable->operations->re_meta_funcs._constraints_dom (NULL, cnc, store,
+											     context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.constraints_dom)
+			return cdata->reuseable->operations->re_meta_funcs.constraints_dom (NULL, cnc, store,
+											    context, error,
+											    domain_catalog, domain_schema,
+											    domain_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__el_types (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._el_types)
+			return cdata->reuseable->operations->re_meta_funcs._el_types (NULL, cnc, store,
+										      context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta_el_types (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			const GValue *specific_name)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.el_types)
+			return cdata->reuseable->operations->re_meta_funcs.el_types (NULL, cnc, store,
+										     context, error,
+										     specific_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__collations (GdaServerProvider *prov, GdaConnection *cnc, 
+			   GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._collations)
+			return cdata->reuseable->operations->re_meta_funcs._collations (NULL, cnc, store,
+											context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.collations)
+			return cdata->reuseable->operations->re_meta_funcs.collations (NULL, cnc, store,
+										       context, error,
+										       collation_catalog,
+										       collation_schema, 
+										       collation_name_n);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__character_sets (GdaServerProvider *prov, GdaConnection *cnc, 
+			       GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._character_sets)
+			return cdata->reuseable->operations->re_meta_funcs._character_sets (NULL, cnc, store,
+											    context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.character_sets)
+			return cdata->reuseable->operations->re_meta_funcs.character_sets (NULL, cnc, store,
+											   context, error,
+											   chset_catalog, chset_schema, 
+											   chset_name_n);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__schemata (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._schemata)
+			return cdata->reuseable->operations->re_meta_funcs._schemata (NULL, cnc, store,
+										      context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "schemas", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta_schemata (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error, 
+			const GValue *catalog_name, const GValue *schema_name_n)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.schemata)
+			return cdata->reuseable->operations->re_meta_funcs.schemata (NULL, cnc, store,
+										     context, error, catalog_name, schema_name_n);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	if (schema_name_n)
+		model = run_meta_command_args (cnc, cdata, "schemas", error,
+					       "catalog_name", g_value_get_string (catalog_name),
+					       "schema_name", g_value_get_string (schema_name_n), NULL);
+	else
+		model = run_meta_command_args (cnc, cdata, "schemas", error,
+					       "catalog_name", g_value_get_string (catalog_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__tables_views (GdaServerProvider *prov, GdaConnection *cnc, 
+			     GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *tables_model, *views_model;
+	gboolean retval = TRUE;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._tables_views)
+			return cdata->reuseable->operations->re_meta_funcs._tables_views (NULL, cnc, store,
+											  context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	tables_model = run_meta_command (cnc, cdata, "tables", error);
+	if (!tables_model)
+		return FALSE;
+	views_model = run_meta_command (cnc, cdata, "views", error);
+	if (!views_model) {
+		g_object_unref (tables_model);
+		return FALSE;
+	}
+
+	GdaMetaContext c2;
+	c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+	if (retval) {
+		c2.table_name = "_tables";
+		retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+	}
+	if (retval) {
+		c2.table_name = "_views";
+		retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+	}
+	g_object_unref (tables_model);
+	g_object_unref (views_model);
+
+	return retval;
+}
+
+gboolean
+_gda_web_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)
+{
+	GdaDataModel *tables_model, *views_model;
+	gboolean retval = TRUE;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.tables_views)
+			return cdata->reuseable->operations->re_meta_funcs.tables_views (NULL, cnc, store,
+											 context, error,
+											 table_catalog, table_schema,
+											 table_name_n);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	if (table_name_n)
+		tables_model = run_meta_command_args (cnc, cdata, "tables", error,
+						      "table_catalog", g_value_get_string (table_catalog),
+						      "table_schema", g_value_get_string (table_schema),
+						      "table_name", g_value_get_string (table_name_n), NULL);
+	else
+		tables_model = run_meta_command_args (cnc, cdata, "tables", error,
+						      "table_catalog", g_value_get_string (table_catalog),
+						      "table_schema", g_value_get_string (table_schema), NULL);
+	if (!tables_model)
+		return FALSE;
+
+	if (table_name_n)
+		tables_model = run_meta_command_args (cnc, cdata, "views", error,
+						      "table_catalog", g_value_get_string (table_catalog),
+						      "table_schema", g_value_get_string (table_schema),
+						      "table_name", g_value_get_string (table_name_n), NULL);
+	else
+		tables_model = run_meta_command_args (cnc, cdata, "views", error,
+						      "table_catalog", g_value_get_string (table_catalog),
+						      "table_schema", g_value_get_string (table_schema), NULL);
+	if (!views_model) {
+		g_object_unref (tables_model);
+		return FALSE;
+	}
+
+	GdaMetaContext c2;
+	c2 = *context; /* copy contents, just because we need to modify @context->table_name */
+	if (retval) {
+		c2.table_name = "_tables";
+		retval = gda_meta_store_modify_with_context (store, &c2, tables_model, error);
+	}
+	if (retval) {
+		c2.table_name = "_views";
+		retval = gda_meta_store_modify_with_context (store, &c2, views_model, error);
+	}
+	g_object_unref (tables_model);
+	g_object_unref (views_model);
+
+	return retval;
+}
+
+gboolean
+_gda_web_meta__columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._columns)
+			return cdata->reuseable->operations->re_meta_funcs._columns (NULL, cnc, store,
+										     context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "columns", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+		       GdaMetaStore *store, GdaMetaContext *context, GError **error,
+		       const GValue *table_catalog, const GValue *table_schema, 
+		       const GValue *table_name)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.columns)
+			return cdata->reuseable->operations->re_meta_funcs.columns (NULL, cnc, store,
+										    context, error, table_catalog, table_schema,
+										    table_name);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command_args (cnc, cdata, "columns", error,
+				       "table_catalog", g_value_get_string (table_catalog),
+				       "table_schema", g_value_get_string (table_schema),
+				       "table_name", g_value_get_string (table_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__view_cols (GdaServerProvider *prov, GdaConnection *cnc, 
+			  GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._view_cols)
+			return cdata->reuseable->operations->re_meta_funcs._view_cols (NULL, cnc, store,
+										       context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.view_cols)
+			return cdata->reuseable->operations->re_meta_funcs.view_cols (NULL, cnc, store,
+										      context, error,
+										      view_catalog, view_schema, 
+										      view_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._constraints_tab)
+			return cdata->reuseable->operations->re_meta_funcs._constraints_tab (NULL, cnc, store,
+											     context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "constraints_tab", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_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)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.constraints_tab)
+			return cdata->reuseable->operations->re_meta_funcs.constraints_tab (NULL, cnc, store,
+											    context, error,
+											    table_catalog, table_schema, 
+											    table_name, constraint_name_n);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	if (constraint_name_n)
+		model = run_meta_command_args (cnc, cdata, "constraints_tab", error,
+					       "table_catalog", g_value_get_string (table_catalog),
+					       "table_schema", g_value_get_string (table_schema),
+					       "table_name", g_value_get_string (table_name),
+					       "constraint_name_", g_value_get_string (table_name), NULL);
+	else
+		model = run_meta_command_args (cnc, cdata, "constraints_tab", error,
+					       "table_catalog", g_value_get_string (table_catalog),
+					       "table_schema", g_value_get_string (table_schema),
+					       "table_name", g_value_get_string (table_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._constraints_ref)
+			return cdata->reuseable->operations->re_meta_funcs._constraints_ref (NULL, cnc, store,
+											     context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "constraints_ref", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_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)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.constraints_ref)
+			return cdata->reuseable->operations->re_meta_funcs.constraints_ref (NULL, cnc, store,
+											    context, error,
+											    table_catalog, table_schema,
+											    table_name,
+											    constraint_name);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command_args (cnc, cdata, "constraints_ref", error,
+				       "table_catalog", g_value_get_string (table_catalog),
+				       "table_schema", g_value_get_string (table_schema),
+				       "table_name", g_value_get_string (table_name),
+				       "constraint_name_", g_value_get_string (table_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__key_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._key_columns)
+			return cdata->reuseable->operations->re_meta_funcs._key_columns (NULL, cnc, store,
+											 context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "key_columns", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_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)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.key_columns)
+			return cdata->reuseable->operations->re_meta_funcs.key_columns (NULL, cnc, store,
+											context, error,
+											table_catalog, table_schema,
+											table_name,
+											constraint_name);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command_args (cnc, cdata, "key_columns", error,
+				       "table_catalog", g_value_get_string (table_catalog),
+				       "table_schema", g_value_get_string (table_schema),
+				       "table_name", g_value_get_string (table_name),
+				       "constraint_name_", g_value_get_string (table_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__check_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._check_columns)
+			return cdata->reuseable->operations->re_meta_funcs._check_columns (NULL, cnc, store,
+											   context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "check_columns", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_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)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.check_columns)
+			return cdata->reuseable->operations->re_meta_funcs.check_columns (NULL, cnc, store,
+											  context, error,
+											  table_catalog, table_schema,
+											  table_name,
+											  constraint_name);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command_args (cnc, cdata, "check_columns", error,
+				       "table_catalog", g_value_get_string (table_catalog),
+				       "table_schema", g_value_get_string (table_schema),
+				       "table_name", g_value_get_string (table_name),
+				       "constraint_name_", g_value_get_string (table_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__triggers (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._triggers)
+			return cdata->reuseable->operations->re_meta_funcs._triggers (NULL, cnc, store,
+										      context, error);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command (cnc, cdata, "triggers", error);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta_triggers (GdaServerProvider *prov, GdaConnection *cnc, 
+			GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			const GValue *table_catalog, const GValue *table_schema, 
+			const GValue *table_name)
+{
+	GdaDataModel *model;
+	gboolean retval;
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.triggers)
+			return cdata->reuseable->operations->re_meta_funcs.triggers (NULL, cnc, store,
+										     context, error,
+										     table_catalog, table_schema,
+										     table_name);
+		else
+			return TRUE;
+	}
+
+	/* fallback to default method */
+	model = run_meta_command_args (cnc, cdata, "triggers", error,
+				       "table_catalog", g_value_get_string (table_catalog),
+				       "table_schema", g_value_get_string (table_schema),
+				       "table_name", g_value_get_string (table_name), NULL);
+	if (!model)
+		return FALSE;
+	retval = gda_meta_store_modify_with_context (store, context, model, error);
+	g_object_unref (model);
+		
+	return retval;
+}
+
+gboolean
+_gda_web_meta__routines (GdaServerProvider *prov, GdaConnection *cnc, 
+			 GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._routines)
+			return cdata->reuseable->operations->re_meta_funcs._routines (NULL, cnc, store,
+										      context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.routines)
+			return cdata->reuseable->operations->re_meta_funcs.routines (NULL, cnc, store,
+										     context, error,
+										     routine_catalog, routine_schema, 
+										     routine_name_n);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__routine_col (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._routine_col)
+			return cdata->reuseable->operations->re_meta_funcs._routine_col (NULL, cnc, store,
+											 context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.routine_col)
+			return cdata->reuseable->operations->re_meta_funcs.routine_col (NULL, cnc, store,
+											context, error,
+											rout_catalog, rout_schema, 
+											rout_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_meta__routine_par (GdaServerProvider *prov, GdaConnection *cnc, 
+			    GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs._routine_par)
+			return cdata->reuseable->operations->re_meta_funcs._routine_par (NULL, cnc, store,
+											 context, error);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
+
+gboolean
+_gda_web_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)
+{
+	WebConnectionData *cdata;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* use reuseable methods if available */
+	if (cdata->reuseable) {
+		if (cdata->reuseable->operations->re_meta_funcs.routine_par)
+			return cdata->reuseable->operations->re_meta_funcs.routine_par (NULL, cnc, store,
+											context, error,
+											rout_catalog, rout_schema, 
+											rout_name);
+		else
+			return TRUE;
+	}
+
+	/* no default method */
+	return TRUE;
+}
diff --git a/providers/web/gda-web-meta.h b/providers/web/gda-web-meta.h
new file mode 100644
index 0000000..9476955
--- /dev/null
+++ b/providers/web/gda-web-meta.h
@@ -0,0 +1,197 @@
+/* GDA 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_WEB_META_H__
+#define __GDA_WEB_META_H__
+
+#include <libgda/gda-server-provider.h>
+
+G_BEGIN_DECLS
+
+void     _gda_web_provider_meta_init    (GdaServerProvider *provider);
+
+/* _information_schema_catalog_name */
+gboolean _gda_web_meta__info            (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+
+/* _builtin_data_types */
+gboolean _gda_web_meta__btypes          (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+
+/* _udt */
+gboolean _gda_web_meta__udt             (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_udt              (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					 const GValue *udt_catalog, const GValue *udt_schema);
+
+/* _udt_columns */
+gboolean _gda_web_meta__udt_cols        (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__enums           (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__domains         (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_domains          (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					 const GValue *domain_catalog, const GValue *domain_schema);
+
+/* _domain_constraints */
+gboolean _gda_web_meta__constraints_dom (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__el_types        (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_meta_el_types         (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error,
+					 const GValue *specific_name);
+
+/* _collations */
+gboolean _gda_web_meta__collations      (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__character_sets  (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__schemata        (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__tables_views    (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__columns         (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__view_cols       (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__key_columns     (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__check_columns   (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__triggers        (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__routines       (GdaServerProvider *prov, GdaConnection *cnc, 
+					GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__routine_col     (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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_web_meta__routine_par     (GdaServerProvider *prov, GdaConnection *cnc, 
+					 GdaMetaStore *store, GdaMetaContext *context, GError **error);
+gboolean _gda_web_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/providers/web/gda-web-provider.c b/providers/web/gda-web-provider.c
new file mode 100644
index 0000000..4841bda
--- /dev/null
+++ b/providers/web/gda-web-provider.c
@@ -0,0 +1,1686 @@
+/* GDA 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <libgda/libgda.h>
+#include <libgda/gda-data-model-private.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include <libgda/gda-statement-extra.h>
+#include <sql-parser/gda-sql-parser.h>
+#include "gda-web.h"
+#include "gda-web-provider.h"
+#include "gda-web-recordset.h"
+#include "gda-web-ddl.h"
+#include "gda-web-meta.h"
+#include "gda-web-util.h"
+
+/* Use the RSA reference implementation included in the RFC-1321, http://www.freesoft.org/CIE/RFC/1321/ */
+#include "global.h"
+#include "md5.h"
+
+#define _GDA_PSTMT(x) ((GdaPStmt*)(x))
+
+/*
+ * GObject methods
+ */
+static void gda_web_provider_class_init (GdaWebProviderClass *klass);
+static void gda_web_provider_init       (GdaWebProvider *provider,
+					 GdaWebProviderClass *klass);
+static GObjectClass *parent_class = NULL;
+
+/*
+ * GdaServerProvider's virtual methods
+ */
+/* connection management */
+static gboolean            gda_web_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+							     GdaQuarkList *params, GdaQuarkList *auth,
+							     guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
+static gboolean            gda_web_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar        *gda_web_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc);
+static const gchar        *gda_web_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc);
+
+/* DDL operations */
+static gboolean            gda_web_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+								GdaServerOperationType type, GdaSet *options);
+static GdaServerOperation *gda_web_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+							      GdaServerOperationType type,
+							      GdaSet *options, GError **error);
+static gchar              *gda_web_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+							      GdaServerOperation *op, GError **error);
+
+static gboolean            gda_web_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+							       GdaServerOperation *op, guint *task_id, 
+							       GdaServerProviderAsyncCallback async_cb, gpointer cb_data,
+							       GError **error);
+/* transactions */
+static gboolean            gda_web_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+							       const gchar *name, GdaTransactionIsolation level, GError **error);
+static gboolean            gda_web_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+								const gchar *name, GError **error);
+static gboolean            gda_web_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection * cnc,
+								  const gchar *name, GError **error);
+static gboolean            gda_web_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+							   const gchar *name, GError **error);
+static gboolean            gda_web_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+								const gchar *name, GError **error);
+static gboolean            gda_web_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+							      const gchar *name, GError **error);
+
+/* information retrieval */
+static const gchar        *gda_web_provider_get_version (GdaServerProvider *provider);
+static gboolean            gda_web_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc,
+							      GdaConnectionFeature feature);
+
+static const gchar        *gda_web_provider_get_name (GdaServerProvider *provider);
+
+static GdaDataHandler     *gda_web_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+							      GType g_type, const gchar *dbms_type);
+
+static const gchar*        gda_web_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc,
+								   GType type);
+/* statements */
+static GdaSqlParser        *gda_web_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
+static gchar               *gda_web_provider_statement_to_sql  (GdaServerProvider *provider, GdaConnection *cnc,
+								GdaStatement *stmt, GdaSet *params, 
+								GdaStatementSqlFlag flags,
+								GSList **params_used, GError **error);
+static gboolean             gda_web_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+								GdaStatement *stmt, GError **error);
+static GObject             *gda_web_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+								GdaStatement *stmt, GdaSet *params,
+								GdaStatementModelUsage model_usage, 
+								GType *col_types, GdaSet **last_inserted_row, 
+								guint *task_id, GdaServerProviderExecCallback async_cb, 
+								gpointer cb_data, GError **error);
+
+/* distributed transactions */
+static gboolean gda_web_provider_xa_start    (GdaServerProvider *provider, GdaConnection *cnc, 
+					      const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_web_provider_xa_end      (GdaServerProvider *provider, GdaConnection *cnc, 
+					      const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_web_provider_xa_prepare  (GdaServerProvider *provider, GdaConnection *cnc, 
+					      const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_web_provider_xa_commit   (GdaServerProvider *provider, GdaConnection *cnc, 
+					      const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_web_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+					      const GdaXaTransactionId *xid, GError **error);
+
+static GList   *gda_web_provider_xa_recover  (GdaServerProvider *provider, GdaConnection *cnc, 
+					      GError **error);
+
+
+/*
+ * GdaWebProvider class implementation
+ */
+static void
+gda_web_provider_class_init (GdaWebProviderClass *klass)
+{
+	GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	provider_class->get_version = gda_web_provider_get_version;
+	provider_class->get_server_version = gda_web_provider_get_server_version;
+	provider_class->get_name = gda_web_provider_get_name;
+	provider_class->supports_feature = gda_web_provider_supports_feature;
+
+	provider_class->get_data_handler = gda_web_provider_get_data_handler;
+	provider_class->get_def_dbms_type = gda_web_provider_get_default_dbms_type;
+
+	provider_class->open_connection = gda_web_provider_open_connection;
+	provider_class->close_connection = gda_web_provider_close_connection;
+	provider_class->get_database = gda_web_provider_get_database;
+
+	provider_class->supports_operation = gda_web_provider_supports_operation;
+        provider_class->create_operation = gda_web_provider_create_operation;
+        provider_class->render_operation = gda_web_provider_render_operation;
+        provider_class->perform_operation = gda_web_provider_perform_operation;
+
+	provider_class->begin_transaction = gda_web_provider_begin_transaction;
+	provider_class->commit_transaction = gda_web_provider_commit_transaction;
+	provider_class->rollback_transaction = gda_web_provider_rollback_transaction;
+	provider_class->add_savepoint = gda_web_provider_add_savepoint;
+        provider_class->rollback_savepoint = gda_web_provider_rollback_savepoint;
+        provider_class->delete_savepoint = gda_web_provider_delete_savepoint;
+
+	provider_class->create_parser = gda_web_provider_create_parser;
+	provider_class->statement_to_sql = gda_web_provider_statement_to_sql;
+	provider_class->statement_prepare = gda_web_provider_statement_prepare;
+	provider_class->statement_execute = gda_web_provider_statement_execute;
+
+	provider_class->is_busy = NULL;
+	provider_class->cancel = NULL;
+	provider_class->create_connection = NULL;
+
+	memset (&(provider_class->meta_funcs), 0, sizeof (GdaServerProviderMeta));
+	provider_class->meta_funcs._info = _gda_web_meta__info;
+	provider_class->meta_funcs._btypes = _gda_web_meta__btypes;
+	provider_class->meta_funcs._udt = _gda_web_meta__udt;
+	provider_class->meta_funcs.udt = _gda_web_meta_udt;
+	provider_class->meta_funcs._udt_cols = _gda_web_meta__udt_cols;
+	provider_class->meta_funcs.udt_cols = _gda_web_meta_udt_cols;
+	provider_class->meta_funcs._enums = _gda_web_meta__enums;
+	provider_class->meta_funcs.enums = _gda_web_meta_enums;
+	provider_class->meta_funcs._domains = _gda_web_meta__domains;
+	provider_class->meta_funcs.domains = _gda_web_meta_domains;
+	provider_class->meta_funcs._constraints_dom = _gda_web_meta__constraints_dom;
+	provider_class->meta_funcs.constraints_dom = _gda_web_meta_constraints_dom;
+	provider_class->meta_funcs._el_types = _gda_web_meta__el_types;
+	provider_class->meta_funcs.el_types = _gda_web_meta_el_types;
+	provider_class->meta_funcs._collations = _gda_web_meta__collations;
+	provider_class->meta_funcs.collations = _gda_web_meta_collations;
+	provider_class->meta_funcs._character_sets = _gda_web_meta__character_sets;
+	provider_class->meta_funcs.character_sets = _gda_web_meta_character_sets;
+	provider_class->meta_funcs._schemata = _gda_web_meta__schemata;
+	provider_class->meta_funcs.schemata = _gda_web_meta_schemata;
+	provider_class->meta_funcs._tables_views = _gda_web_meta__tables_views;
+	provider_class->meta_funcs.tables_views = _gda_web_meta_tables_views;
+	provider_class->meta_funcs._columns = _gda_web_meta__columns;
+	provider_class->meta_funcs.columns = _gda_web_meta_columns;
+	provider_class->meta_funcs._view_cols = _gda_web_meta__view_cols;
+	provider_class->meta_funcs.view_cols = _gda_web_meta_view_cols;
+	provider_class->meta_funcs._constraints_tab = _gda_web_meta__constraints_tab;
+	provider_class->meta_funcs.constraints_tab = _gda_web_meta_constraints_tab;
+	provider_class->meta_funcs._constraints_ref = _gda_web_meta__constraints_ref;
+	provider_class->meta_funcs.constraints_ref = _gda_web_meta_constraints_ref;
+	provider_class->meta_funcs._key_columns = _gda_web_meta__key_columns;
+	provider_class->meta_funcs.key_columns = _gda_web_meta_key_columns;
+	provider_class->meta_funcs._check_columns = _gda_web_meta__check_columns;
+	provider_class->meta_funcs.check_columns = _gda_web_meta_check_columns;
+	provider_class->meta_funcs._triggers = _gda_web_meta__triggers;
+	provider_class->meta_funcs.triggers = _gda_web_meta_triggers;
+	provider_class->meta_funcs._routines = _gda_web_meta__routines;
+	provider_class->meta_funcs.routines = _gda_web_meta_routines;
+	provider_class->meta_funcs._routine_col = _gda_web_meta__routine_col;
+	provider_class->meta_funcs.routine_col = _gda_web_meta_routine_col;
+	provider_class->meta_funcs._routine_par = _gda_web_meta__routine_par;
+	provider_class->meta_funcs.routine_par = _gda_web_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);
+	provider_class->xa_funcs->xa_start = gda_web_provider_xa_start;
+	provider_class->xa_funcs->xa_end = gda_web_provider_xa_end;
+	provider_class->xa_funcs->xa_prepare = gda_web_provider_xa_prepare;
+	provider_class->xa_funcs->xa_commit = gda_web_provider_xa_commit;
+	provider_class->xa_funcs->xa_rollback = gda_web_provider_xa_rollback;
+	provider_class->xa_funcs->xa_recover = gda_web_provider_xa_recover;
+
+	/* provider is thread safe */
+	provider_class->limiting_thread = NULL;
+}
+
+static void
+gda_web_provider_init (GdaWebProvider *web_prv, GdaWebProviderClass *klass)
+{
+}
+
+GType
+gda_web_provider_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static GTypeInfo info = {
+			sizeof (GdaWebProviderClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gda_web_provider_class_init,
+			NULL, NULL,
+			sizeof (GdaWebProvider),
+			0,
+			(GInstanceInitFunc) gda_web_provider_init
+		};
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaWebProvider", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+
+	return type;
+}
+
+
+/*
+ * Get provider name request
+ */
+static const gchar *
+gda_web_provider_get_name (GdaServerProvider *provider)
+{
+	return WEB_PROVIDER_NAME;
+}
+
+/* 
+ * Get provider's version, no need to change this
+ */
+static const gchar *
+gda_web_provider_get_version (GdaServerProvider *provider)
+{
+	return PACKAGE_VERSION;
+}
+
+static gboolean
+do_server_setup (GdaConnection *cnc, WebConnectionData *cdata)
+{
+	SoupMessage *msg;
+	guint status;
+	gchar *real_url;
+
+	real_url = g_strdup_printf ("%s/gda-setup.php", cdata->server_base_url);
+	msg = soup_message_new ("GET", real_url);
+	if (!msg) {
+		gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
+		g_free (real_url);
+		return FALSE;
+	}
+	g_free (real_url);
+
+	g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 5, NULL);
+	status = soup_session_send_message (cdata->front_session, msg);
+	if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+		gda_connection_add_event_string (cnc, msg->reason_phrase);
+		g_object_unref (msg);
+		return FALSE;
+	}
+
+	xmlDocPtr doc;
+	gchar out_status_chr;
+	doc = _gda_web_decode_response (cnc, cdata, msg->response_body, &out_status_chr, NULL);
+	g_object_unref (msg);
+	if (doc) {
+		if (out_status_chr != 'O') {
+			_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+			xmlFreeDoc (doc);
+			return FALSE;
+		}
+		xmlFreeDoc (doc);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/* 
+ * Open connection request
+ *
+ * In this function, the following _must_ be done:
+ *   - check for the presence and validify of the parameters required to actually open a connection,
+ *     using @params
+ *   - open the real connection to the database using the parameters previously checked
+ *   - create a WebConnectionData structure and associate it to @cnc
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
+ */
+static gboolean
+gda_web_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+				  GdaQuarkList *params, GdaQuarkList *auth,
+				  guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data)
+{
+	g_return_val_if_fail (GDA_IS_WEB_PROVIDER (provider), FALSE);
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+
+	/* If asynchronous connection opening is not supported, then exit now */
+	if (async_cb) {
+		gda_connection_add_event_string (cnc, _("Provider does not support asynchronous connection open"));
+                return FALSE;
+	}
+
+	/* Check for connection parameters */
+	const gchar *db_name, *host, *path, *port, *serversecret, *pass = NULL, *use_ssl;
+
+	if (auth)
+		pass = gda_quark_list_find (auth, "PASSWORD");
+	if (!pass) {
+		gda_connection_add_event_string (cnc, _("The connection string must contain the %s value"), "PASSWORD");
+                return FALSE;
+	}
+	host = gda_quark_list_find (params, "HOST");
+	if (!host) {
+		gda_connection_add_event_string (cnc,
+						 _("The connection string must contain the %s value"), "HOST");
+		return FALSE;
+	}
+	serversecret = gda_quark_list_find (params, "SECRET");
+	if (!serversecret) {
+		gda_connection_add_event_string (cnc,
+						 _("The connection string must contain the %s value"), "SECRET");
+		return FALSE;
+	}
+	path = gda_quark_list_find (params, "PATH");
+	port = gda_quark_list_find (params, "PORT");
+	db_name = gda_quark_list_find (params, "DB_NAME");
+	if (!db_name) {
+		gda_connection_add_event_string (cnc,
+						 _("The connection string must contain the %s value"), "DB_NAME");
+		return FALSE;
+	}
+	use_ssl = gda_quark_list_find (params, "USE_SSL");
+	if (use_ssl && (*use_ssl != 'T') && (*use_ssl != 't'))
+		use_ssl = NULL;
+	
+	/* open Libsoup session */
+	WebConnectionData *cdata;
+	GString *server_url;
+
+	cdata = g_new0 (WebConnectionData, 1);
+	cdata->mutex = gda_mutex_new ();
+	cdata->server_id = NULL;
+	cdata->forced_closing = FALSE;
+	cdata->worker_session = soup_session_sync_new ();
+	cdata->front_session = soup_session_sync_new_with_options ("max-conns-per-host", 1, NULL);
+	if (use_ssl) {
+		server_url = g_string_new ("https://";);
+		g_print ("USING SSL\n");
+	}
+	else
+		server_url = g_string_new ("http://";);
+	g_string_append (server_url, host);
+	if (port)
+		g_string_append_printf (server_url, ":%s", port);
+	if (path)
+		g_string_append_printf (server_url, "/%s", path);
+	cdata->front_url = g_strdup_printf ("%s/gda-front.php", server_url->str);
+	cdata->worker_url = g_strdup_printf ("%s/gda-worker.php", server_url->str);
+	cdata->server_base_url = g_string_free (server_url, FALSE);
+	if (serversecret)
+		cdata->key = g_strdup (serversecret);
+	gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) _gda_web_free_cnc_data);
+
+	/*
+	 * perform setup
+	 */
+	if (! do_server_setup (cnc, cdata))
+		return FALSE;
+
+	/*
+	 * send HELLO
+	 */
+	xmlDocPtr doc;
+	gchar status;
+#define HELLO_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" \
+		"<request>\n"						\
+		"  <cmd>HELLO</cmd>\n"					\
+		"</request>"
+	doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_HELLO, HELLO_MSG, NULL, &status);
+	if (!doc) {
+		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+		_gda_web_do_server_cleanup (cnc, cdata);
+		return FALSE;
+	}
+	if (status != 'O') {
+		_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+		xmlFreeDoc (doc);
+		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+		_gda_web_do_server_cleanup (cnc, cdata);
+		return FALSE;
+	}
+	xmlFreeDoc (doc);
+
+	/*
+	 * send CONNECT
+	 */
+	gchar *tmp, *token;
+#define CONNECT_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
+		"<request>\n"						\
+		"  <token>%s</token>\n"					\
+		"  <cmd>CONNECT</cmd>\n"				\
+		"</request>"
+	if (cdata->key)
+		g_free (cdata->key);
+	cdata->key = g_strdup_printf ("%s/AND/%s", db_name, pass);
+	
+	token = _gda_web_compute_token (cdata);
+	tmp = g_strdup_printf (CONNECT_MSG, token);
+	g_free (token);
+
+	cdata->server_secret = g_strdup (serversecret);
+	doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_CONNECT, tmp, serversecret, &status);
+	g_free (tmp);
+	if (!doc) {
+		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+		_gda_web_do_server_cleanup (cnc, cdata);
+		return FALSE;
+	}
+	if (status != 'O') {
+		_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+		xmlFreeDoc (doc);
+		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+		_gda_web_do_server_cleanup (cnc, cdata);
+		return FALSE;
+	}
+	xmlFreeDoc (doc);
+
+	/*
+	 * change key: cdata->key = MD5(cdata->key)
+	 */
+	MD5_CTX md5c;
+	unsigned char digest[16];
+	GString *md5str;
+        gint i;
+	MD5Init (&md5c);
+        MD5Update (&md5c, (unsigned char *) cdata->key, strlen (cdata->key));
+        MD5Final (digest, &md5c);
+	md5str = g_string_new ("");
+        for (i = 0; i < 16; i++)
+                g_string_append_printf (md5str, "%02x", digest[i]);
+	g_free (cdata->key);
+	cdata->key = g_string_free (md5str, FALSE);
+
+	return TRUE;
+}
+
+/* 
+ * Close connection request
+ *
+ * In this function, the following _must_ be done:
+ *   - Actually close the connection to the database using @cnc's associated WebConnectionData structure
+ *   - Free the WebConnectionData structure and its contents
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
+ */
+static gboolean
+gda_web_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	gda_mutex_lock (cdata->mutex);
+	if (!cdata->forced_closing && cdata->worker_running) {
+		gda_mutex_unlock (cdata->mutex);
+		/* send BYE message */
+		xmlDocPtr doc;
+		gchar status;
+		gchar *tmp, *token;
+#define BYE_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
+			"<request>\n"					\
+			"  <token>%s</token>\n"				\
+			"  <cmd>BYE</cmd>\n"				\
+			"</request>"	
+		token = _gda_web_compute_token (cdata);
+		tmp = g_strdup_printf (BYE_MSG, token);
+		g_free (token);
+		
+		doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_BYE, tmp, cdata->key, &status);
+		g_free (tmp);
+		if (!doc)
+			return FALSE;
+		if (status != 'C') {
+			_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
+			xmlFreeDoc (doc);
+			return FALSE;
+		}
+		xmlFreeDoc (doc);
+	}
+	else
+		gda_mutex_unlock (cdata->mutex);
+
+	_gda_web_do_server_cleanup (cnc, cdata);
+
+	/* Free the WebConnectionData structure and its contents*/
+	_gda_web_free_cnc_data (cdata);
+	gda_connection_internal_set_provider_data (cnc, NULL, NULL);
+
+	return TRUE;
+}
+
+/*
+ * Server version request
+ *
+ * Returns the server version as a string, which should be stored in @cnc's associated WebConnectionData structure
+ */
+static const gchar *
+gda_web_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+	TO_IMPLEMENT;
+	return NULL;
+}
+
+/*
+ * Get database request
+ *
+ * Returns the database name as a string, which should be stored in @cnc's associated WebConnectionData structure
+ */
+static const gchar *
+gda_web_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return NULL;
+	TO_IMPLEMENT;
+	return NULL;
+}
+
+/*
+ * Support operation request
+ *
+ * Tells what the implemented server operations are. To add support for an operation, the following steps are required:
+ *   - create a web_specs_....xml.in file describing the required and optional parameters for the operation
+ *   - add it to the Makefile.am
+ *   - make this method return TRUE for the operation type
+ *   - implement the gda_web_provider_render_operation() and gda_web_provider_perform_operation() methods
+ *
+ * In this example, the GDA_SERVER_OPERATION_CREATE_TABLE is implemented
+ */
+static gboolean
+gda_web_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
+				     GdaServerOperationType type, GdaSet *options)
+{
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+
+        switch (type) {
+        case GDA_SERVER_OPERATION_CREATE_DB:
+        case GDA_SERVER_OPERATION_DROP_DB:
+		return FALSE;
+
+        case GDA_SERVER_OPERATION_CREATE_TABLE:
+		return TRUE;
+        case GDA_SERVER_OPERATION_DROP_TABLE:
+        case GDA_SERVER_OPERATION_RENAME_TABLE:
+
+        case GDA_SERVER_OPERATION_ADD_COLUMN:
+
+        case GDA_SERVER_OPERATION_CREATE_INDEX:
+        case GDA_SERVER_OPERATION_DROP_INDEX:
+
+        case GDA_SERVER_OPERATION_CREATE_VIEW:
+        case GDA_SERVER_OPERATION_DROP_VIEW:
+        default:
+                return FALSE;
+        }
+}
+
+/*
+ * Create operation request
+ *
+ * Creates a #GdaServerOperation. The following code is generic and should only be changed
+ * if some further initialization is required, or if operation's contents is dependent on @cnc
+ */
+static GdaServerOperation *
+gda_web_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
+				   GdaServerOperationType type, GdaSet *options, GError **error)
+{
+        gchar *file;
+        GdaServerOperation *op;
+        gchar *str;
+	gchar *dir;
+
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+
+        file = g_utf8_strdown (gda_server_operation_op_type_to_string (type), -1);
+        str = g_strdup_printf ("web_specs_%s.xml", file);
+        g_free (file);
+
+	dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+        file = gda_server_provider_find_file (provider, dir, str);
+	g_free (dir);
+
+        if (! file) {
+                g_set_error (error, 0, 0, _("Missing spec. file '%s'"), str);
+		g_free (str);
+                return NULL;
+        }
+        g_free (str);
+
+        op = gda_server_operation_new (type, file);
+        g_free (file);
+
+        return op;
+}
+
+/*
+ * Render operation request
+ */
+static gchar *
+gda_web_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
+				   GdaServerOperation *op, GError **error)
+{
+        gchar *sql = NULL;
+        gchar *file;
+        gchar *str;
+	gchar *dir;
+
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+
+	/* test @op's validity */
+        file = g_utf8_strdown (gda_server_operation_op_type_to_string (gda_server_operation_get_op_type (op)), -1);
+        str = g_strdup_printf ("web_specs_%s.xml", file);
+        g_free (file);
+
+	dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+        file = gda_server_provider_find_file (provider, dir, str);
+	g_free (dir);
+
+        if (! file) {
+                g_set_error (error, 0, 0, _("Missing spec. file '%s'"), str);
+		g_free (str);
+                return NULL;
+        }
+        g_free (str);
+        if (!gda_server_operation_is_valid (op, file, error)) {
+                g_free (file);
+                return NULL;
+        }
+        g_free (file);
+
+	/* actual rendering */
+        switch (gda_server_operation_get_op_type (op)) {
+        case GDA_SERVER_OPERATION_CREATE_DB:
+        case GDA_SERVER_OPERATION_DROP_DB:
+		sql = NULL;
+                break;
+        case GDA_SERVER_OPERATION_CREATE_TABLE:
+                sql = gda_web_render_CREATE_TABLE (provider, cnc, op, error);
+                break;
+        case GDA_SERVER_OPERATION_DROP_TABLE:
+        case GDA_SERVER_OPERATION_RENAME_TABLE:
+        case GDA_SERVER_OPERATION_ADD_COLUMN:
+        case GDA_SERVER_OPERATION_CREATE_INDEX:
+        case GDA_SERVER_OPERATION_DROP_INDEX:
+        case GDA_SERVER_OPERATION_CREATE_VIEW:
+        case GDA_SERVER_OPERATION_DROP_VIEW:
+                sql = NULL;
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+        return sql;
+}
+
+/*
+ * Perform operation request
+ */
+static gboolean
+gda_web_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
+				    GdaServerOperation *op, guint *task_id, 
+				    GdaServerProviderAsyncCallback async_cb, gpointer cb_data, GError **error)
+{
+        GdaServerOperationType optype;
+
+	/* If asynchronous connection opening is not supported, then exit now */
+	if (async_cb) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
+			     "%s", _("Provider does not support asynchronous server operation"));
+                return FALSE;
+	}
+
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+        optype = gda_server_operation_get_op_type (op);
+	switch (optype) {
+	case GDA_SERVER_OPERATION_CREATE_DB: 
+	case GDA_SERVER_OPERATION_DROP_DB: 
+        default: 
+		/* use the SQL from the provider to perform the action */
+		return gda_server_provider_perform_operation_default (provider, cnc, op, error);
+	}
+}
+
+/*
+ * Begin transaction request
+ */
+static gboolean
+gda_web_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+				    const gchar *name, GdaTransactionIsolation level,
+				    GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+/*
+ * Commit transaction request
+ */
+static gboolean
+gda_web_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+				     const gchar *name, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+/*
+ * Rollback transaction request
+ */
+static gboolean
+gda_web_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection *cnc,
+				       const gchar *name, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+/*
+ * Add savepoint request
+ */
+static gboolean
+gda_web_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+				const gchar *name, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+/*
+ * Rollback savepoint request
+ */
+static gboolean
+gda_web_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+				     const gchar *name, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+/*
+ * Delete savepoint request
+ */
+static gboolean
+gda_web_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
+				   const gchar *name, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+/*
+ * Feature support request
+ */
+static gboolean
+gda_web_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc, GdaConnectionFeature feature)
+{
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+
+	switch (feature) {
+	case GDA_CONNECTION_FEATURE_SQL :
+		return TRUE;
+	default: 
+		return FALSE;
+	}
+}
+
+/*
+ * Get data handler request
+ *
+ * This method allows one to obtain a pointer to a #GdaDataHandler object specific to @type or @dbms_type (@dbms_type
+ * must be considered only if @type is not a valid GType).
+ *
+ * A data handler allows one to convert a value between its different representations which are a human readable string,
+ * an SQL representation and a GValue.
+ *
+ * The recommended method is to create GdaDataHandler objects only when they are needed and to keep a reference to them
+ * for further usage, using the gda_server_provider_handler_declare() method.
+ *
+ * The implementation shown here does not define any specific data handler, but there should be some for at least 
+ * binary and time related types.
+ */
+static GdaDataHandler *
+gda_web_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
+				   GType type, const gchar *dbms_type)
+{
+	GdaDataHandler *dh;
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+
+	if (type == G_TYPE_INVALID) {
+		TO_IMPLEMENT; /* use @dbms_type */
+		dh = NULL;
+	}
+	else if ((type == GDA_TYPE_BINARY) ||
+		 (type == GDA_TYPE_BLOB)) {
+		TO_IMPLEMENT; /* define data handlers for these types */
+		dh = NULL;
+	}
+	else if ((type == GDA_TYPE_TIME) ||
+		 (type == GDA_TYPE_TIMESTAMP) ||
+		 (type == G_TYPE_DATE)) {
+		TO_IMPLEMENT; /* define data handlers for these types */
+		dh = NULL;
+	}
+	else
+		dh = gda_server_provider_get_data_handler_default (provider, cnc, type, dbms_type);
+
+	return dh;
+}
+
+/*
+ * Get default DBMS type request
+ *
+ * This method returns the "preferred" DBMS type for GType
+ */
+static const gchar*
+gda_web_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc, GType type)
+{
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	}
+
+	TO_IMPLEMENT;
+
+	if ((type == G_TYPE_INT64) ||
+	    (type == G_TYPE_INT) ||
+	    (type == GDA_TYPE_SHORT) ||
+	    (type == GDA_TYPE_USHORT) ||
+	    (type == G_TYPE_CHAR) ||
+	    (type == G_TYPE_UCHAR) ||
+	    (type == G_TYPE_ULONG) ||
+	    (type == G_TYPE_UINT) ||
+	    (type == G_TYPE_UINT64))
+		return "integer";
+
+	if ((type == GDA_TYPE_BINARY) ||
+	    (type == GDA_TYPE_BLOB))
+		return "blob";
+
+	if (type == G_TYPE_BOOLEAN)
+		return "boolean";
+	
+	if ((type == G_TYPE_DATE) || 
+	    (type == GDA_TYPE_GEOMETRIC_POINT) ||
+	    (type == G_TYPE_OBJECT) ||
+	    (type == GDA_TYPE_LIST) ||
+	    (type == G_TYPE_STRING) ||
+	    (type == GDA_TYPE_TIME) ||
+	    (type == GDA_TYPE_TIMESTAMP) ||
+	    (type == G_TYPE_INVALID) ||
+	    (type == G_TYPE_GTYPE))
+		return "string";
+
+	if ((type == G_TYPE_DOUBLE) ||
+	    (type == GDA_TYPE_NUMERIC) ||
+	    (type == G_TYPE_FLOAT))
+		return "real";
+	
+	if (type == GDA_TYPE_TIME)
+		return "time";
+	if (type == GDA_TYPE_TIMESTAMP)
+		return "timestamp";
+	if (type == G_TYPE_DATE)
+		return "date";
+
+	return "text";
+}
+
+/*
+ * Create parser request
+ *
+ * This method is responsible for creating a #GdaSqlParser object specific to the SQL dialect used
+ * by the database. See the PostgreSQL provider implementation for an example.
+ */
+static GdaSqlParser *
+gda_web_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc)
+{
+	WebConnectionData *cdata = NULL;
+
+	if (cnc)
+		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return NULL;
+	if (cdata->reuseable && cdata->reuseable->operations->re_create_parser)
+		return cdata->reuseable->operations->re_create_parser (cdata->reuseable);
+	else
+		return NULL;
+}
+
+/*
+ * GdaStatement to SQL request
+ * 
+ * This method renders a #GdaStatement into its SQL representation.
+ *
+ * The implementation show here simply calls gda_statement_to_sql_extended() but the rendering
+ * can be specialized to the database's SQL dialect, see the implementation of gda_statement_to_sql_extended()
+ * and SQLite's specialized rendering for more details
+ */
+static gchar *
+gda_web_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
+				   GdaStatement *stmt, GdaSet *params, GdaStatementSqlFlag flags,
+				   GSList **params_used, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
+	if (cnc) {
+		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+	}
+
+	return gda_statement_to_sql_extended (stmt, cnc, params, flags, params_used, error);
+}
+
+static const gchar*
+gtype_to_webtype (GType type)
+{
+	if (type == G_TYPE_INT64)
+                return "integer";
+        if (type == G_TYPE_UINT64)
+                return "integer";
+        if (type == GDA_TYPE_BINARY)
+                return "text";
+        if (type == GDA_TYPE_BLOB)
+                return "blob";
+        if (type == G_TYPE_BOOLEAN)
+                return "boolean";
+        if (type == G_TYPE_DATE)
+                return "date";
+        if (type == G_TYPE_DOUBLE)
+                return "float";
+        if (type == GDA_TYPE_GEOMETRIC_POINT)
+                return "text";
+        if (type == G_TYPE_OBJECT)
+                return "text";
+        if (type == G_TYPE_INT)
+                return "integer";
+        if (type == GDA_TYPE_LIST)
+                return "text";
+        if (type == GDA_TYPE_NUMERIC)
+                return "decimal";
+        if (type == G_TYPE_FLOAT)
+                return "float";
+        if (type == GDA_TYPE_SHORT)
+		return "integer";
+        if (type == GDA_TYPE_USHORT)
+                return "integer";
+        if (type == G_TYPE_STRING)
+                return "text";
+        if (type == GDA_TYPE_TIME)
+                return "time";
+        if (type == GDA_TYPE_TIMESTAMP)
+                return "timestamp";
+        if (type == G_TYPE_CHAR)
+                return "integer";
+        if (type == G_TYPE_UCHAR)
+                return "integer";
+        if (type == G_TYPE_ULONG)
+                return "integer";
+        if (type == G_TYPE_GTYPE)
+                return "text";
+        if (type == G_TYPE_UINT)
+                return "integer";
+        if (type == G_TYPE_INVALID)
+                return "text";
+
+        return "text";
+}
+
+/*
+ * Statement prepare request
+ *
+ * This methods "converts" @stmt into a prepared statement. A prepared statement is a notion
+ * specific in its implementation details to the C API used here. If successfull, it must create
+ * a new #GdaWebPStmt object and declare it to @cnc.
+ */
+static gboolean
+gda_web_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
+				    GdaStatement *stmt, GError **error)
+{
+	GdaWebPStmt *ps;
+	gboolean retval = FALSE;
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
+
+
+	/* fetch prepares stmt if already done */
+	ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+	if (ps)
+		return TRUE;
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	/* render as SQL understood by the provider */
+	GdaSet *params = NULL;
+	gchar *sql;
+	GSList *used_params = NULL;
+	if (! gda_statement_get_parameters (stmt, &params, error))
+                return FALSE;
+        sql = gda_web_provider_statement_to_sql (provider, cnc, stmt, params, GDA_STATEMENT_SQL_PARAMS_AS_UQMARK,
+						 &used_params, error);
+        if (!sql) 
+		goto out;
+
+	/* make a list of the parameter names used in the statement */
+	GSList *param_ids = NULL;
+        if (used_params) {
+                GSList *list;
+                for (list = used_params; list; list = list->next) {
+                        const gchar *cid;
+                        cid = gda_holder_get_id (GDA_HOLDER (list->data));
+                        if (cid) {
+                                param_ids = g_slist_append (param_ids, g_strdup (cid));
+                                /*g_print ("PREPARATION: param ID: %s\n", cid);*/
+                        }
+                        else {
+                                g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+                                             "%s", _("Unnamed parameter is not allowed in prepared statements"));
+                                g_slist_foreach (param_ids, (GFunc) g_free, NULL);
+                                g_slist_free (param_ids);
+                                goto out;
+                        }
+                }
+        }
+
+	/* prepare XML command */
+	xmlDocPtr doc;
+	xmlNodePtr root, cmdnode, node;
+	gchar *token;
+	doc = xmlNewDoc (BAD_CAST "1.0");
+	root = xmlNewNode (NULL, BAD_CAST "request");
+	xmlDocSetRootElement (doc, root);
+	token = _gda_web_compute_token (cdata);
+	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
+	g_free (token);
+	cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "PREPARE");
+	node = xmlNewTextChild (cmdnode, NULL, BAD_CAST "sql", BAD_CAST sql);
+	if ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) ||
+	    (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))
+		xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+	else if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_UNKNOWN) {
+		if (! g_ascii_strncasecmp (sql, "select", 6) ||
+		    ! g_ascii_strncasecmp (sql, "pragma", 6) ||
+		    ! g_ascii_strncasecmp (sql, "describe", 8))
+			xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+	}
+	if (param_ids) {
+		GSList *list;
+		xmlNodePtr argsnode;
+		argsnode = xmlNewChild (cmdnode, NULL, BAD_CAST "arguments", NULL);
+		for (list = used_params; list; list = list->next) {
+			node = xmlNewChild (argsnode, NULL, BAD_CAST "arg", NULL);
+			xmlSetProp (node, BAD_CAST "type",
+				    BAD_CAST gtype_to_webtype (gda_holder_get_g_type (GDA_HOLDER (list->data))));
+		}
+	}
+
+	/* send command */
+	xmlChar *cmde;
+	xmlDocPtr replydoc;
+	int size;
+	gchar status;
+	
+	xmlDocDumpMemory (doc, &cmde, &size);
+	xmlFreeDoc (doc);
+	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
+	xmlFree (cmde);
+
+	if (!replydoc) {
+		_gda_web_change_connection_to_closed (cnc, cdata);
+		goto out;
+	}
+	if (status != 'O') {
+		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
+		xmlFreeDoc (replydoc);
+
+		if (status == 'C')
+			_gda_web_change_connection_to_closed (cnc, cdata);
+		goto out;
+	}
+	
+	/* create a prepared statement object */
+	ps = NULL;
+	root = xmlDocGetRootElement (replydoc);
+	for (node = root->children; node; node = node->next) {
+		if (!strcmp ((gchar*) node->name, "preparehash")) {
+			xmlChar *contents;
+			contents = xmlNodeGetContent (node);
+			ps = gda_web_pstmt_new (cnc, (gchar*) contents);
+			xmlFree (contents);
+			break;
+		}
+	}
+	xmlFreeDoc (replydoc);
+	
+	if (!ps) 
+		goto out;
+
+	gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
+	_GDA_PSTMT (ps)->param_ids = param_ids;
+	_GDA_PSTMT (ps)->sql = sql;
+
+	gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
+	g_object_unref (ps);
+
+	retval = TRUE;
+
+ out:
+	if (used_params)
+                g_slist_free (used_params);
+        if (params)
+                g_object_unref (params);
+	return retval;
+}
+
+/*
+ * Execute statement request
+ *
+ * Executes a statement. This method should do the following:
+ *    - try to prepare the statement if not yet done
+ *    - optionnally reset the prepared statement
+ *    - bind the variables's values (which are in @params)
+ *    - add a connection event to log the execution
+ *    - execute the prepared statement
+ *
+ * If @stmt is an INSERT statement and @last_inserted_row is not NULL then additional actions must be taken to return the
+ * actual inserted row
+ */
+static GObject *
+gda_web_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+				    GdaStatement *stmt, GdaSet *params,
+				    GdaStatementModelUsage model_usage, 
+				    GType *col_types, GdaSet **last_inserted_row, 
+				    guint *task_id, 
+				    GdaServerProviderExecCallback async_cb, gpointer cb_data, GError **error)
+{
+	GdaWebPStmt *ps;
+	WebConnectionData *cdata;
+	gboolean allow_noparam;
+        gboolean empty_rs = FALSE; /* TRUE when @allow_noparam is TRUE and there is a problem with @params
+                                      => resulting data model will be empty (0 row) */
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
+
+	/* If asynchronous connection opening is not supported, then exit now */
+	if (async_cb) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
+			     "%s", _("Provider does not support asynchronous statement execution"));
+                return NULL;
+	}
+
+        allow_noparam = (model_usage & GDA_STATEMENT_MODEL_ALLOW_NOPARAM) &&
+                (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT);
+	
+        if (last_inserted_row)
+                *last_inserted_row = NULL;
+
+	/* Get private data */
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return NULL;
+
+
+	/* get/create new prepared statement */
+	ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+	if (!ps) {
+		if (!gda_web_provider_statement_prepare (provider, cnc, stmt, error)) {
+			/* this case can appear for example if some variables are used in places
+			 * where the C API cannot allow them (for example if the variable is the table name
+			 * in a SELECT statement). The action here is to get the actual SQL code for @stmt,
+			 * and use that SQL instead of @stmt to create another GdaWebPStmt object.
+			 *
+			 * Don't call gda_connection_add_prepared_statement() with this new prepared statement
+			 * as it will be destroyed once used.
+			 */
+			return NULL;
+		}
+		else {
+			ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+			g_object_ref (ps);
+		}
+	}
+	else
+		g_object_ref (ps);
+	g_assert (ps);
+
+	/* prepare XML command */
+	xmlDocPtr doc;
+	xmlNodePtr root, cmdnode, node;
+	gchar *token;
+	doc = xmlNewDoc (BAD_CAST "1.0");
+	root = xmlNewNode (NULL, BAD_CAST "request");
+	xmlDocSetRootElement (doc, root);
+	token = _gda_web_compute_token (cdata);
+	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
+	g_free (token);
+	cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "EXEC");
+	node = xmlNewTextChild (cmdnode, NULL, BAD_CAST "sql", BAD_CAST _GDA_PSTMT (ps)->sql);
+	if ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) ||
+	    (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))
+		xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+	else if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_UNKNOWN) {
+		if (! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "select", 6) ||
+		    ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "pragma", 6) ||
+		    ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "describe", 8))
+			xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
+	}
+	xmlNewChild (cmdnode, NULL, BAD_CAST "preparehash", BAD_CAST (ps->pstmt_hash));
+
+	/* bind statement's parameters */
+	GSList *list;
+	GdaConnectionEvent *event = NULL;
+	int i;
+	xmlNodePtr argsnode;
+	if (_GDA_PSTMT (ps)->param_ids)
+		argsnode = xmlNewChild (cmdnode, NULL, BAD_CAST "arguments", NULL);
+
+	for (i = 1, list = _GDA_PSTMT (ps)->param_ids; list; list = list->next, i++) {
+		const gchar *pname = (gchar *) list->data;
+		GdaHolder *h;
+		
+		/* find requested parameter */
+		if (!params) {
+			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+			gda_connection_event_set_description (event, _("Missing parameter(s) to execute query"));
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR,
+				     "%s", _("Missing parameter(s) to execute query"));
+			break;
+		}
+
+		h = gda_set_get_holder (params, pname);
+		if (!h) {
+			gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
+			if (tmp) {
+				h = gda_set_get_holder (params, tmp);
+				g_free (tmp);
+			}
+		}
+		if (!h) {
+			if (! allow_noparam) {
+				gchar *str;
+				str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
+				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+				gda_connection_event_set_description (event, str);
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
+				g_free (str);
+				break;
+			}
+			else {
+                                /* bind param to NULL */
+				node = xmlNewChild (argsnode, NULL, BAD_CAST "arg", NULL);
+				xmlSetProp (node, BAD_CAST "type", BAD_CAST "NULL");
+                                empty_rs = TRUE;
+                                continue;
+                        }
+
+		}
+		if (!gda_holder_is_valid (h)) {
+			if (! allow_noparam) {
+				gchar *str;
+				str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+				gda_connection_event_set_description (event, str);
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
+				g_free (str);
+				break;
+			}
+			else {
+                                /* bind param to NULL */
+				xmlSetProp (node, BAD_CAST "type", BAD_CAST "NULL");
+                                empty_rs = TRUE;
+                                continue;
+                        }
+		}
+
+		/* actual binding using the C API, for parameter at position @i */
+		const GValue *value = gda_holder_get_value (h);
+		gchar *tmp;
+		tmp = gda_value_stringify (value);
+		node = xmlNewTextChild (argsnode, NULL, BAD_CAST "arg", BAD_CAST tmp);
+		g_free (tmp);
+		xmlSetProp (node, BAD_CAST "type",
+			    BAD_CAST gtype_to_webtype (gda_holder_get_g_type (h)));
+	}
+		
+	if (event) {
+		gda_connection_add_event (cnc, event);
+		g_object_unref (ps);
+		xmlFreeDoc (doc);
+		return NULL;
+	}
+	
+	/* add a connection event for the execution */
+	event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
+        gda_connection_event_set_description (event, _GDA_PSTMT (ps)->sql);
+        gda_connection_add_event (cnc, event);
+
+	if (empty_rs) {
+		/* There are some missing parameters, so the SQL can't be executed but we still want
+		 * to execute something to get the columns correctly. A possibility is to actually
+		 * execute another SQL which is the code shown here.
+		 *
+		 * To adapt depending on the C API and its features */
+		GdaStatement *estmt;
+                gchar *esql;
+                estmt = gda_select_alter_select_for_empty (stmt, error);
+                if (!estmt) {
+			g_object_unref (ps);
+                        return NULL;
+		}
+                esql = gda_statement_to_sql (estmt, NULL, error);
+                g_object_unref (estmt);
+                if (!esql) {
+			g_object_unref (ps);
+                        return NULL;
+		}
+
+		/* Execute the 'esql' SQL code */
+                g_free (esql);
+
+		/* modify @doc */
+		TO_IMPLEMENT;
+	}
+
+	/* send command */
+	xmlChar *cmde;
+	xmlDocPtr replydoc;
+	int size;
+	gchar status;
+	
+	xmlDocDumpMemory (doc, &cmde, &size);
+	xmlFreeDoc (doc);
+	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_EXEC, (gchar*) cmde, cdata->key, &status);
+	xmlFree (cmde);
+	
+	if (!replydoc)
+		status = 'E';
+	if (status != 'O') {
+		if (replydoc) {
+			_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
+			xmlFreeDoc (replydoc);
+			if (status == 'C')
+				_gda_web_change_connection_to_closed (cnc, cdata);
+		}
+		else
+			_gda_web_change_connection_to_closed (cnc, cdata);
+		return NULL;
+	}
+
+	/* required: help @cnc keep some stats */
+	event = gda_connection_event_new (GDA_CONNECTION_EVENT_NOTICE);
+	gda_connection_event_set_description (event, "Command OK");
+	gda_connection_add_event (cnc, event);
+	gda_connection_internal_statement_executed (cnc, stmt, params, event);
+
+	root = xmlDocGetRootElement (replydoc);
+	GObject *retval = NULL;
+	for (node = root->children; node; node = node->next) {
+		if (!strcmp ((gchar*) node->name, "impacted_rows")) {
+			xmlChar *contents;
+			GdaSet *set = NULL;
+
+			contents = xmlNodeGetContent (node);
+			set = gda_set_new_inline (1, "IMPACTED_ROWS", G_TYPE_INT, atoi ((gchar*) contents));
+			xmlFree (contents);
+			retval = (GObject*) set;
+		}
+		else if (!strcmp ((gchar*) node->name, "gda_array")) {
+			GdaDataModel *data_model;
+			gda_mutex_lock (cdata->mutex);
+			data_model = gda_web_recordset_new (cnc, ps, params, model_usage,
+							    col_types, cdata->session_id, node, error);
+			gda_mutex_unlock (cdata->mutex);
+			retval = (GObject*) data_model;
+
+			if (! gda_web_recordset_store (GDA_WEB_RECORDSET (data_model), node, error)) {
+				g_object_unref (G_OBJECT (data_model));
+				retval = NULL;
+			}
+		}
+		else if (!strcmp ((gchar*) node->name, "preparehash")) {
+			xmlChar *contents;
+			contents = xmlNodeGetContent (node);
+			g_free (ps->pstmt_hash);
+			ps->pstmt_hash = g_strdup ((gchar*) contents);
+			xmlFree (contents);
+		}
+	}
+
+	xmlFreeDoc (replydoc);
+	g_object_unref (ps);
+	return retval;
+}
+
+/*
+ * starts a distributed transaction: put the XA transaction in the ACTIVE state
+ */
+static gboolean
+gda_web_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc, 
+			   const GdaXaTransactionId *xid, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+	return FALSE;
+}
+
+/*
+ * put the XA transaction in the IDLE state: the connection won't accept any more modifications.
+ * This state is required by some database providers before actually going to the PREPARED state
+ */
+static gboolean
+gda_web_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc, 
+			 const GdaXaTransactionId *xid, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+	return FALSE;
+}
+
+/*
+ * prepares the distributed transaction: put the XA transaction in the PREPARED state
+ */
+static gboolean
+gda_web_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc, 
+			     const GdaXaTransactionId *xid, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+	return FALSE;
+}
+
+/*
+ * commits the distributed transaction: actually write the prepared data to the database and
+ * terminates the XA transaction
+ */
+static gboolean
+gda_web_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc, 
+			    const GdaXaTransactionId *xid, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+	return FALSE;
+}
+
+/*
+ * Rolls back an XA transaction, possible only if in the ACTIVE, IDLE or PREPARED state
+ */
+static gboolean
+gda_web_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+			      const GdaXaTransactionId *xid, GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return FALSE;
+
+	TO_IMPLEMENT;
+	return FALSE;
+}
+
+/*
+ * Lists all XA transactions that are in the PREPARED state
+ *
+ * Returns: a list of GdaXaTransactionId structures, which will be freed by the caller
+ */
+static GList *
+gda_web_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+			     GError **error)
+{
+	WebConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return NULL;
+
+	TO_IMPLEMENT;
+	return NULL;
+}
diff --git a/providers/web/gda-web-provider.h b/providers/web/gda-web-provider.h
new file mode 100644
index 0000000..63953a1
--- /dev/null
+++ b/providers/web/gda-web-provider.h
@@ -0,0 +1,51 @@
+/* GDA 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.
+ */
+
+#ifndef __GDA_WEB_PROVIDER_H__
+#define __GDA_WEB_PROVIDER_H__
+
+#include <libgda/gda-server-provider.h>
+
+#define GDA_TYPE_WEB_PROVIDER            (gda_web_provider_get_type())
+#define GDA_WEB_PROVIDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_PROVIDER, GdaWebProvider))
+#define GDA_WEB_PROVIDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_PROVIDER, GdaWebProviderClass))
+#define GDA_IS_WEB_PROVIDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_WEB_PROVIDER))
+#define GDA_IS_WEB_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_WEB_PROVIDER))
+
+typedef struct _GdaWebProvider      GdaWebProvider;
+typedef struct _GdaWebProviderClass GdaWebProviderClass;
+
+struct _GdaWebProvider {
+	GdaServerProvider      provider;
+};
+
+struct _GdaWebProviderClass {
+	GdaServerProviderClass parent_class;
+};
+
+G_BEGIN_DECLS
+
+GType gda_web_provider_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/providers/web/gda-web-pstmt.c b/providers/web/gda-web-pstmt.c
new file mode 100644
index 0000000..dfe412c
--- /dev/null
+++ b/providers/web/gda-web-pstmt.c
@@ -0,0 +1,143 @@
+/* GDA Web 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 <glib/gi18n-lib.h>
+#include "gda-web-pstmt.h"
+#include "gda-web-util.h"
+
+static void gda_web_pstmt_class_init (GdaWebPStmtClass *klass);
+static void gda_web_pstmt_init       (GdaWebPStmt *pstmt, GdaWebPStmtClass *klass);
+static void gda_web_pstmt_finalize    (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/**
+ * gda_web_pstmt_get_type
+ *
+ * Returns: the #GType of GdaWebPStmt.
+ */
+GType
+gda_web_pstmt_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (GdaWebPStmtClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gda_web_pstmt_class_init,
+			NULL,
+			NULL,
+			sizeof (GdaWebPStmt),
+			0,
+			(GInstanceInitFunc) gda_web_pstmt_init
+		};
+
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GDA_TYPE_PSTMT, "GdaWebPStmt", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+static void 
+gda_web_pstmt_class_init (GdaWebPStmtClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* virtual functions */
+	object_class->finalize = gda_web_pstmt_finalize;
+}
+
+static void
+gda_web_pstmt_init (GdaWebPStmt *pstmt, GdaWebPStmtClass *klass)
+{
+	g_return_if_fail (GDA_IS_PSTMT (pstmt));
+	
+	pstmt->pstmt_hash = NULL;
+}
+
+static void
+gda_web_pstmt_finalize (GObject *object)
+{
+	GdaWebPStmt *pstmt = (GdaWebPStmt *) object;
+
+	g_return_if_fail (GDA_IS_PSTMT (pstmt));
+
+	if (pstmt->pstmt_hash) {
+		WebConnectionData *cdata;
+		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (pstmt->cnc);
+		if (!cdata) 
+			goto next;
+
+		/* send command to deallocate prepared statement */
+		xmlDocPtr doc;
+		xmlNodePtr root, cmdnode, node;
+		gchar *token;
+		doc = xmlNewDoc (BAD_CAST "1.0");
+		root = xmlNewNode (NULL, BAD_CAST "request");
+		xmlDocSetRootElement (doc, root);
+		token = _gda_web_compute_token (cdata);
+		xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
+		g_free (token);
+		cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "UNPREPARE");
+		node = xmlNewChild (cmdnode, NULL, BAD_CAST "preparehash", BAD_CAST pstmt->pstmt_hash);
+
+		xmlChar *cmde;
+		xmlDocPtr replydoc;
+		int size;
+		gchar status;
+		
+		xmlDocDumpMemory (doc, &cmde, &size);
+		xmlFreeDoc (doc);
+		replydoc = _gda_web_send_message_to_frontend (pstmt->cnc, cdata, MESSAGE_UNPREPARE, (gchar*) cmde,
+							      cdata->key, &status);
+		xmlFree (cmde);		
+		if (replydoc)
+			xmlFreeDoc (replydoc);
+		
+	next:
+		/* free memory */
+		g_free (pstmt->pstmt_hash);
+	}
+
+	/* chain to parent class */
+	parent_class->finalize (object);
+}
+
+GdaWebPStmt *
+gda_web_pstmt_new (GdaConnection *cnc, const gchar *pstmt_hash)
+{
+	GdaWebPStmt *pstmt;
+	g_return_val_if_fail (pstmt_hash && *pstmt_hash, NULL);
+
+        pstmt = (GdaWebPStmt *) g_object_new (GDA_TYPE_WEB_PSTMT, NULL);
+	pstmt->cnc = cnc;
+        pstmt->pstmt_hash = g_strdup (pstmt_hash);
+
+        return pstmt;
+}
diff --git a/providers/web/gda-web-pstmt.h b/providers/web/gda-web-pstmt.h
new file mode 100644
index 0000000..9844ea0
--- /dev/null
+++ b/providers/web/gda-web-pstmt.h
@@ -0,0 +1,57 @@
+/* GDA library
+ * Copyright (C) 2009 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_WEB_PSTMT_H__
+#define __GDA_WEB_PSTMT_H__
+
+#include <providers-support/gda-pstmt.h>
+#include "gda-web.h"
+#include <libgda/libgda.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_WEB_PSTMT            (gda_web_pstmt_get_type())
+#define GDA_WEB_PSTMT(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_PSTMT, GdaWebPStmt))
+#define GDA_WEB_PSTMT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_PSTMT, GdaWebPStmtClass))
+#define GDA_IS_WEB_PSTMT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_WEB_PSTMT))
+#define GDA_IS_WEB_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_WEB_PSTMT))
+
+typedef struct _GdaWebPStmt        GdaWebPStmt;
+typedef struct _GdaWebPStmtClass   GdaWebPStmtClass;
+
+struct _GdaWebPStmt {
+	GdaPStmt        object;
+
+	GdaConnection  *cnc;
+	gchar          *pstmt_hash;
+};
+
+struct _GdaWebPStmtClass {
+	GdaPStmtClass  parent_class;
+};
+
+GType         gda_web_pstmt_get_type  (void) G_GNUC_CONST;
+GdaWebPStmt  *gda_web_pstmt_new (GdaConnection *cnc, const gchar *pstmt_hash);
+
+G_END_DECLS
+
+#endif
diff --git a/providers/web/gda-web-recordset.c b/providers/web/gda-web-recordset.c
new file mode 100644
index 0000000..910c119
--- /dev/null
+++ b/providers/web/gda-web-recordset.c
@@ -0,0 +1,631 @@
+/* GDA 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 <stdarg.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-util.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <libgda/gda-connection-private.h>
+#include "gda-web.h"
+#include "gda-web-recordset.h"
+#include "gda-web-provider.h"
+
+#define _GDA_PSTMT(x) ((GdaPStmt*)(x))
+
+static void gda_web_recordset_class_init (GdaWebRecordsetClass *klass);
+static void gda_web_recordset_init       (GdaWebRecordset *recset,
+					     GdaWebRecordsetClass *klass);
+static void gda_web_recordset_dispose   (GObject *object);
+
+/* virtual methods */
+static gint     gda_web_recordset_fetch_nb_rows (GdaDataSelect *model);
+static gboolean gda_web_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_web_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_web_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+static gboolean gda_web_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
+
+
+struct _GdaWebRecordsetPrivate {
+	GdaConnection *cnc;
+	
+	GdaConnection *rs_cnc; /* connection to store resultsets */
+	gboolean table_created;
+	
+	GdaStatement *insert; /* adding new data to @rs_cnc */
+	GdaStatement *select; /* final SELECT */
+	GdaDataModel *real_model;
+};
+static GObjectClass *parent_class = NULL;
+
+/*
+ * Object init and finalize
+ */
+static void
+gda_web_recordset_init (GdaWebRecordset *recset,
+			   GdaWebRecordsetClass *klass)
+{
+	g_return_if_fail (GDA_IS_WEB_RECORDSET (recset));
+	recset->priv = g_new0 (GdaWebRecordsetPrivate, 1);
+	recset->priv->cnc = NULL;
+	recset->priv->rs_cnc = NULL;
+	recset->priv->table_created = FALSE;
+	recset->priv->insert = NULL;
+	recset->priv->select = NULL;
+}
+
+static void
+gda_web_recordset_class_init (GdaWebRecordsetClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GdaDataSelectClass *pmodel_class = GDA_DATA_SELECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = gda_web_recordset_dispose;
+	pmodel_class->fetch_nb_rows = gda_web_recordset_fetch_nb_rows;
+	pmodel_class->fetch_random = gda_web_recordset_fetch_random;
+
+	pmodel_class->fetch_next = gda_web_recordset_fetch_next;
+	pmodel_class->fetch_prev = gda_web_recordset_fetch_prev;
+	pmodel_class->fetch_at = gda_web_recordset_fetch_at;
+}
+
+static void
+gda_web_recordset_dispose (GObject *object)
+{
+	GdaWebRecordset *recset = (GdaWebRecordset *) object;
+
+	g_return_if_fail (GDA_IS_WEB_RECORDSET (recset));
+
+	if (recset->priv) {
+		if (recset->priv->cnc) 
+			g_object_unref (recset->priv->cnc);
+		if (recset->priv->real_model)
+			g_object_unref (recset->priv->real_model);
+		if (recset->priv->rs_cnc)
+			g_object_unref (recset->priv->rs_cnc);
+
+		if (recset->priv->insert)
+			g_object_unref (recset->priv->insert);
+		if (recset->priv->select)
+			g_object_unref (recset->priv->select);
+
+		g_free (recset->priv);
+		recset->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+/*
+ * Public functions
+ */
+
+GType
+gda_web_recordset_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (GdaWebRecordsetClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gda_web_recordset_class_init,
+			NULL,
+			NULL,
+			sizeof (GdaWebRecordset),
+			0,
+			(GInstanceInitFunc) gda_web_recordset_init
+		};
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaWebRecordset", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+
+	return type;
+}
+
+/*
+ * the @ps struct is modified and transferred to the new data model created in
+ * this function
+ */
+GdaDataModel *
+gda_web_recordset_new (GdaConnection *cnc, GdaWebPStmt *ps, GdaSet *exec_params,
+		       GdaDataModelAccessFlags flags, GType *col_types,
+		       const gchar *session_id, xmlNodePtr data_node, GError **error)
+{
+	GdaWebRecordset *model;
+        gint i;
+	GdaDataModelAccessFlags rflags;
+	static guint counter = 0;
+
+        g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+        g_return_val_if_fail (ps != NULL, NULL);
+
+	/* prepare internal connection which will be used to store
+	 * the recordset's data
+	 */
+	GdaConnection *rs_cnc;
+	gchar *fname, *tmp;
+	
+	for (fname = (gchar*) session_id; *fname && (*fname != '='); fname++);
+	g_assert (*fname == '=');
+	fname++;
+	tmp = g_strdup_printf ("%s%u.db", fname, counter++);
+	rs_cnc = gda_connection_open_sqlite (NULL, tmp, TRUE);
+	if (!rs_cnc) {
+		fname = g_build_filename (g_get_tmp_dir(), tmp, NULL);
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+			     _("Can't create temporary file '%s'"), fname);
+		g_free (tmp);
+		g_free (fname);
+		return NULL;
+	}
+	g_free (tmp);
+
+	/* make sure @ps reports the correct number of columns using the API*/
+        if (_GDA_PSTMT (ps)->ncols < 0) {
+		xmlNodePtr child;
+		_GDA_PSTMT (ps)->ncols = 0;
+		for (child = data_node->children; child; child = child->next) {
+			if (!strcmp ((gchar*) child->name, "gda_array_field"))
+				_GDA_PSTMT (ps)->ncols ++;
+		}
+	}
+
+        /* completing @ps if not yet done */
+        if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
+		/* create prepared statement's columns */
+		GSList *list;
+		for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
+			_GDA_PSTMT (ps)->tmpl_columns = g_slist_prepend (_GDA_PSTMT (ps)->tmpl_columns, 
+									 gda_column_new ());
+		_GDA_PSTMT (ps)->tmpl_columns = g_slist_reverse (_GDA_PSTMT (ps)->tmpl_columns);
+
+		/* create prepared statement's types */
+		_GDA_PSTMT (ps)->types = g_new0 (GType, _GDA_PSTMT (ps)->ncols); /* all types are initialized to GDA_TYPE_NULL */
+		if (col_types) {
+			for (i = 0; ; i++) {
+				if (col_types [i] > 0) {
+					if (col_types [i] == G_TYPE_NONE)
+						break;
+					if (i >= _GDA_PSTMT (ps)->ncols)
+						g_warning (_("Column %d out of range (0-%d), ignoring its specified type"), i,
+							   _GDA_PSTMT (ps)->ncols - 1);
+					else
+						_GDA_PSTMT (ps)->types [i] = col_types [i];
+				}
+			}
+		}
+		
+		/* fill GdaColumn's data */
+		xmlNodePtr child;
+		WebConnectionData *cdata;
+		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
+		if (!cdata) 
+			return FALSE;
+
+		for (child = data_node->children, i = 0, list = _GDA_PSTMT (ps)->tmpl_columns; 
+		     child && (i < GDA_PSTMT (ps)->ncols);
+		     child = child->next, i++, list = list->next) {
+			GdaColumn *column;
+			xmlChar *prop;
+			gboolean typeset = FALSE;
+
+			while (strcmp ((gchar*) child->name, "gda_array_field"))
+				child = child->next;
+			column = GDA_COLUMN (list->data);
+
+			if (_GDA_PSTMT (ps)->types [i] == 0) {
+				if (cdata && cdata->reuseable && cdata->reuseable->operations->re_get_type) {
+					prop = xmlGetProp (child, BAD_CAST "dbtype");
+					if (prop) {
+						GType type;
+						type = cdata->reuseable->operations->re_get_type (cnc, cdata->reuseable,
+												  (gchar*) prop);
+						if (type != GDA_TYPE_NULL) {
+							_GDA_PSTMT (ps)->types [i] = type;
+							gda_column_set_g_type (column, type);
+							typeset = TRUE;
+						}
+						xmlFree (prop);
+					}
+				}
+				if (!typeset) {
+					prop = xmlGetProp (child, BAD_CAST "gdatype");
+					if (prop) {
+						GType type;
+						
+						type = gda_g_type_from_string ((gchar*) prop);
+						_GDA_PSTMT (ps)->types [i] = type;
+						gda_column_set_g_type (column, type);
+						xmlFree (prop);
+					}
+					else {
+						_GDA_PSTMT (ps)->types [i] = G_TYPE_STRING;
+						gda_column_set_g_type (column, G_TYPE_STRING);
+					}
+				}
+			}
+			else
+				gda_column_set_g_type (column, _GDA_PSTMT (ps)->types [i]);
+			prop = xmlGetProp (child, BAD_CAST "name");
+			if (prop && *prop) {
+				gda_column_set_name (column, (gchar*) prop);
+				gda_column_set_description (column, (gchar*) prop);
+			}
+			else {
+				gchar *tmp;
+				tmp = g_strdup_printf ("col%d", i);
+				gda_column_set_name (column, tmp);
+				gda_column_set_description (column, tmp);
+				g_free (tmp);
+			}
+			if (prop)
+				xmlFree (prop);
+		}
+        }
+
+	/* determine access mode: RANDOM or CURSOR FORWARD are the only supported */
+	if (flags & GDA_DATA_MODEL_ACCESS_RANDOM)
+		rflags = GDA_DATA_MODEL_ACCESS_RANDOM;
+	else
+		rflags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
+
+	/* create data model */
+        model = g_object_new (GDA_TYPE_WEB_RECORDSET, 
+			      "prepared-stmt", ps, 
+			      "model-usage", rflags, 
+			      "exec-params", exec_params, NULL);
+        model->priv->cnc = cnc;
+	model->priv->rs_cnc = rs_cnc;
+	g_object_ref (cnc);
+
+        return GDA_DATA_MODEL (model);
+}
+
+
+/*
+ * create tha table to store the actual data
+ */
+static gboolean
+create_table (GdaWebRecordset *rs, GError **error)
+{
+#define TABLE_NAME "data"
+	GString *string;
+	gint i, ncols;
+	gboolean retval = FALSE;
+
+	GdaSqlBuilder *sb, *ib;
+
+	ib = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
+	sb = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+
+	gda_sql_builder_set_table (ib, TABLE_NAME);
+	gda_sql_builder_select_add_target (sb, 0, gda_sql_builder_add_id (sb, 0, TABLE_NAME), NULL);
+
+	string = g_string_new ("CREATE table " TABLE_NAME " (");
+	ncols = gda_data_model_get_n_columns ((GdaDataModel*) rs);
+	for (i = 0; i < ncols; i++) {
+		GdaColumn *column;
+		gchar *colname;
+
+		column = gda_data_model_describe_column ((GdaDataModel*) rs, i);
+		if (i > 0)
+			g_string_append (string, ", ");
+		colname = g_strdup_printf ("col%d", i);
+		g_string_append_printf (string, "%s %s", colname,
+					gda_g_type_to_string (gda_column_get_g_type (column)));
+
+		gda_sql_builder_add_field (ib, gda_sql_builder_add_id (ib, 0, colname),
+					   gda_sql_builder_add_param (ib, 0, colname,
+								      gda_column_get_g_type (column), TRUE));
+		gda_sql_builder_add_field (sb, gda_sql_builder_add_id (sb, 0, colname), 0);
+
+		g_free (colname);
+	}
+	g_string_append (string, ")");
+	/*g_print ("CREATE SQL: [%s]\n", string->str);*/
+
+	GdaStatement *stmt;
+	GdaSqlParser *parser;
+	const gchar *remain;
+	parser = gda_sql_parser_new ();
+	stmt = gda_sql_parser_parse_string (parser, string->str, &remain, NULL);
+	g_object_unref (parser);
+	if (!stmt || remain) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+			     _("Can't create temporary table to store data from web server"));
+		goto out;
+	}
+	if (gda_connection_statement_execute_non_select (rs->priv->rs_cnc, stmt, NULL, NULL, NULL)) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+			     _("Can't create temporary table to store data from web server"));
+		goto out;
+	}
+
+	rs->priv->insert = gda_sql_builder_get_statement (ib, error);
+	if (rs->priv->insert) 
+		rs->priv->select = gda_sql_builder_get_statement (sb, NULL);
+	if (! rs->priv->insert || ! rs->priv->select) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+			     _("Can't create temporary table to store data from web server"));
+		goto out;
+	}
+	/*g_print ("INSERT: [%s]\n", gda_statement_to_sql (rs->priv->insert, NULL, NULL));
+	  g_print ("SELECT: [%s]\n", gda_statement_to_sql (rs->priv->select, NULL, NULL));*/
+
+	retval = TRUE;
+ out:
+	if (!retval) {
+		if (rs->priv->insert)
+			g_object_unref (rs->priv->insert);
+		rs->priv->insert = NULL;
+		if (rs->priv->select)
+			g_object_unref (rs->priv->select);
+		rs->priv->select = NULL;
+	}
+	g_object_unref (ib);
+	g_object_unref (sb);
+	if (stmt)
+		g_object_unref (stmt);
+	g_string_free (string, TRUE);
+	return retval;
+}
+
+/**
+ * gda_web_recordset_store
+ *
+ * Store some more rows coming from the @data_node array
+ */
+gboolean
+gda_web_recordset_store (GdaWebRecordset *rs, xmlNodePtr data_node, GError **error)
+{
+	GdaSet *params, *iter;
+	GdaDataModel *data;
+	GSList *plist, *ilist;
+	gboolean retval = FALSE;
+	gint i, ncols;
+	xmlNodePtr node;
+
+	g_return_val_if_fail (GDA_IS_WEB_RECORDSET (rs), FALSE);
+	g_return_val_if_fail (data_node, FALSE);
+	g_return_val_if_fail (!strcmp ((gchar*) data_node->name, "gda_array"), FALSE);
+	if (! rs->priv->table_created && ! create_table (rs, error))
+		return FALSE;
+
+	/* modify the @data_node tree to set the correct data types */
+	ncols = gda_data_model_get_n_columns ((GdaDataModel*) rs);
+	for (node = data_node->children, i = 0;
+	     (i < ncols) && node;
+	     node = node->next) {
+		if (strcmp ((gchar*) node->name, "gda_array_field"))
+			continue;
+		GdaColumn *column;
+
+		column = gda_data_model_describe_column ((GdaDataModel*) rs, i);
+		i++;
+		xmlSetProp (node, BAD_CAST "gdatype", gda_g_type_to_string (gda_column_get_g_type (column)));
+	}
+
+	/* for each row in @data_mode, insert the row in @rs->priv->rs_cnc */
+	g_assert (rs->priv->insert);
+
+	data = gda_data_model_import_new_xml_node (data_node);
+	if (!data) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+			     _("Can't import data from web server"));
+		return FALSE;
+	}
+
+	g_assert (gda_statement_get_parameters (rs->priv->insert, &params, NULL));
+	iter = GDA_SET (gda_data_model_create_iter (data));
+	for (plist = params->holders, ilist = iter->holders;
+	     plist && ilist;
+	     plist = plist->next, ilist = ilist->next) {
+		GdaHolder *ph, *ih;
+		ph = GDA_HOLDER (plist->data);
+		ih = GDA_HOLDER (ilist->data);
+		g_assert (gda_holder_set_bind (ph, ih, NULL));
+	}
+	g_assert (!plist && !ilist);
+
+	for (; gda_data_model_iter_move_next ((GdaDataModelIter*) iter); ) {
+		if (gda_connection_statement_execute_non_select (rs->priv->rs_cnc, rs->priv->insert, 
+								 params, NULL, NULL) == -1) {
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s",
+			     _("Can't import data from web server"));
+			goto out;
+		}
+	}
+
+	retval = TRUE;
+
+ out:
+	g_object_unref (data);
+	g_object_unref (iter);
+	g_object_unref (params);
+
+	return retval;
+}
+
+static void
+create_real_model (GdaWebRecordset *rs)
+{
+	if (rs->priv->real_model)
+		return;
+	rs->priv->real_model = gda_connection_statement_execute_select (rs->priv->rs_cnc, rs->priv->select,
+									NULL, NULL);
+}
+
+/*
+ * Get the number of rows in @model, if possible
+ */
+static gint
+gda_web_recordset_fetch_nb_rows (GdaDataSelect *model)
+{
+	GdaWebRecordset *imodel;
+
+	imodel = GDA_WEB_RECORDSET (model);
+	if (model->advertized_nrows >= 0)
+		return model->advertized_nrows;
+
+	create_real_model (imodel);
+	if (imodel->priv->real_model)
+		model->advertized_nrows = gda_data_model_get_n_rows (imodel->priv->real_model);
+
+	return model->advertized_nrows;
+}
+
+/*
+ * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ *  -  If *prow is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ *     (through a call to this very function), and in this case it should not be modified
+ *     but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row(). If new row objects are "given" to the GdaDataSelect implemantation
+ * using that method, then this method should detect when all the data model rows have been analyzed
+ * (when model->nb_stored_rows == model->advertized_nrows) and then possibly discard the API handle
+ * as it won't be used anymore to fetch rows.
+ */
+static gboolean 
+gda_web_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+	GdaWebRecordset *imodel;
+
+	imodel = GDA_WEB_RECORDSET (model);
+
+	if (*prow)
+                return TRUE;
+
+	create_real_model (imodel);
+	if (imodel->priv->real_model)
+		return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_random
+			((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+	return FALSE;
+}
+
+/*
+ * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ *  -  If *prow is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ *     (through a call to this very function), and in this case it should not be modified
+ *     but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row().
+ */
+static gboolean 
+gda_web_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+	GdaWebRecordset *imodel;
+
+	imodel = GDA_WEB_RECORDSET (model);
+
+	if (*prow)
+                return TRUE;
+
+	create_real_model (imodel);
+	if (imodel->priv->real_model)
+		return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_next
+			((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+	return FALSE;
+}
+
+/*
+ * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ *  -  If *prow is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ *     (through a call to this very function), and in this case it should not be modified
+ *     but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row().
+ */
+static gboolean 
+gda_web_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+	GdaWebRecordset *imodel;
+
+	imodel = GDA_WEB_RECORDSET (model);
+
+	if (*prow)
+                return TRUE;
+
+	create_real_model (imodel);
+	if (imodel->priv->real_model)
+		return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_prev
+			((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+	return FALSE;
+}
+
+/*
+ * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
+ *
+ * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
+ *  -  If *prow is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ *     (through a call to this very function), and in this case it should not be modified
+ *     but the function may return FALSE if an error occurred.
+ *
+ * Memory management for that new GdaRow object is left to the implementation, which
+ * can use gda_data_select_take_row().
+ */
+static gboolean 
+gda_web_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
+{
+	GdaWebRecordset *imodel;
+
+	imodel = GDA_WEB_RECORDSET (model);
+
+	if (*prow)
+                return TRUE;
+
+	create_real_model (imodel);
+	if (imodel->priv->real_model)
+		return GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (imodel->priv->real_model))->fetch_at
+			((GdaDataSelect*) imodel->priv->real_model, prow, rownum, error);
+
+	return FALSE;
+}
+
diff --git a/providers/web/gda-web-recordset.h b/providers/web/gda-web-recordset.h
new file mode 100644
index 0000000..84dabd7
--- /dev/null
+++ b/providers/web/gda-web-recordset.h
@@ -0,0 +1,60 @@
+/* GDA 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.
+ */
+
+#ifndef __GDA_WEB_RECORDSET_H__
+#define __GDA_WEB_RECORDSET_H__
+
+#include <libgda/libgda.h>
+#include <providers-support/gda-data-select-priv.h>
+#include "gda-web-pstmt.h"
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_WEB_RECORDSET            (gda_web_recordset_get_type())
+#define GDA_WEB_RECORDSET(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_WEB_RECORDSET, GdaWebRecordset))
+#define GDA_WEB_RECORDSET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_WEB_RECORDSET, GdaWebRecordsetClass))
+#define GDA_IS_WEB_RECORDSET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_WEB_RECORDSET))
+#define GDA_IS_WEB_RECORDSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_WEB_RECORDSET))
+
+typedef struct _GdaWebRecordset        GdaWebRecordset;
+typedef struct _GdaWebRecordsetClass   GdaWebRecordsetClass;
+typedef struct _GdaWebRecordsetPrivate GdaWebRecordsetPrivate;
+
+struct _GdaWebRecordset {
+	GdaDataSelect           model;
+	GdaWebRecordsetPrivate *priv;
+};
+
+struct _GdaWebRecordsetClass {
+	GdaDataSelectClass      parent_class;
+};
+
+GType         gda_web_recordset_get_type  (void) G_GNUC_CONST;
+GdaDataModel *gda_web_recordset_new       (GdaConnection *cnc, GdaWebPStmt *ps, GdaSet *exec_params,
+					   GdaDataModelAccessFlags flags, GType *col_types,
+					   const gchar *session_id, xmlNodePtr data_node, GError **error);
+
+gboolean      gda_web_recordset_store     (GdaWebRecordset *rs, xmlNodePtr data_node, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/providers/web/gda-web-util.c b/providers/web/gda-web-util.c
new file mode 100644
index 0000000..761fd89
--- /dev/null
+++ b/providers/web/gda-web-util.c
@@ -0,0 +1,657 @@
+/* GDA 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.
+ */
+
+/*#define DEBUG_WEB_PROV*/
+
+#include <glib/gi18n-lib.h>
+#include "gda-web-util.h"
+#include <string.h>
+#include "../reuseable/reuse-all.h"
+
+/* Use the RSA reference implementation included in the RFC-1321, http://www.freesoft.org/CIE/RFC/1321/ */
+#include "global.h"
+#include "md5.h"
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#define PAD_LEN 64  /* PAD length */
+#define SIG_LEN 16  /* MD5 digest length */
+/*
+ * From RFC 2104
+ */
+static void
+hmac_md5 (uint8_t*  text,            /* pointer to data stream */
+	  int   text_len,            /* length of data stream */
+	  uint8_t*  key,             /* pointer to authentication key */
+	  int   key_len,             /* length of authentication key */
+	  uint8_t  *hmac)            /* returned hmac-md5 */
+{
+	MD5_CTX md5c;
+	uint8_t k_ipad[PAD_LEN];    /* inner padding - key XORd with ipad */
+	uint8_t k_opad[PAD_LEN];    /* outer padding - key XORd with opad */
+	uint8_t keysig[SIG_LEN];
+	int i;
+
+	/* if key is longer than PAD length, reset it to key=MD5(key) */
+	if (key_len > PAD_LEN) {
+		MD5_CTX md5key;
+
+		MD5Init (&md5key);
+		MD5Update (&md5key, key, key_len);
+		MD5Final (keysig, &md5key);
+
+		key = keysig;
+		key_len = SIG_LEN;
+	}
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(Key XOR opad, MD5(Key XOR ipad, text))
+	 *
+	 * where Key is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* Zero pads and store key */
+	memset (k_ipad, 0, PAD_LEN);
+	memcpy (k_ipad, key, key_len);
+	memcpy (k_opad, k_ipad, PAD_LEN);
+
+	/* XOR key with ipad and opad values */
+	for (i=0; i<PAD_LEN; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* perform inner MD5 */
+	MD5Init (&md5c);                    /* start inner hash */
+	MD5Update (&md5c, k_ipad, PAD_LEN); /* hash inner pad */
+	MD5Update (&md5c, text, text_len);  /* hash text */
+	MD5Final (hmac, &md5c);             /* store inner hash */
+
+	/* perform outer MD5 */
+	MD5Init (&md5c);                    /* start outer hash */
+	MD5Update (&md5c, k_opad, PAD_LEN); /* hash outer pad */
+	MD5Update (&md5c, hmac, SIG_LEN);   /* hash inner hash */
+	MD5Final (hmac, &md5c);             /* store results */
+}
+
+static gboolean
+check_hash (const gchar *key, const gchar *data, const gchar *expected_hash)
+{
+	uint8_t hmac[16];
+	GString *md5str;
+	gint i;
+	gboolean retval = TRUE;
+	
+	hmac_md5 ((uint8_t *) data, strlen (data),
+		  (uint8_t *) key, strlen (key), hmac);
+	md5str = g_string_new ("");
+	for (i = 0; i < 16; i++)
+		g_string_append_printf (md5str, "%02x", hmac[i]);
+	
+	if (strcmp (md5str->str, expected_hash))
+		retval = FALSE;	
+	
+	g_string_free (md5str, TRUE);
+	return retval;
+}
+
+/*
+ * If there is a mismatch or an error, then gda_connection_add_event_string() is used
+ *
+ *  - Modifies @sbuffer (to separate HASH from XML part)
+ *  - if all OK, extracs the <challenge> value and replace cdata->next_challenge with it (or simply
+ *    reset cdata->next_challenge to NULL)
+ *
+ * Returns: a new #xmlDocPtr, or %NULL on error
+ */
+static xmlDocPtr
+decode_buffer_response (GdaConnection *cnc, WebConnectionData *cdata, SoupBuffer *sbuffer,
+			gchar *out_status_chr, guint *out_counter_id)
+{
+	xmlDocPtr doc;
+	gchar *ptr, *response;
+
+	if (out_status_chr)
+		*out_status_chr = 0;
+	if (out_counter_id)
+		*out_counter_id = 0;
+
+	g_assert (sbuffer);
+	response = (gchar*) sbuffer->data;
+
+	for (ptr = response; *ptr && (*ptr != '\n'); ptr++);
+	if (*ptr != '\n') {
+		gda_connection_add_event_string (cnc, _("Could not parse server's reponse"));
+		return NULL;
+	}
+	*ptr = 0;
+	ptr++;
+
+	if ((cdata->key && !check_hash (cdata->key, ptr, response)) &&
+	    (cdata->server_secret && !check_hash (cdata->server_secret, ptr, response))) {
+		gda_connection_add_event_string (cnc, _("Invalid response hash"));
+		return NULL;
+	}
+	doc = xmlParseMemory (ptr, strlen (ptr));
+
+	if (!doc) {
+		gda_connection_add_event_string (cnc, _("Could not parse server's reponse"));
+		return NULL;
+	}
+	else {
+		xmlNodePtr node, root;
+
+		root = xmlDocGetRootElement (doc);
+		for (node = root->children; node; node = node->next) {
+			if (!strcmp ((gchar*) node->name, "session")) {
+				xmlChar *contents;
+				contents = xmlNodeGetContent (node);
+				g_free (cdata->session_id);
+				cdata->session_id = g_strdup ((gchar*) contents);
+				xmlFree (contents);
+			}
+			else if (!strcmp ((gchar*) node->name, "challenge")) {
+				xmlChar *contents;
+				if (cdata->next_challenge) {
+					g_free (cdata->next_challenge);
+					cdata->next_challenge = NULL;
+				}
+				contents = xmlNodeGetContent (node);
+				cdata->next_challenge = g_strdup ((gchar*) contents);
+				xmlFree (contents);
+			}
+			else if (out_status_chr && !strcmp ((gchar*) node->name, "status")) {
+				xmlChar *contents;
+				contents = xmlNodeGetContent (node);
+				*out_status_chr = *contents;
+				xmlFree (contents);
+			}
+			else if (out_counter_id && !strcmp ((gchar*) node->name, "counter")) {
+				xmlChar *contents;
+				contents = xmlNodeGetContent (node);
+				*out_counter_id = atoi ((gchar*) contents);
+				xmlFree (contents);
+			}
+			else if (!cdata->server_id && !strcmp ((gchar*) node->name, "servertype")) {
+				xmlChar *contents;
+				contents = xmlNodeGetContent (node);
+				cdata->server_id = g_strdup ((gchar*) contents);
+				xmlFree (contents);
+
+				/* FIXME: get version from server */
+				cdata->reuseable = _gda_provider_reuseable_new (cdata->server_id, NULL, NULL);
+#ifdef DEBUG_WEB_PROV
+				g_print ("REUSEABLE [%s]: %p\n", cdata->server_id, cdata->reuseable);
+#endif
+			}
+		}
+	}
+
+	return doc;
+}
+
+typedef struct {
+	GdaConnection *cnc;
+	WebConnectionData *cdata;
+} ThreadData;
+
+/* executed in sub thread */
+static void
+worker_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, ThreadData *thdata)
+{
+	xmlDocPtr doc;
+	gchar *data, *ptr;
+
+	data = g_strndup (chunk->data, chunk->length);
+	soup_message_body_set_accumulate (msg->response_body, FALSE);
+	
+#ifdef DEBUG_WEB_PROV
+	if (*data)
+		g_print (">>>> WORKER\n%s\n<<<< WORKER\n", data);
+#endif
+	if (!thdata->cdata->session_id) {
+		ptr = strstr (data, "</reply>");
+		if (ptr) {
+			gchar status;
+			guint counter_id;
+			ptr += 8;
+			*ptr = 0;
+			doc = decode_buffer_response (thdata->cnc, thdata->cdata, chunk, &status, &counter_id);
+			if (!doc || (status != 'O')) {
+				/* this cannot happen at the moment */
+				g_assert_not_reached ();
+				if (doc)
+					xmlFreeDoc (doc);
+			}
+			else {
+				gda_mutex_lock (thdata->cdata->mutex);
+				g_assert (thdata->cdata->worker_counter == counter_id);
+				gda_mutex_unlock (thdata->cdata->mutex);
+				xmlFreeDoc (doc);
+			}
+		}
+	}
+	g_free (data);
+}
+
+/* executed in sub thread */
+static gpointer
+start_worker_in_sub_thread (ThreadData *thdata)
+{
+	SoupMessage *msg;
+	gulong sigid;
+	gboolean runagain = TRUE;
+	
+	while (runagain) {
+		GString *real_url;
+		gda_mutex_lock (thdata->cdata->mutex);
+		real_url = g_string_new (thdata->cdata->worker_url);
+		if (thdata->cdata->session_id)
+			g_string_append_printf (real_url, "?%s", thdata->cdata->session_id);
+		thdata->cdata->worker_running = TRUE;
+		if (thdata->cdata->worker_counter == 0)
+			thdata->cdata->worker_counter = 1;
+		else
+			thdata->cdata->worker_counter ++;
+		gda_mutex_unlock (thdata->cdata->mutex);
+
+		msg = soup_message_new ("GET", real_url->str);
+		/*g_print ("=== WORKER Request URL: [%s]\n", real_url->str);*/
+		if (!msg) {
+			g_warning (_("Invalid HOST/SCRIPT '%s'"), real_url->str);
+			g_string_free (real_url, TRUE);
+			gda_mutex_lock (thdata->cdata->mutex);
+			thdata->cdata->worker_running = FALSE;
+			gda_mutex_unlock (thdata->cdata->mutex);
+			g_free (thdata);
+			return NULL;
+		}
+		g_string_free (real_url, TRUE);
+		
+		sigid = g_signal_connect (msg, "got-chunk",
+					  G_CALLBACK (worker_got_chunk_cb), thdata);
+		guint res;
+		res = soup_session_send_message (thdata->cdata->worker_session, msg);
+		
+		gda_mutex_lock (thdata->cdata->mutex);
+		thdata->cdata->worker_running = FALSE;
+		runagain = thdata->cdata->worker_needed;
+		runagain = runagain && SOUP_STATUS_IS_SUCCESSFUL (res);
+		gda_mutex_unlock (thdata->cdata->mutex);
+
+		g_signal_handler_disconnect (msg, sigid);
+		g_object_unref (msg);
+	}
+
+	g_free (thdata);
+#ifdef DEBUG_WEB_PROV
+	g_print ("Worker closed!\n");
+#endif
+
+	/* end of sub thread */
+	return NULL;
+}
+
+static void
+start_worker (GdaConnection *cnc, WebConnectionData *cdata)
+{
+	ThreadData *thdata;
+
+	thdata = g_new0 (ThreadData, 1); /* freed by sub thread */
+	thdata->cnc = cnc;
+	thdata->cdata = cdata;
+
+	/* set cdata->worker_running to TRUE to avoid having to add a delay */
+	gda_mutex_lock (cdata->mutex);
+	cdata->worker_running = TRUE;
+	gda_mutex_unlock (cdata->mutex);
+
+	if (! g_thread_create ((GThreadFunc) start_worker_in_sub_thread,
+			       thdata, FALSE, NULL)) {
+		g_free (thdata);
+		gda_connection_add_event_string (cnc, _("Can't start new thread"));
+		return;
+	}
+	
+	gint nb_retries;
+	for (nb_retries = 0; nb_retries < 10; nb_retries++) {
+		gboolean wait_over;
+		gda_mutex_lock (cdata->mutex);
+		wait_over = !cdata->worker_running || cdata->session_id;
+		gda_mutex_unlock (cdata->mutex);
+		if (wait_over)
+			break;
+		else
+			g_usleep (200000);
+	}
+
+	gda_mutex_lock (cdata->mutex);
+	if (!cdata->session_id) {
+		/* there was an error */
+		cdata->worker_running = FALSE;
+	}
+	gda_mutex_unlock (cdata->mutex);
+}
+
+/*
+ * Adds a HASH to the message using @hash_key, or adds "NOHASH" if @hash_key is %NULL
+ *
+ * If there is an error, then gda_connection_add_event_string() is called
+ *
+ * @out_status_chr, if NOT NULL will contain the 1st char of the <status> node's contents
+ */
+xmlDocPtr
+_gda_web_send_message_to_frontend (GdaConnection *cnc, WebConnectionData *cdata,
+				   WebMessageType msgtype, const gchar *message,
+				   const gchar *hash_key, gchar *out_status_chr)
+{
+	SoupMessage *msg;
+	guint status;
+	gchar *h_message;
+	gchar *real_url;
+	static gint counter = 0;
+
+	if (out_status_chr)
+		*out_status_chr = 0;
+
+	/* handle the need to run the worker to get an initial sessionID */
+	gda_mutex_lock (cdata->mutex);
+	cdata->worker_needed = TRUE;
+	if (!cdata->worker_running && !cdata->session_id) {
+		gda_mutex_unlock (cdata->mutex);
+		start_worker (cnc, cdata);
+
+		gda_mutex_lock (cdata->mutex);
+		if (! cdata->worker_running) {
+			gda_connection_add_event_string (cnc, _("Could not run PHP script on the server"));
+			cdata->worker_needed = FALSE;
+			gda_mutex_unlock (cdata->mutex);
+			return NULL;
+		}
+	}
+
+	/* prepare new message */
+	g_assert (cdata->session_id);
+	real_url = g_strdup_printf ("%s?%s&c=%d", cdata->front_url, cdata->session_id, counter++);
+	gda_mutex_unlock (cdata->mutex);
+	msg = soup_message_new ("POST", real_url);
+	if (!msg) {
+		gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
+		g_free (real_url);
+		return NULL;
+	}
+	g_free (real_url);
+
+	/* check context */
+	gda_mutex_lock (cdata->mutex);
+	if (gda_connection_get_transaction_status (cnc) &&
+	    (!cdata->worker_running ||
+	     ((msgtype == MESSAGE_EXEC) && (cdata->last_exec_counter != cdata->worker_counter)))) {
+		/* update cdata->last_exec_counter so next statement can be run */
+		cdata->last_exec_counter = cdata->worker_counter;
+
+		gda_connection_add_event_string (cnc, _("The transaction has been automatically rolled back"));
+		g_object_unref (msg);
+
+		gda_connection_internal_reset_transaction_status (cnc);
+		gda_mutex_unlock (cdata->mutex);
+		return NULL;
+	}
+	if (! cdata->worker_running) {
+		gda_mutex_unlock (cdata->mutex);
+		start_worker (cnc, cdata);
+
+		gda_mutex_lock (cdata->mutex);
+		if (! cdata->worker_running) {
+			gda_connection_add_event_string (cnc, _("Could not run PHP script on the server"));
+			g_object_unref (msg);
+			gda_mutex_unlock (cdata->mutex);
+			return NULL;
+		}
+	}
+	gda_mutex_unlock (cdata->mutex);
+
+	/* finalize and send message */
+ 	if (hash_key) {
+		uint8_t hmac[16];
+		GString *md5str;
+		gint i;
+		
+		hmac_md5 ((uint8_t *) message, strlen (message),
+			  (uint8_t *) hash_key, strlen (hash_key), hmac);
+		md5str = g_string_new ("");
+		for (i = 0; i < 16; i++)
+			g_string_append_printf (md5str, "%02x", hmac[i]);
+		g_string_append_c (md5str, '\n');
+		g_string_append (md5str, message);
+		h_message = g_string_free (md5str, FALSE);
+	}
+	else
+		h_message = g_strdup_printf ("NOHASH\n%s", message);
+	
+#ifdef DEBUG_WEB_PROV
+	g_print ("=== START of request ===\n%s\n=== END of request ===\n", h_message);
+#endif
+	soup_message_set_request (msg, "text/plain",
+				  SOUP_MEMORY_COPY, h_message, strlen (h_message));
+	g_free (h_message);
+	g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 20, NULL);
+	status = soup_session_send_message (cdata->front_session, msg);
+
+	gda_mutex_lock (cdata->mutex);
+	cdata->worker_needed = FALSE;
+	gda_mutex_unlock (cdata->mutex);
+
+	if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+		gda_connection_add_event_string (cnc, msg->reason_phrase);
+		g_object_unref (msg);
+		return NULL;
+	}
+
+	xmlDocPtr doc;
+	guint counter_id;
+	doc = _gda_web_decode_response (cnc, cdata, msg->response_body, out_status_chr, &counter_id);
+	g_object_unref (msg);
+	
+	gda_mutex_lock (cdata->mutex);
+	if (msgtype == MESSAGE_EXEC)
+		cdata->last_exec_counter = counter_id;
+	gda_mutex_unlock (cdata->mutex);
+
+	return doc;
+}
+
+
+/*
+ * If there is a mismatch or an error, then gda_connection_add_event_string() is used
+ *
+ *  - if all OK, extracs the <challenge> value and replace cdata->next_challenge with it (or simply
+ *    reset cdata->next_challenge to NULL)
+ *
+ * Returns: a new #xmlDocPtr, or %NULL on error
+ */
+xmlDocPtr
+_gda_web_decode_response (GdaConnection *cnc, WebConnectionData *cdata, SoupMessageBody *body,
+			  gchar *out_status_chr, guint *out_counter_id)
+{
+	SoupBuffer *sbuffer;
+	xmlDocPtr doc;
+	sbuffer = soup_message_body_flatten (body);
+#ifdef DEBUG_WEB_PROV
+	g_print ("=== START of response ===\n%s\n=== END of response ===\n", (gchar*) sbuffer->data);
+#endif
+	doc = decode_buffer_response (cnc, cdata, sbuffer, out_status_chr, out_counter_id);
+	soup_buffer_free (sbuffer);
+	return doc;
+}
+
+/*
+ * Creates a new string
+ */
+gchar *
+_gda_web_compute_token (WebConnectionData *cdata)
+{
+	uint8_t hmac[16];
+	GString *md5str;
+	gint i;
+	
+	g_return_val_if_fail (cdata->next_challenge && cdata->key, NULL);
+
+	hmac_md5 ((uint8_t *) cdata->next_challenge, strlen (cdata->next_challenge),
+		  (uint8_t *) cdata->key, strlen (cdata->key), hmac);
+	md5str = g_string_new ("");
+	for (i = 0; i < 16; i++)
+		g_string_append_printf (md5str, "%02x", hmac[i]);
+	
+	return g_string_free (md5str, FALSE);
+}
+
+/*
+ * Cleans any remaining data on the web server
+ */
+void
+_gda_web_do_server_cleanup (GdaConnection *cnc, WebConnectionData *cdata)
+{
+	SoupMessage *msg;
+	guint status;
+	gchar *real_url;
+	gint nb_retries;
+
+	/* wait for worker to finish */
+	gda_mutex_lock (cdata->mutex);
+	for (nb_retries = 0; (nb_retries < 10) && cdata->worker_running; nb_retries ++) {
+		gda_mutex_unlock (cdata->mutex);
+		g_usleep (50000);
+		gda_mutex_lock (cdata->mutex);
+	}
+	gda_mutex_unlock (cdata->mutex);
+ 
+	real_url = g_strdup_printf ("%s/gda-clean.php?%s", cdata->server_base_url, cdata->session_id);
+	msg = soup_message_new ("GET", real_url);
+	if (!msg) {
+		gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
+ 		g_free (real_url);
+		return;
+ 	}
+	g_free (real_url);
+
+	g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 5, NULL);
+	status = soup_session_send_message (cdata->front_session, msg);
+	g_object_unref (msg);
+
+	if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+		g_warning (_("Error cleaning data on the server for session %s"), cdata->session_id);
+#ifdef DEBUG_WEB_PROV
+	else
+		g_print ("CLEANUP DONE!\n");
+#endif
+}
+
+/**
+ * _gda_web_set_connection_error_from_xmldoc
+ *
+ * Handles errors reported by @doc, and ser @error if not %NULL
+ *
+ * Returns: a #GdaConnectionEvent, which must not be modified or freed
+ */
+GdaConnectionEvent *
+_gda_web_set_connection_error_from_xmldoc (GdaConnection *cnc, xmlDocPtr doc, GError **error)
+{
+	xmlNodePtr node, root;
+	GdaConnectionEvent *ev = NULL;
+
+	g_return_val_if_fail (doc, NULL);
+
+	root = xmlDocGetRootElement (doc);
+	for (node = root->children; node; node = node->next) {
+		if (!strcmp ((gchar*) node->name, "status")) {
+			xmlChar *prop;
+			prop = xmlGetProp (node, BAD_CAST "error");
+			if (prop) {
+				ev = gda_connection_add_event_string (cnc, (gchar*) prop);
+				xmlFree (prop);
+			}
+			else
+				ev = gda_connection_add_event_string (cnc, _("Non detailled error"));
+			break;
+		}
+	}
+
+	if (ev && error) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_STATEMENT_EXEC_ERROR, "%s",
+			     gda_connection_event_get_description (ev));
+	}
+
+	return ev;
+}
+
+/*
+ * Actually closes the connection from Libgda's point of view
+ */
+void
+_gda_web_change_connection_to_closed (GdaConnection *cnc, WebConnectionData *cdata)
+{
+	cdata->forced_closing = TRUE;
+	gda_connection_close_no_warning (cnc);
+}
+
+/*
+ * Free connection's specific data
+ */
+void
+_gda_web_free_cnc_data (WebConnectionData *cdata)
+{
+	if (!cdata)
+		return;
+
+	if (cdata->reuseable) {
+		g_assert (cdata->reuseable->operations);
+		if (cdata->reuseable->operations->re_reset_data)
+			cdata->reuseable->operations->re_reset_data (cdata->reuseable);
+		g_free (cdata->reuseable);
+	}
+	g_free (cdata->server_id);
+	g_free (cdata->server_base_url);
+	g_free (cdata->front_url);
+	g_free (cdata->worker_url);
+	if (cdata->mutex)
+		gda_mutex_free (cdata->mutex);
+	if (cdata->worker_session)
+		g_object_unref (cdata->worker_session);
+	if (cdata->front_session)
+		g_object_unref (cdata->front_session);
+	g_free (cdata->session_id);
+	g_free (cdata->server_secret);
+	g_free (cdata->key);
+	g_free (cdata->next_challenge);
+
+	g_free (cdata);
+}
diff --git a/providers/web/gda-web-util.h b/providers/web/gda-web-util.h
new file mode 100644
index 0000000..06df2d1
--- /dev/null
+++ b/providers/web/gda-web-util.h
@@ -0,0 +1,59 @@
+/* GDA 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_WEB_UTIL_H__
+#define __GDA_WEB_UTIL_H__
+
+#include "gda-web.h"
+#include <libgda/libgda.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	MESSAGE_HELLO,
+	MESSAGE_CONNECT,
+	MESSAGE_BYE,
+	MESSAGE_PREPARE,
+	MESSAGE_UNPREPARE,
+	MESSAGE_EXEC,
+	MESSAGE_META
+} WebMessageType;
+
+gchar *_gda_web_compute_token (WebConnectionData *cdata);
+
+xmlDocPtr _gda_web_send_message_to_frontend (GdaConnection *cnc, WebConnectionData *cdata,
+					     WebMessageType msgtype, const gchar *message,
+					     const gchar *hash_key, gchar *out_status_chr);
+
+xmlDocPtr _gda_web_decode_response (GdaConnection *cnc, WebConnectionData *cdata, SoupMessageBody *body,
+				    gchar *out_status_chr, guint *out_counter_id);
+
+GdaConnectionEvent *_gda_web_set_connection_error_from_xmldoc (GdaConnection *cnc, xmlDocPtr doc, GError **error);
+
+void      _gda_web_do_server_cleanup (GdaConnection *cnc, WebConnectionData *cdata);
+void      _gda_web_change_connection_to_closed (GdaConnection *cnc, WebConnectionData *cdata);
+void      _gda_web_free_cnc_data (WebConnectionData *cdata);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/providers/web/gda-web.h b/providers/web/gda-web.h
new file mode 100644
index 0000000..5aa3140
--- /dev/null
+++ b/providers/web/gda-web.h
@@ -0,0 +1,67 @@
+/* GDA web 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.
+ */
+
+#ifndef __GDA_WEB_H__
+#define __GDA_WEB_H__
+
+/*
+ * Provider name
+ */
+#define WEB_PROVIDER_NAME "Web"
+
+#include <libsoup/soup.h>
+#include <libgda/gda-mutex.h>
+#include <libgda/gda-connection.h>
+#include "../reuseable/gda-provider-reuseable.h"
+
+/*
+ * Provider's specific connection data
+ */
+typedef struct {
+	GdaProviderReuseable *reuseable; /* pointer to GdaProviderReuseable, not inherited! */
+	GdaMutex *mutex; /* protected access */
+
+	gchar *server_id; /* PostgreSQL, MySQL, ... */
+
+	gboolean forced_closing;
+	gchar *server_base_url;
+	gchar *front_url;
+	gchar *worker_url;
+
+	gchar *server_secret;
+	gchar *key;
+	gchar *next_challenge;
+
+	gchar *session_id; /* as defined by the server */
+	
+	/* worker attributes */
+	SoupSession *worker_session;
+	gboolean worker_needed;
+	gboolean worker_running;
+	guint worker_counter; /* incremented each time the worker is run */
+
+	/* front and others attributes */
+	SoupSession *front_session;
+	guint last_exec_counter; /* the worker counter which replied to the last EXEC command */
+} WebConnectionData;
+
+#endif
diff --git a/providers/web/libgda-web-4.0.pc.in b/providers/web/libgda-web-4.0.pc.in
new file mode 100644
index 0000000..ca0a707
--- /dev/null
+++ b/providers/web/libgda-web-4.0.pc.in
@@ -0,0 +1,9 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: libgda-web- GDA_ABI_MAJOR_VERSION@  GDA_ABI_MINOR_VERSION@
+Description: GDA (GNOME Data Access) Web provider
+Requires: libgda- GDA_ABI_MAJOR_VERSION@  GDA_ABI_MINOR_VERSION@
+Version: @VERSION@
diff --git a/providers/web/libmain.c b/providers/web/libmain.c
new file mode 100644
index 0000000..f17bfde
--- /dev/null
+++ b/providers/web/libmain.c
@@ -0,0 +1,104 @@
+/* GDA Web Provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      TO_ADD: your name and email
+ *
+ * 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.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include "gda-web.h"
+#include "gda-web-provider.h"
+
+static gchar      *module_path = NULL;
+const gchar       *plugin_get_name (void);
+const gchar       *plugin_get_description (void);
+gchar             *plugin_get_dsn_spec (void);
+GdaServerProvider *plugin_create_provider (void);
+
+/*
+ * Functions executed when calling g_module_open() and g_module_close()
+ */
+const gchar *
+g_module_check_init (GModule *module)
+{
+        /*g_module_make_resident (module);*/
+        return NULL;
+}
+
+void
+g_module_unload (GModule *module)
+{
+        g_free (module_path);
+        module_path = NULL;
+}
+
+/*
+ * Normal plugin functions 
+ */
+void
+plugin_init (const gchar *real_path)
+{
+        if (real_path)
+                module_path = g_strdup (real_path);
+}
+
+const gchar *
+plugin_get_name (void)
+{
+	return WEB_PROVIDER_NAME;
+}
+
+const gchar *
+plugin_get_description (void)
+{
+	return _("Provider for web server proxies");
+}
+
+gchar *
+plugin_get_dsn_spec (void)
+{
+	gchar *ret, *dir;
+
+	dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+	ret = gda_server_provider_load_file_contents (module_path, dir, "web_specs_dsn.xml");
+	g_free (dir);
+	return ret;
+}
+
+gchar *
+plugin_get_auth_spec (void)
+{
+	gchar *ret, *dir;
+
+	dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+	ret = gda_server_provider_load_file_contents (module_path, dir, "web_specs_auth.xml");
+	g_free (dir);
+	return ret;
+}
+
+GdaServerProvider *
+plugin_create_provider (void)
+{
+	GdaServerProvider *prov;
+
+	prov = (GdaServerProvider*) g_object_new (GDA_TYPE_WEB_PROVIDER, NULL);
+        g_object_set_data ((GObject *) prov, "GDA_PROVIDER_DIR", module_path);
+        return prov;
+}
diff --git a/providers/web/php/README b/providers/web/php/README
new file mode 100644
index 0000000..8ff3940
--- /dev/null
+++ b/providers/web/php/README
@@ -0,0 +1,35 @@
+Installation instructions
+=========================
+
+The PHP scripts in this directory are used by the GDA Web provider and
+have to be installed on a web server. The location of the scripts will have
+to be passed as the PATH argument on the Libgda side when
+opening the connection. For example if the scripts are accessible
+as: http://example.com/path/to/gda-worker.php then the PATH argument
+will have to be "path/to".
+
+These scripts need the PHP engine's SimpleXML extension (which is
+usually included when compiling PHP) and the PEAR's MDB2 module
+(it's the module which actually opens the connection to the database)
+for which the current implementation is provided under the PEAR_MDB2/
+directory.
+
+Once the scripts are installed, some configuration needs to be done
+in the gda-config.php script.
+
+Note that for better security, this script should not be accessible
+from the outside (ie it should be put outside ot the web server's
+root directory, and the gda-front.php and gda-worker.php scripts should be
+modified to point to its real location).
+
+
+Note about gda-tester.php
+-------------------------
+The gda-tester.php script can be executed to diagnose any setup problem,
+it reports errors if the MDB2 PEAR of SimpleXML extensions are missing,
+and, for each configured connection, tests if it can be opened.
+
+Note that by default the gda-tester.php will _not_ display any
+configured connection information, this needs to be enabled in the
+script's source. Better yet is to remove that file once not necessary
+anymore as it could leak usefull information.
diff --git a/providers/web/php/gda-clean.php b/providers/web/php/gda-clean.php
new file mode 100644
index 0000000..ea8b1d2
--- /dev/null
+++ b/providers/web/php/gda-clean.php
@@ -0,0 +1,23 @@
+<?php
+/* Configure session cache */
+session_cache_limiter('nocache');
+session_start();
+
+include_once "gda-utils.php";
+include_once "gda-config.php";
+
+header('Content-type: text/plain; charset=UTF-8');
+
+$cmdfile = get_command_filename (session_id ());
+$replyfile = get_reply_filename (session_id ());
+
+ unlink ($cmdfile);
+ unlink ($replyfile);
+
+/* all cleaned */
+$reply = new SimpleXMLElement("<reply></reply>");
+$reply->addChild ("status", "OK");
+echo gda_add_hash ($init_shared, $reply->asXml());
+session_destroy ();
+
+?>
diff --git a/providers/web/php/gda-config.php b/providers/web/php/gda-config.php
new file mode 100644
index 0000000..8acd867
--- /dev/null
+++ b/providers/web/php/gda-config.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * initial shared secret: will have to be passed as the SECRET argument when opening
+ * the connection from Libgda
+ */
+$init_shared = "MySecret";
+
+/*
+ * declared connections: for each connection which can be opened by Libgda, the
+ * the connection's password and the real connection's DSN need to be added respectively
+ * to the $cnc and $dsn arrays, using the connection name as a key. The connection name
+ * and password have no significance outside of the Libgda's context and be arbitrary.
+ * However the real connection's DSN need to be valid for the PEAR's MDB2 module, as
+ * per http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php
+ *
+ */
+
+/* sample connection cnc1 */
+$cnc["cnc1"] = "MyPass1";
+//$dsn["cnc1"] = "pgsql://vivien unix(/var/run/postgresql)/sales";
+$dsn["cnc1"] = "pgsql://unix(/tmp)/sales";
+
+/* sample connection cnc2 */
+$cnc["cnc2"] = "MyPass2";
+$dsn["cnc2"] = "mysql://user unix(/path/to/socket)/sales";
+
+?>
diff --git a/providers/web/php/gda-exception.php b/providers/web/php/gda-exception.php
new file mode 100644
index 0000000..d59372b
--- /dev/null
+++ b/providers/web/php/gda-exception.php
@@ -0,0 +1,9 @@
+<?php
+class GdaException extends Exception {
+    var $cnc_closed;
+    // Constructor
+    public function __construct($msg, $cnc_closed) {
+        parent :: __construct($msg);
+        $this->cnc_closed = $cnc_closed;
+    }
+}
diff --git a/providers/web/php/gda-front.php b/providers/web/php/gda-front.php
new file mode 100644
index 0000000..1de3f76
--- /dev/null
+++ b/providers/web/php/gda-front.php
@@ -0,0 +1,68 @@
+<?php
+include_once "gda-utils.php";
+include_once "gda-config.php";
+
+/*
+ * REM: no session is started in this script because sessions would serialize the
+ * execution of the gda-front.php and gda-worker.php scripts. Only the session ID, transmitted
+ * in the URL is used here.
+ */
+
+header ('Content-type: text/plain; charset=UTF-8');
+
+$datafile = null;
+
+try {
+	global $datafile;
+	$text = $GLOBALS["HTTP_RAW_POST_DATA"];
+	if ($text == "")
+		throw new Exception ("Empty command");
+
+	/* Send command */
+	$cmdfile = get_command_filename ($_GET['PHPSESSID']);
+	if (! file_exists ($cmdfile))
+		throw new Exception ("Bad setup $cmdfile");
+	
+	$datafile = tempnam (session_save_path (), "GDACommand");
+	//echo "DATAFILE [$datafile]\n"; flush (); ob_flush();
+	if (file_put_contents ($datafile, $text) == false)
+		throw new Exception ("Can't create command file ".$cmdfile);
+	
+	$file = fopen ($cmdfile, "w");
+	if (fwrite ($file, $datafile) == false) // block until there is a reader
+		throw new Exception ("Can't send command");
+	fclose ($file);
+
+	/* wait for reply, and destroy the reading file */
+	$text = "";
+	$replyfile = get_reply_filename ($_GET['PHPSESSID']);
+	if (! file_exists ($replyfile))
+		throw new Exception ("Bad setup");
+	$file = fopen ($replyfile, "r");
+	$datafile = fread ($file, 8192);
+	fclose ($file);
+	//echo "DATAFILE [$datafile]\n"; flush (); ob_flush();
+
+	if (filesize ($datafile) == 0)
+		throw new Exception ("No reply");
+
+	$file = fopen ($datafile, 'rb');
+	fpassthru ($file);
+	@unlink ($datafile);
+	
+	echo $text;
+}
+catch (Exception $e) {
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$node = $reply->addChild ('status', "ERROR");
+	$node->addAttribute ("error", $e->getMessage());
+
+	global $init_shared;
+	echo gda_add_hash ($init_shared, $reply->asXml());
+
+	global $datafile;
+	if (isset ($datafile))
+		@unlink ($datafile);
+}
+
+?>
diff --git a/providers/web/php/gda-meta.php b/providers/web/php/gda-meta.php
new file mode 100644
index 0000000..ce227d1
--- /dev/null
+++ b/providers/web/php/gda-meta.php
@@ -0,0 +1,648 @@
+<?php
+
+/*
+ * _gda_web_meta__info
+ */
+function do_meta_info ($reply, &$mdb2)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+
+	$node = $reply->addChild ("gda_array", null);
+	declare_column ($node, 0, "name", "string", false);
+	$data = $node->addChild ("gda_array_data", null);
+	$xmlrow = $data->addChild ("gda_array_row", null);
+
+	add_value_child ($xmlrow, $catalog);
+}
+
+/*
+ * _gda_web_meta__btypes
+ */
+function do_meta_btypes ($reply, &$mdb2)
+{
+	$res = $mdb2->datatype->getValidTypes();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+
+	$col = 0;
+	declare_column ($node, $col++, "short_type_name", "string", false);
+	declare_column ($node, $col++, "full_type_name", "string", false);
+	declare_column ($node, $col++, "gtype", "string", false);
+	declare_column ($node, $col++, "comments", "string", true);
+	declare_column ($node, $col++, "synonyms", "string", true);
+	declare_column ($node, $col++, "internal", "boolean", false);
+
+	$data = $node->addChild ("gda_array_data", null);
+	foreach ($res as $i => $value) {
+		$xmlrow = $data->addChild ("gda_array_row", null);
+		add_value_child ($xmlrow, $i);
+		add_value_child ($xmlrow, $i);
+		$field = array ("type" => $i);
+		$mtype = $mdb2->datatype->mapNativeDatatype ($field);
+		if (PEAR::isError($mtype))
+			add_value_child ($xmlrow, "gchararray"); // FIXME, maybe use mapNativeDatatype()
+		else {
+			$done = false;
+			foreach ($mtype[0] as $j => $type) {
+				if ($type == "text") {
+					add_value_child ($xmlrow, $type);
+					$done = true;
+					break;
+				}
+			}
+			if (!$done)
+				add_value_child ($xmlrow, $mtype[0][0]);
+		}
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, false, true);
+	}
+}
+
+/*
+ * _gda_web_meta__schemata
+ */
+function do_meta_schemas ($reply, &$mdb2, $schema_name = null)
+{
+	if (determine_db_type ($mdb2) ==  "PostgreSQL") {
+		do_meta_schemas_pgsql ($reply, &$mdb2);
+		return;
+	}
+	$catalog = get_catalog ($reply, &$mdb2);
+
+	$res = $mdb2->manager->listDatabases();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	declare_column ($node, 0, "catalog_name", "string", false);
+	declare_column ($node, 1, "schema_name", "string", false);
+	declare_column ($node, 2, "schema_owner", "string", false);
+	declare_column ($node, 3, "schema_internal", "boolean", false);
+	$data = $node->addChild ("gda_array_data", null);
+	
+	foreach ($res as $i => $value) {
+		if ($schema_name && ($schema_name != $value))
+			continue;
+		$xmlrow = $data->addChild ("gda_array_row", null);
+
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $value);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, false, true);
+	}
+}
+
+function do_meta_schemas_pgsql ($reply, &$mdb2)
+{
+	$sql = "SELECT current_database()::information_schema.sql_identifier AS catalog_name, n.nspname::information_schema.sql_identifier AS schema_name, u.rolname::information_schema.sql_identifier AS schema_owner, CASE WHEN n.nspname::information_schema.sql_identifier ~ '^pg_' THEN TRUE WHEN n.nspname::information_schema.sql_identifier = 'information_schema' THEN TRUE ELSE FALSE END FROM pg_namespace n, pg_roles u WHERE n.nspowner = u.oid";
+	$res = &$mdb2->query ($sql);
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	declare_column ($node, 0, "catalog_name", "string", false);
+	declare_column ($node, 1, "schema_name", "string", false);
+	declare_column ($node, 2, "schema_owner", "string", false);
+	declare_column ($node, 3, "schema_internal", "boolean", false);
+	$data = $node->addChild ("gda_array_data", null);
+	while (($row = $res->fetchRow())) {
+		$xmlrow = $data->addChild ("gda_array_row", null);
+
+		add_value_child ($xmlrow, $row[0]);
+		add_value_child ($xmlrow, $row[1]);
+		add_value_child ($xmlrow, $row[2]);
+		add_value_child ($xmlrow, $row[3] == 't' ? "TRUE" : "FALSE");
+	}
+}
+
+/*
+ * _gda_web_meta__schemata
+ */
+function do_meta_tables ($reply, &$mdb2, $table_schema = null, $table_name = null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->manager->listTables();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "table_type", "string", false);
+	declare_column ($node, $col++, "is_insertable_into", "boolean", true);
+	declare_column ($node, $col++, "table_comments", "string", true);
+	declare_column ($node, $col++, "table_short_name", "string", false);
+	declare_column ($node, $col++, "table_full_name", "string", false);
+	declare_column ($node, $col++, "table_owner", "string", true);
+	$data = $node->addChild ("gda_array_data", null);
+	
+	foreach ($res as $i => $value) {
+		if ($table_name && ($table_name != $value))
+			continue;
+		$xmlrow = $data->addChild ("gda_array_row", null);
+		
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $value);
+		add_value_child ($xmlrow, "BASE TABLE");
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, $value);
+		add_value_child ($xmlrow, $catalog.".".$value);
+		add_value_child ($xmlrow, null);
+	}
+}
+
+function do_meta_views ($reply, &$mdb2, $table_schema = null, $table_name = null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+
+	$res = $mdb2->manager->listViews();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "view_definition", "string", true);
+	declare_column ($node, $col++, "check_option", "string", true);
+	declare_column ($node, $col++, "is_updatable", "boolean", true);
+	$data = $node->addChild ("gda_array_data", null);
+	
+	foreach ($res as $i => $value) {
+		if ($table_name && ($table_name != $value))
+			continue;
+
+		$xmlrow = $data->addChild ("gda_array_row", null);
+
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $value);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+	}
+}
+
+function do_meta_columns_named ($reply, &$mdb2, $data, $table_name)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->reverse->tableInfo($table_name);
+	if (PEAR::isError($res))
+		return;
+
+	foreach ($res as $i => $value) {
+		$xmlrow = $data->addChild ("gda_array_row", null);
+
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $table_name);
+		add_value_child ($xmlrow, $value['name']);
+		add_value_child ($xmlrow, $i+1);
+
+		if ($value['default'] != "")
+			add_value_child ($xmlrow, $value['default']);
+		else
+			add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, true, true);
+
+		add_value_child ($xmlrow, $value['nativetype']);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, mdb2_type_to_gtype ($value['mdb2type']));
+		add_value_child ($xmlrow, $value['len']);
+
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+
+		if ($value['autoincrement'])
+			add_value_child ($xmlrow, "AUTO_INCREMENT");
+		else
+			add_value_child ($xmlrow, null);
+
+		add_value_child ($xmlrow, null);
+		add_value_child ($xmlrow, null);
+	}
+}
+
+function do_meta_columns ($reply, &$mdb2, $table_schema=null, $table_name=null)
+{
+	if (! $table_name) {
+		$res = $mdb2->manager->listTables();
+		handle_pear_error ($res, $reply);
+	}
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "column_name", "string", false);
+	declare_column ($node, $col++, "ordinal_position", "gint", false);
+	declare_column ($node, $col++, "column_default", "string", true);
+	declare_column ($node, $col++, "is_nullable", "boolean", false);
+	declare_column ($node, $col++, "data_type", "string", true);
+	declare_column ($node, $col++, "array_spec", "string", true);
+	declare_column ($node, $col++, "gtype", "string", false);
+	declare_column ($node, $col++, "character_maximum_length", "gint", true);
+	declare_column ($node, $col++, "character_octet_length", "gint", true);
+	declare_column ($node, $col++, "numeric_precision", "gint", true);
+	declare_column ($node, $col++, "numeric_scale", "gint", true);
+	declare_column ($node, $col++, "datetime_precision", "gint", true);
+	declare_column ($node, $col++, "character_set_catalog", "string", true);
+	declare_column ($node, $col++, "character_set_schema", "string", true);
+	declare_column ($node, $col++, "character_set_name", "string", true);
+	declare_column ($node, $col++, "collation_catalog", "string", true);
+	declare_column ($node, $col++, "collation_schema", "string", true);
+	declare_column ($node, $col++, "collation_name", "string", true);
+	declare_column ($node, $col++, "extra", "string", true);
+	declare_column ($node, $col++, "is_updatable", "boolean", true);
+	declare_column ($node, $col++, "column_comments", "string", true);
+
+	$data = $node->addChild ("gda_array_data", null);
+
+	if ($table_name)
+		do_meta_columns_named ($reply, $mdb2, $data, $table_name);
+	else {
+		foreach ($res as $i => $value)
+			do_meta_columns_named ($reply, $mdb2, $data, $value);
+	}
+}
+
+function do_meta_constraints_tab ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->manager->listTables();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "constraint_catalog", "string", true);
+	declare_column ($node, $col++, "constraint_schema", "string", true);
+	declare_column ($node, $col++, "constraint_name", "string", false);
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "constraint_type", "string", false);
+	declare_column ($node, $col++, "check_clause", "string", true);
+	declare_column ($node, $col++, "is_deferrable", "boolean", true);
+	declare_column ($node, $col++, "initially_deferred", "boolean", true);
+
+	$data = $node->addChild ("gda_array_data", null);
+	foreach ($res as $i => $value) {
+		if ($table_name && ($table_name != $value))
+			continue;
+
+		$constraints = $mdb2->manager->listTableConstraints ($value);
+		if (PEAR::isError($constraints))
+			continue;
+
+		foreach ($constraints as $cindex => $cname) {
+			if ($constraint_name && ($constraint_name != $cname))
+				continue;
+
+			$cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+			if (PEAR::isError($cdef))
+			continue;
+
+			$xmlrow = $data->addChild ("gda_array_row", null);
+			
+			add_value_child ($xmlrow, null);
+			add_value_child ($xmlrow, null);
+			add_value_child ($xmlrow, $cname);
+			add_value_child ($xmlrow, $catalog);
+			add_value_child ($xmlrow, $catalog);
+			add_value_child ($xmlrow, $value);
+			if ($cdef ["primary"]) {
+				add_value_child ($xmlrow, "PRIMARY KEY");
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+			}
+			else if ($cdef ["unique"]) {
+				add_value_child ($xmlrow, "UNIQUE");
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+			}
+			else if ($cdef ["foreign"]) {
+				add_value_child ($xmlrow, "FOREIGN KEY");
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, $cdef ["deferrable"] ? true : false, true);
+				add_value_child ($xmlrow, $cdef ["initiallydeferred"] ? true : false, true);
+			}
+			else if ($cdef ["check"]) {
+				add_value_child ($xmlrow, "CHECK");
+				add_value_child ($xmlrow, null); /* FIXME: how to obtain that ? */
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+			}
+			else {
+				add_value_child ($xmlrow, "UNKNOWN");
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+				add_value_child ($xmlrow, null);
+			}
+		}
+	}
+}
+
+
+function do_meta_constraints_ref ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->manager->listTables();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "constraint_name", "string", false);
+	declare_column ($node, $col++, "ref_table_catalog", "string", false);
+	declare_column ($node, $col++, "ref_table_schema", "string", false);
+	declare_column ($node, $col++, "ref_table_name", "string", false);
+	declare_column ($node, $col++, "ref_constraint_name", "string", false);	
+	declare_column ($node, $col++, "match_option", "string", true);
+	declare_column ($node, $col++, "update_rule", "string", true);
+	declare_column ($node, $col++, "delete_rule", "string", true);
+
+
+	$data = $node->addChild ("gda_array_data", null);
+	foreach ($res as $i => $value) {
+		if ($table_name && ($table_name != $value))
+			continue;
+
+		$constraints = $mdb2->manager->listTableConstraints ($value);
+		if (PEAR::isError($constraints))
+			continue;
+
+		foreach ($constraints as $cindex => $cname) {
+			if ($constraint_name && ($constraint_name != $cname))
+				continue;
+
+			$cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+			if (PEAR::isError($cdef))
+			continue;
+
+			if (! $cdef ["foreign"])
+				continue;
+
+			$xmlrow = $data->addChild ("gda_array_row", null);
+			
+			add_value_child ($xmlrow, $catalog);
+			add_value_child ($xmlrow, $catalog);
+			add_value_child ($xmlrow, $value);
+			add_value_child ($xmlrow, $cname);
+
+			add_value_child ($xmlrow, $catalog);
+			add_value_child ($xmlrow, $catalog);
+			add_value_child ($xmlrow, $cdef["references"]["table"]);
+			add_value_child ($xmlrow, _get_table_pk_constraint ($mdb2, $cdef["references"]));
+
+			if (isset ($cdef["match"]) && ($cdef["match"] != "UNSPECIFIED"))
+				add_value_child ($xmlrow, $cdef["match"]);
+			else
+				add_value_child ($xmlrow, null);
+			add_value_child ($xmlrow, $cdef["onupdate"]);
+			add_value_child ($xmlrow, $cdef["ondelete"]);
+		}
+	}
+}
+
+function _get_table_pk_constraint (&$mdb2, $references)
+{
+	$table_name = $references["table"];
+	$constraints = $mdb2->manager->listTableConstraints ($table_name);
+	if (PEAR::isError($constraints))
+		return "";
+	foreach ($constraints as $cindex => $cname) {
+		$cdef = $mdb2->reverse->getTableConstraintDefinition ($table_name, $cname);
+		if (PEAR::isError($constraints))
+			continue;
+		if (! $cdef ["primary"])
+			continue;
+
+		$rfields = $references["fields"];
+		$allmatch = true;
+		foreach ($cdef ["fields"] as $fname => $attrs) {
+			if (!isset ($rfields [$fname]) ||
+			    ($rfields [$fname]["position"] != $attrs["position"])) {
+				$allmatch = false;
+				break;
+			}
+		}
+
+		if ($allmatch)
+			return $cname;
+	}
+	return "";
+}
+
+function do_meta_key_columns ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->manager->listTables();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "constraint_name", "string", false);
+	declare_column ($node, $col++, "column_name", "string", false);
+	declare_column ($node, $col++, "ordinal_position", "gint", false);
+
+	$data = $node->addChild ("gda_array_data", null);
+	foreach ($res as $i => $value) {
+		if ($table_name && ($table_name != $value))
+			continue;
+
+		$constraints = $mdb2->manager->listTableConstraints ($value);
+		if (PEAR::isError($constraints))
+			continue;
+
+		foreach ($constraints as $cindex => $cname) {
+			if ($constraint_name && ($constraint_name != $cname))
+				continue;
+
+			$cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+			if (PEAR::isError($cdef))
+				continue;
+
+			if (! $cdef ["primary"])
+				continue;
+
+			$fields = $cdef["fields"];
+			if (!$fields)
+				continue;
+
+			foreach ($fields as $fname => $attrs) {
+				$xmlrow = $data->addChild ("gda_array_row", null);
+			
+				add_value_child ($xmlrow, $catalog);
+				add_value_child ($xmlrow, $catalog);
+				add_value_child ($xmlrow, $value);
+				add_value_child ($xmlrow, $cname);
+				add_value_child ($xmlrow, $fname);
+				add_value_child ($xmlrow, $attrs["position"]);
+			}
+		}
+	}
+}
+
+function do_meta_check_columns ($reply, &$mdb2, $table_schema=null, $table_name=null, $constraint_name=null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->manager->listTables();
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "table_catalog", "string", false);
+	declare_column ($node, $col++, "table_schema", "string", false);
+	declare_column ($node, $col++, "table_name", "string", false);
+	declare_column ($node, $col++, "constraint_name", "string", false);
+	declare_column ($node, $col++, "column_name", "string", false);
+
+	$data = $node->addChild ("gda_array_data", null);
+	foreach ($res as $i => $value) {
+		if ($table_name && ($table_name != $value))
+			continue;
+
+		$constraints = $mdb2->manager->listTableConstraints ($value);
+		if (PEAR::isError($constraints))
+			continue;
+
+		foreach ($constraints as $cindex => $cname) {
+			if ($constraint_name && ($constraint_name != $cname))
+				continue;
+
+			$cdef = $mdb2->reverse->getTableConstraintDefinition ($value, $cname);
+			if (PEAR::isError($cdef))
+				continue;
+
+			if (! $cdef ["check"])
+				continue;
+
+			$fields = $cdef["fields"];
+			if (!$fields)
+				continue;
+
+			foreach ($fields as $fname => $attrs) {
+				$xmlrow = $data->addChild ("gda_array_row", null);
+			
+				add_value_child ($xmlrow, $catalog);
+				add_value_child ($xmlrow, $catalog);
+				add_value_child ($xmlrow, $value);
+				add_value_child ($xmlrow, $cname);
+				add_value_child ($xmlrow, $fname);
+			}
+		}
+	}
+}
+
+function do_meta_triggers ($reply, &$mdb2, $table_schema=null, $table_name=null)
+{
+	$catalog = get_catalog ($reply, &$mdb2);
+	$res = $mdb2->manager->listTableTriggers ($table_name);
+	handle_pear_error ($res, $reply);
+
+	$node = $reply->addChild ("gda_array", null);
+	$col = 0;
+	declare_column ($node, $col++, "trigger_catalog", "string", false);
+	declare_column ($node, $col++, "trigger_schema", "string", false);
+	declare_column ($node, $col++, "trigger_name", "string", false);
+	declare_column ($node, $col++, "event_manipulation", "string", false);
+	declare_column ($node, $col++, "event_object_catalog", "string", false);
+	declare_column ($node, $col++, "event_object_schema", "string", false);
+	declare_column ($node, $col++, "event_object_table", "string", false);
+	declare_column ($node, $col++, "action_statement", "string", true);
+	declare_column ($node, $col++, "action_orientation", "string", false);
+	declare_column ($node, $col++, "condition_timing", "string", false);
+	declare_column ($node, $col++, "trigger_comments", "string", true);
+	declare_column ($node, $col++, "trigger_short_name", "string", false);
+	declare_column ($node, $col++, "trigger_full_name", "string", false);
+
+	$data = $node->addChild ("gda_array_data", null);
+	foreach ($res as $i => $value) {
+		$tdef = $mdb2->reverse->getTriggerDefinition($value);
+		if (PEAR::isError($constraints))
+			continue;
+
+		print_r ($tdef);
+
+		$xmlrow = $data->addChild ("gda_array_row", null);
+		
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $value);
+		add_value_child ($xmlrow, $tdef ["trigger_event"]);
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $catalog);
+		add_value_child ($xmlrow, $tdef ["table_name"]);
+		add_value_child ($xmlrow, $tdef ["trigger_body"]);
+		add_value_child ($xmlrow, "ROW"); /* FIXME */
+		add_value_child ($xmlrow, $tdef ["trigger_type"]);
+		add_value_child ($xmlrow, $tdef ["trigger_comment"]);
+		add_value_child ($xmlrow, $value);
+		add_value_child ($xmlrow, $catalog.".".$value);
+	}
+}
+
+/*
+ * get the connection's catalog
+ */
+$catalog = null;
+function get_catalog ($reply, &$mdb2)
+{
+	global $catalog;
+	if (!$catalog) {
+		$res = MDB2::parseDSN ($_SESSION['dsn']);
+		handle_pear_error ($res, $reply);
+		$catalog = $res ['database'];
+	}
+	return $catalog;
+}
+
+/*
+ * Declare a column in the resulting XML tree
+ */
+function declare_column ($arraynode, $id, $name, $gdatype, $nullok)
+{
+	$field = $arraynode->addChild ("gda_array_field", null);
+
+	$field->addAttribute ("id", "FI".$id);
+	$field->addAttribute ("name", $name);
+	$field->addAttribute ("gdatype", $gdatype);
+	$field->addAttribute ("nullok", $nullok ? "TRUE" : "FALSE");
+}
+
+/*
+ * Add a <gda_value> node to @node
+ */
+function add_value_child ($node, $value, $isbool = false)
+{
+	$val = $node->addChild ("gda_value");
+	if (isset ($value)) {
+		if ($isbool)
+			$val[0] = ($value === true || $value == 't') ? "TRUE" : "FALSE";
+		else
+			$val[0] = str_replace ("&", "&amp;", $value);
+	}
+	else
+		$val->addAttribute ("isnull", "t");
+	return $val;
+}
+?>
\ No newline at end of file
diff --git a/providers/web/php/gda-setup.php b/providers/web/php/gda-setup.php
new file mode 100644
index 0000000..eb98391
--- /dev/null
+++ b/providers/web/php/gda-setup.php
@@ -0,0 +1,57 @@
+<?php
+/* Configure session cache */
+session_cache_limiter('nocache');
+session_start();
+
+header('Content-type: text/plain; charset=UTF-8');
+
+include_once "gda-utils.php";
+include_once "gda-config.php";
+
+if (! try_include ("MDB2.php") && ! try_include ("PEAR".DIRECTORY_SEPARATOR."MDB2.php")) {
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$node = $reply->addChild ("status", "ERROR");
+	$node->addAttribute ("error", "The PEAR MDB2 extension is required");
+	echo gda_add_hash ($init_shared, $reply->asXml());
+	session_destroy ();
+	exit (1);
+}
+
+if (! extension_loaded ("SimpleXML")) {
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$node = $reply->addChild ("status", "ERROR");
+	$node->addAttribute ("error", "The SimpleXML extension is required");
+	echo gda_add_hash ($init_shared, $reply->asXml());
+	session_destroy ();
+	exit (1);
+}
+
+$cmdfile = get_command_filename (session_id ());
+$replyfile = get_reply_filename (session_id ());
+
+umask(0);
+$mode = 0600;
+posix_mkfifo ($cmdfile, $mode);
+posix_mkfifo ($replyfile, $mode);
+
+if (!file_exists ($cmdfile) ||
+    !file_exists ($replyfile)) {
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$node = $reply->addChild ("status", "ERROR");
+	$node->addAttribute ("error", "Can't create named pipes");
+	echo gda_add_hash ($init_shared, $reply->asXml());
+	
+	@unlink ($cmdfile);
+	@unlink ($replyfile);
+	session_destroy ();
+	exit (1);
+}
+
+/* all setup */
+$reply = new SimpleXMLElement("<reply></reply>");
+$reply->addChild ('session', htmlspecialchars(SID));
+$reply->addChild ("status", "OK");
+echo gda_add_hash ($init_shared, $reply->asXml());
+flush ();
+ob_flush ();
+?>
diff --git a/providers/web/php/gda-tester.php b/providers/web/php/gda-tester.php
new file mode 100644
index 0000000..07781ca
--- /dev/null
+++ b/providers/web/php/gda-tester.php
@@ -0,0 +1,50 @@
+<?php
+session_cache_limiter('nocache');
+header('Content-type: text/plain; charset=UTF-8');
+
+include_once "gda-utils.php";
+include_once "gda-exception.php";
+include_once "gda-config.php";
+
+$test_connections = false; // set to true to enable each connection testing
+
+echo "Gda connections tester\n----------------------\n\n";
+
+if (! try_include ("MDB2.php", true)) {
+	echo "ERROR: The PEAR MDB2 extension is required\n";
+	exit (1);
+}
+
+if (! extension_loaded ("SimpleXML")) {
+	echo "ERROR: The SimpleXML extension is required\n";
+	exit (1);
+}
+
+function handle_pear_error ($res, $reply)
+{
+	if (PEAR::isError($res)) {
+		$cause = "\tStandard Message [".$res->getMessage()."]\n". 
+			"\tUser Information [".$res->getUserInfo()."]\n".
+			"\tDebug Information [".$res->getDebugInfo()."]";
+		throw new GdaException($cause, false);
+	}
+}
+
+echo "\n";
+if ($test_connections) {
+	foreach ($cnc as $dbname => $dbpass) {
+		echo "Connection ".$dbname;
+		try {
+			$mdb2 = MDB2::connect($dsn[$dbname]);
+			handle_pear_error ($mdb2, $reply);
+			echo " ==> OK\n";
+		}
+		catch (GdaException $e) {
+			echo " ==> FAILED:\n".$e->getMessage()."\n";
+		}
+	}
+}
+else {
+	echo "Connections are not tested, set \$test_connections to true to enable connection testing\n";
+}
+?>
diff --git a/providers/web/php/gda-utils.php b/providers/web/php/gda-utils.php
new file mode 100644
index 0000000..212b27d
--- /dev/null
+++ b/providers/web/php/gda-utils.php
@@ -0,0 +1,107 @@
+<?php
+
+function get_command_filename ($session_id)
+{
+	//return session_save_path ()."/command.gda";
+	return session_save_path ()."/GDA".$session_id."Cpipe";
+}
+
+function get_reply_filename ($session_id)
+{
+	//return session_save_path ()."/reply.gda";
+	return session_save_path ()."/GDA".$session_id."Rpipe";
+}
+
+function try_include ($file, $debug=false)
+{
+	$array = explode (":", get_include_path ());
+	foreach ($array as $index => $path) {
+		if (file_exists ($path.DIRECTORY_SEPARATOR.$file)) {
+			include_once ($path.DIRECTORY_SEPARATOR.$file);
+			if ($debug)
+				echo "Using: ".$path.DIRECTORY_SEPARATOR.$file."\n";
+			return true;
+		}
+	}
+	return false;
+}
+
+function gda_add_hash ($key, $text)
+{
+	if ($key == "")
+		return "NOHASH\n".$text;
+	else
+		return hmac ($key, $text)."\n".$text;
+}
+
+// Create an md5 HMAC
+function hmac ($key, $data)
+{
+    // RFC 2104 HMAC implementation for php.
+    // Creates an md5 HMAC.
+    // Eliminates the need to install mhash to compute a HMAC
+    // Hacked by Lance Rushing
+
+    $b = 64; // byte length for md5
+    if (strlen($key) > $b) {
+        $key = pack("H*",md5($key));
+    }
+    $key  = str_pad($key, $b, chr(0x00));
+    $ipad = str_pad('', $b, chr(0x36));
+    $opad = str_pad('', $b, chr(0x5c));
+    $k_ipad = $key ^ $ipad ;
+    $k_opad = $key ^ $opad;
+
+    return md5($k_opad  . pack("H*",md5($k_ipad . $data)));
+}
+
+// Generate a random character string
+function rand_str($length = 32, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890')
+{
+    // Length of character list
+    $chars_length = (strlen($chars) - 1);
+
+    // Start our string
+    $string = $chars{rand(0, $chars_length)};
+   
+    // Generate random string
+    for ($i = 1; $i < $length; $i = strlen($string))
+    {
+        // Grab a random character from our list
+        $r = $chars{rand(0, $chars_length)};
+       
+        // Make sure the same two characters don't appear next to each other
+        if ($r != $string{$i - 1}) $string .=  $r;
+    }
+   
+    // Return the string
+    return $string;
+}
+
+function mdb2_type_to_gtype ($mdb2type)
+{
+	switch ($mdb2type) {
+	default:
+	case "text":
+	case "clob":
+		return "gchararray";
+	case "integer":
+		return "gint";
+	case "boolean":
+		return "boolean";
+	case "decimal":
+		return "GdaNumerical";
+	case "float":
+		return "gdouble";
+	case "date":
+		return "GDate";
+	case "time":
+		return "GdaTime";
+	case "timestamp":
+		return "GdaTimestamp";
+	case "blob":
+		return "GdaBinary";
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/providers/web/php/gda-worker.php b/providers/web/php/gda-worker.php
new file mode 100644
index 0000000..f4de601
--- /dev/null
+++ b/providers/web/php/gda-worker.php
@@ -0,0 +1,654 @@
+<?php
+/* Configure session cache */
+session_cache_limiter('nocache');
+session_start();
+
+header('Content-type: text/plain; charset=UTF-8');
+
+include_once "gda-utils.php";
+include_once "gda-exception.php";
+include_once "gda-meta.php";
+include_once "gda-config.php";
+
+$debug = false;
+
+if (! try_include ("MDB2.php")) {
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$node = $reply->addChild ("status", "CLOSED");
+	$node->addAttribute ("error", "The PEAR MDB2 extension is required");
+	echo gda_add_hash ($init_shared, $reply->asXml());
+	session_destroy ();
+	exit (1);
+}
+
+if (! extension_loaded ("SimpleXML")) {
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$node = $reply->addChild ("status", "CLOSED");
+	$node->addAttribute ("error", "The SimpleXML extension is required");
+	echo $reply->asXml();
+	exit (1);
+}
+
+register_shutdown_function ('shutdown');
+
+$cmdfile = get_command_filename (session_id ());
+$replyfile = get_reply_filename (session_id ());
+
+if (isset ($_SESSION ["cncclosed"]) && $_SESSION ["cncclosed"]) {
+	/* just wait a little for the worker to terminate and return:
+	 * the worker script has been re-spawned but should not try to read
+	 * any further command 
+	 */
+	usleep (50000);
+}
+else {
+	if (isset ($_SESSION ["counter"]))
+		$_SESSION ["counter"] ++;
+	else
+		$_SESSION ["counter"] = 1;
+	gda_worker_main ();
+}
+
+function shutdown ()
+{
+	session_write_close ();
+	flush ();
+	ob_flush();
+}
+
+function send_reply ($retval)
+{
+	global $cmdfile;
+	global $replyfile;
+	global $debug;
+
+	if ($debug) {
+		echo "{{{".$retval."}}}\n";
+		flush ();
+		ob_flush();
+	}
+
+	if (!file_exists ($replyfile))
+		throw new GdaException ("Bad setup", true);
+
+	$datafile = tempnam (session_save_path (), "GDAReply");
+	if (file_put_contents ($datafile, $retval) == false)
+		throw new GdaException ("Can't send reply ", true);
+	
+	$file = fopen ($replyfile, "w");
+	if (fwrite ($file, $datafile) == false)
+		throw new Exception ("Can't send command");
+	fclose ($file);
+}
+
+/*
+ * Main function, leaves only when a database error occurs of when
+ * asked to.
+ */
+function gda_worker_main ()
+{
+	global $init_shared;
+	global $cmdfile;
+	global $replyfile;
+
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$reply->addChild ('session', htmlspecialchars(SID));
+	$reply->addChild ('status', "OK");
+	$reply->addChild ("counter", $_SESSION ["counter"]);
+	echo gda_add_hash ($init_shared, $reply->asXml());
+	flush (); ob_flush();
+
+	/* set max execution time to 10s */
+	set_time_limit (10);
+
+	while (1) {
+		$doquit = false;
+		try {
+			if (!file_exists ($cmdfile))
+				throw new GdaException ("Bad setup", true);
+			//echo "Reading from fifo...\n"; flush (); ob_flush();
+			$file = fopen ($cmdfile, "r+");
+			stream_set_blocking ($file, false);
+			while (1) {
+				$readers = array ($file);
+				if (stream_select ($readers, $writers=null, $except=null, 5, 0) < 1) {
+					/* More waiting... */
+					echo chr(0); flush (); ob_flush(); // send something to the client to test connection
+					if (connection_status() != 0) {
+						fclose ($file);
+						exit (0);
+						break;
+					}
+					continue;
+				}
+				else {
+					/* data available to read */
+					break;
+				}
+			}
+			$datafile = fread ($file, 8192);
+			fclose ($file);
+			
+			$text = file_get_contents ($datafile);
+			@unlink ($datafile);
+			if ($text == false)
+				throw new GdaException('Could not read command', false);
+
+			//echo "COMMAND [$datafile]\n"; flush (); ob_flush();
+
+			$array = split ("\n", $text, 2);
+			if (sizeof ($array) != 2)
+				throw new GdaException('Missing message hash', true);
+			if ($array[0][0] == "<")
+				throw new GdaException('Bad message hash', true);
+			if ($array[0] != "NOHASH" ||
+			    isset ($_SESSION['key'])) {
+				$hash = hmac ($_SESSION['key'], $array[1]);
+				if ($array[0] != $hash)
+					throw new GdaException('Bad message hash', true);
+			}
+			if ($array[1][0] != "<")
+				throw new GdaException('Bad message format', true);
+			
+			$xml = simplexml_load_string ($array[1]);
+			$retval = gda_handle_xml ($xml, $doquit);
+
+			/* send reply */
+			try {
+				send_reply ($retval);
+			}
+			catch (Exception $e) {
+				/* will be caught by the listening thread */
+				echo $retval;
+			}
+		}
+		catch (GdaException $e) {
+			$reply = new SimpleXMLElement("<reply></reply>");
+			if (! $e->cnc_closed) {
+				$chal = rand_str ();
+				$_SESSION['challenge'] = $chal;
+				$reply->addChild ('challenge', $chal);
+				
+				if (isset ($debug) && $debug)
+					$reply->addChild ('session', htmlspecialchars(SID));
+
+				$node = $reply->addChild ('status', "ERROR");
+			}
+			else {
+				$_SESSION ["cncclosed"] = true;
+				$doquit = true;
+				$node = $reply->addChild ('status', "CLOSED");
+			}
+			$node->addAttribute ("error", $e->getMessage());
+
+			$reply->addChild ("counter", $_SESSION ["counter"]);
+
+			if (isset ($_SESSION['key']))
+				$retval = gda_add_hash ($_SESSION['key'], $reply->asXml());
+			else
+				$retval = gda_add_hash ("", $reply->asXml());
+
+			try {
+				send_reply ($retval);
+			}
+			catch (Exception $e) {
+				/* will be caught by the listening thread */
+				echo $retval;
+			}
+		}
+		//echo "REPLY [$retval]\n";
+		flush ();
+		ob_flush();
+		if ($doquit)
+			break;
+	}
+}
+
+function handle_pear_error ($res, $reply)
+{
+	if (PEAR::isError($res)) {
+		global $debug;
+		if (isset ($debug) && $debug) {
+			$cause = "Standard Message [".$res->getMessage()."] ". 
+				"User Information  [".$res->getUserInfo()."] ".
+				"Debug Information [".$res->getDebugInfo()."]";
+			$reply->addChild ("debug", $cause);
+		}
+		$tmp = $res->getMessage ();
+		$tmp = str_replace ("MDB2 Error: ", "", $tmp);
+		throw new GdaException ($tmp, false);
+	}
+}
+
+function determine_db_type ($mdb2)
+{
+	if (strstr (get_class ($mdb2), "pgsql"))
+		return "PostgreSQL";
+	else if (strstr (get_class ($mdb2), "mysql"))
+		return "MySQL";
+	else if (strstr (get_class ($mdb2), "sqlite"))
+		return "SQLite";
+	else if (strstr (get_class ($mdb2), "oci"))
+		return "Oracle";
+
+	return false;
+}
+
+/* MDB2 connection, global so it can persist multiple gda_handle_xml() calls */
+$mdb2 = null;
+$prepared_statements = array();
+
+function real_connect ($dsn, $reply)
+{
+	global $mdb2;
+	$options = array(
+			 'debug'       => 2,
+			 'portability' => MDB2_PORTABILITY_DELETE_COUNT | MDB2_PORTABILITY_EMPTY_TO_NULL |
+			 MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES | MDB2_PORTABILITY_NUMROWS |
+			 MDB2_PORTABILITY_RTRIM,
+			 );
+	$mdb2 = &MDB2::connect ($dsn."?new_link=true", $options);
+	handle_pear_error ($mdb2, $reply);
+
+	/* load extra modules */
+	$mdb2->loadModule('Reverse', null, true);
+	$mdb2->loadModule('Manager', null, true);
+	$mdb2->loadModule('Datatype', null, true);
+
+	return $mdb2;
+}
+
+function gda_handle_xml ($xml, &$doquit)
+{
+	//var_dump($xml);
+	$reply = new SimpleXMLElement("<reply></reply>");
+	$apply_md5_to_key = false;
+	$doquit = false;
+		
+	global $debug;
+	global $init_shared;
+	global $cnc;
+	global $dsn;
+	global $mdb2;
+	$token = "-";
+	$status = "";
+
+	if ($xml->getName() != "request")
+		throw new GdaException('Bad XML input', true);
+	foreach ($xml->children() as $child) {
+		if ($child->getName() == "token") {
+			$token = $child[0];
+			if ($_SESSION['key'] == $init_shared) {
+				/* change $_SESSION['key'] depending on the configured connections */
+				foreach ($cnc as $dbname => $dbpass) {
+					$computed_token = hmac ($dbname."/AND/".$dbpass, $_SESSION['challenge']);
+					if ($computed_token == $token) {
+						$_SESSION['key'] = $dbname."/AND/".$dbpass;
+						$_SESSION['dsn'] = $dsn[$dbname];
+						$cncfound = true;
+						break;
+					}
+				}
+				if (!isset ($cncfound))
+					throw new GdaException('Connection not found', true);
+			}
+			else {
+				$computed_token = hmac ($_SESSION['key'], $_SESSION['challenge']);
+				if ($computed_token != $token)
+					throw new GdaException('Authentication error', true);
+			}
+		}
+		else if ($child->getName() == "cmd") {
+			switch ($child[0]) {
+			case "HELLO":
+				/* just output a new challenge */
+				$_SESSION['key'] = $init_shared;
+				$reply->addChild ('session', htmlspecialchars(SID));
+				$status = "OK";
+				break;
+
+			case "CONNECT":
+				if (!isset ($_SESSION['key']))
+					throw new GdaException('Protocol error', true);
+				if ($token == "-")
+					throw new GdaException('Not authenticated', true);
+					
+				/* actually open the connection */
+				$mdb2 = real_connect ($_SESSION['dsn'], $reply);			       
+
+				$apply_md5_to_key = true;
+				$dbtype = determine_db_type ($mdb2);
+				if ($dbtype)
+					$reply->addChild ('servertype', $dbtype);
+				$status = "OK";
+				break;
+
+			case "BYE":
+				if (!isset ($_SESSION['key']))
+					throw new GdaException('Protocol error', true);
+				if ($token == "-")
+					throw new GdaException('Not authenticated', true);
+
+				/* actually close the connection */
+				if (isset ($mdb2))
+					$mdb2->disconnect();
+
+				$status = "CLOSED";
+				$doquit = true;
+				$_SESSION ["cncclosed"] = true;
+				break;
+
+			case "PREPARE":
+				if (!isset ($_SESSION['key']))
+					throw new GdaException('Protocol error', true);
+				if ($token == "-")
+					throw new GdaException('Not authenticated', true);
+				$argsnode = null;
+				$type_is_result = false;
+				foreach ($child->children() as $sqlnode) {
+					if ($sqlnode->getName() == "sql") {
+						$sql = $sqlnode[0];
+						foreach($sqlnode->attributes() as $a => $b) {
+							if (($a == "type") && ($b == "SELECT")) {
+								$type_is_result = true;
+								break;
+							}
+						}
+					}
+					else if ($sqlnode->getName() == "arguments") {
+						$argsnode = $sqlnode;
+					}
+				}
+				if (! isset ($sql))
+					throw new GdaException('Bad XML input', true);
+
+				/* actually execute SQL in $sql */
+				if (!isset ($mdb2))
+					$mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+				do_prepare ($reply, $mdb2, $sql, $type_is_result, $argsnode);
+				$status = "OK";
+				break;
+			case "UNPREPARE":
+				if (!isset ($_SESSION['key']))
+					throw new GdaException('Protocol error', true);
+				if ($token == "-")
+					throw new GdaException('Not authenticated', true);
+				$pstmt_hash = null;
+				foreach ($child->children() as $sqlnode) {
+					if ($sqlnode->getName() == "preparehash") {
+						$pstmt_hash = $sqlnode[0];
+						break;
+					}
+				}
+				/* actually execute SQL in $sql */
+				if (isset ($mdb2) && isset ($pstmt_hash))
+					do_unprepare ($reply, $mdb2, $pstmt_hash);
+				$status = "OK";
+				break;
+			case "EXEC":
+				if (!isset ($_SESSION['key']))
+					throw new GdaException('Protocol error', true);
+				if ($token == "-")
+					throw new GdaException('Not authenticated', true);
+				$argsnode = null;
+				$pstmt_hash = null;
+				$type_is_result = false;
+
+				foreach ($child->children() as $sqlnode) {
+					if ($sqlnode->getName() == "sql") {
+						$sql = $sqlnode[0];
+						foreach ($sqlnode->attributes() as $a => $b) {
+							if (($a == "type") && ($b == "SELECT")) {
+								$type_is_result = true;
+								break;
+							}
+						}
+					}
+					else if ($sqlnode->getName() == "arguments")
+						$argsnode = $sqlnode;
+					else if ($sqlnode->getName() == "preparehash")
+						$pstmt_hash = $sqlnode[0];
+				}
+				if (! isset ($sql))
+					throw new GdaException('Bad XML input', true);
+					
+				/* actually execute SQL in $sql */
+				if (!isset ($mdb2))
+					$mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+				do_exec ($reply, $mdb2, $pstmt_hash, $sql, $type_is_result, $argsnode);
+				$status = "OK";
+				break;
+			case 'META':
+				if (!isset ($_SESSION['key']))
+					throw new GdaException('Protocol error', true);
+				if ($token == "-")
+					throw new GdaException('Not authenticated', true);
+
+				$args = array ();
+				foreach ($child->children() as $sqlnode) {
+					if ($sqlnode->getName() == "arg") {
+						foreach ($sqlnode->attributes() as $a => $b) {
+							if ($a == "name") {
+								$args[$b] = $sqlnode[0];
+								break;
+							}
+						}
+					}
+				}
+				foreach ($child->attributes() as $a => $b) {
+					if ($a == "type") {
+						$type = $b;
+						break;
+					}
+				}
+				if (! isset ($type))
+					throw new GdaException('Bad XML input', true);
+
+				if (!isset ($mdb2))
+					$mdb2 = real_connect ($_SESSION['dsn'], $reply);
+
+				do_meta ($reply, $mdb2, $type, $args);
+				$status = "OK";
+				break;
+			default:
+				throw new GdaException('Unknown command '.$child[0], false);
+			}
+		}
+		else {
+			throw new GdaException('Bad XML input', true);
+		}
+	}
+		
+	if ($status != "CLOSED") {
+		$chal = rand_str ();
+		$_SESSION['challenge'] = $chal;
+		$reply->addChild ('challenge', $chal);
+	}
+
+	if ($status != "")
+		$reply->addChild ('status', $status);
+	$reply->addChild ("counter", $_SESSION ["counter"]);
+	if (isset ($debug) && $debug && $status != "CLOSED") {
+		$reply->addChild ('session', htmlspecialchars(SID));
+	}
+		
+	/* output reply */
+	$retval = gda_add_hash ($_SESSION['key'], $reply->asXml());
+	if ($apply_md5_to_key) {
+		/* modify $key so it's possible for the client to forget about password */
+		$_SESSION['key'] = md5 ($_SESSION['key']);
+	}
+	return $retval;
+}
+
+function do_prepare ($reply, &$mdb2, $sql, $type_is_result, $argsnode)
+{
+	/* prepare argument types */
+	if ($argsnode) {
+		$argtypes = array();
+		foreach ($argsnode->children() as $node) {
+			if ($node->getName() == "arg") {
+				foreach($node->attributes() as $a => $b) {
+					if ($a == "type") {
+						$argtypes[] = $b;
+						break;
+					}
+				}
+			}
+		}
+	}
+	else
+		$argtypes = null;
+	if ($type_is_result)
+		$res = $mdb2->prepare((string) $sql, $argtypes, MDB2_PREPARE_RESULT);
+	else
+		$res = $mdb2->prepare((string) $sql, $argtypes, MDB2_PREPARE_MANIP);
+	handle_pear_error ($res, $reply);
+	
+	global $prepared_statements;
+	$pstmt_hash = md5 ((string) $sql);
+	$prepared_statements [$pstmt_hash] = $res;
+	$reply->addChild ('preparehash', $pstmt_hash);
+	return $res;
+}
+
+function do_unprepare ($reply, &$mdb2, $pstmt_hash)
+{
+	/* get prepared statement */
+	global $prepared_statements;
+	if (isset ($pstmt_hash))
+		$prep = $prepared_statements [(string) $pstmt_hash];
+	if (isset ($prep)) {
+		$prep->free();
+		unset ($prepared_statements [(string) $pstmt_hash]);
+	}
+}
+
+function do_exec ($reply, &$mdb2, $pstmt_hash, $sql, $type_is_result, $argsnode)
+{
+	/* get prepared statement */
+	global $prepared_statements;
+	if (isset ($pstmt_hash))
+		$prep = $prepared_statements [(string) $pstmt_hash];
+
+	if (!isset ($prep))
+		$prep = do_prepare ($reply, $mdb2, (string) $sql, $type_is_result, $argsnode);
+	
+	/* handle arguments */
+	if ($argsnode) {
+		$args = array();
+		foreach ($argsnode->children() as $node) {
+			if ($node->getName() == "arg") {
+				$args[] = $node[0];
+			}
+		}
+	}
+	else
+		$args = null;
+
+	/* actual execution */
+	$res = $prep->execute ($args);
+	handle_pear_error ($res, $reply);
+
+	if ($type_is_result) {
+		$node = $reply->addChild ("gda_array", null);
+		$ncols = $res->numCols();
+
+		/* compute field type */
+		$tableinfo = $mdb2->tableInfo ($res, NULL);
+		if (! PEAR::isError ($tableinfo)) {
+			//var_dump($tableinfo);
+			$gtypes = array();
+			$dbtypes = array();
+			for ($i = 0; $i < $ncols; $i++) {
+				$gtypes [$i] = mdb2_type_to_gtype ($tableinfo[$i]['mdb2type']);
+				$dbtypes [$i] = $tableinfo[$i]['type'];
+			}
+		}
+
+		$colnames = $res->getColumnNames ();
+		if (PEAR::isError ($colnames))
+			unset ($colnames);
+		for ($i = 0; $i < $ncols; $i++) {
+			$field = $node->addChild ("gda_array_field", null);
+			$field->addAttribute ("id", "FI".$i);
+			if (isset ($colnames)) {
+				foreach ($colnames as $name => $pos) {
+					if ($pos == $i) {
+						$field->addAttribute ("name", $name);
+						break;
+					}
+				}
+			}
+			if (isset ($gtypes))
+				$field->addAttribute ("gdatype", $gtypes[$i]);
+			else
+				$field->addAttribute ("gdatype", "string");
+			if (isset ($dbtypes) && $dbtypes[$i])
+				$field->addAttribute ("dbtype", $dbtypes[$i]);
+			$field->addAttribute ("nullok", "TRUE");
+		}
+		
+		$data = $node->addChild ("gda_array_data", null);
+		while (($row = $res->fetchRow())) {
+			// MDB2's default fetchmode is MDB2_FETCHMODE_ORDERED
+			$xmlrow = $data->addChild ("gda_array_row", null);
+			for ($i = 0; $i < $ncols; $i++) {
+				$val = $xmlrow->addChild ("gda_value");
+				$val[0] = str_replace ("&", "&amp;", $row[$i]);
+			}
+		}
+	}
+	else {
+		$reply->addChild ("impacted_rows", (string) $res);
+	}
+}
+
+function do_meta ($reply, &$mdb2, $type, $args)
+{
+	switch ($type) {
+	case "info":
+		do_meta_info ($reply, &$mdb2);
+		break;
+	case "btypes":
+		do_meta_btypes ($reply, &$mdb2);
+		break;
+	case "schemas":
+		do_meta_schemas ($reply, &$mdb2, $args["schema_name"]);
+		break;
+	case "tables":
+		do_meta_tables ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+		break;
+	case "views":
+		do_meta_views ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+		break;
+	case "columns":
+		do_meta_columns ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+		break;
+	case "constraints_tab":
+		do_meta_constraints_tab ($reply, &$mdb2,
+					 $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+		break;
+	case "constraints_ref":
+		do_meta_constraints_ref ($reply, &$mdb2,
+					 $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+		break;
+	case "key_columns":
+		do_meta_key_columns ($reply, &$mdb2,
+				     $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+		break;
+	case "check_columns":
+		do_meta_check_columns ($reply, &$mdb2,
+				       $args["table_schema"], $args["table_name"], $args["constraint_name"]);
+		break;
+	case "triggers":
+		do_meta_triggers ($reply, &$mdb2, $args["table_schema"], $args["table_name"]);
+		break;
+	default:
+		throw new GdaException('Unknown META command '.$type, false);
+	}
+}
+
+?>
diff --git a/providers/web/protocol.dia b/providers/web/protocol.dia
new file mode 100644
index 0000000..c83a252
Binary files /dev/null and b/providers/web/protocol.dia differ
diff --git a/providers/web/web_specs_auth.xml.in b/providers/web/web_specs_auth.xml.in
new file mode 100644
index 0000000..cf7106f
--- /dev/null
+++ b/providers/web/web_specs_auth.xml.in
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<data-set-spec>
+  <parameters>
+    <parameter id="PASSWORD" _name="Password" _descr="Connection password as defined on the web server" gdatype="gchararray" nullok="FALSE"/>
+  </parameters>
+</data-set-spec>
diff --git a/providers/web/web_specs_dsn.xml.in b/providers/web/web_specs_dsn.xml.in
new file mode 100644
index 0000000..0578478
--- /dev/null
+++ b/providers/web/web_specs_dsn.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<data-set-spec>
+  <parameters>
+    <parameter id="HOST" _name="Web server name" _descr="The name of the web server which proxies connections" gdatype="gchararray" nullok="FALSE"/>
+    <parameter id="PORT" _name="Port" _descr="Web server port (leave this field empty for default)" gdatype="gint"/>
+    <parameter id="PATH" _name="Path to PHP script" _descr="The path of the script to use if service is not at the web server's root" gdatype="gchararray"/>
+    <parameter id="SECRET" _name="Server secret" _descr="Server secret" gdatype="gchararray" nullok="FALSE"/>
+    <parameter id="DB_NAME" _name="Database name" _descr="The name of a database to use" gdatype="gchararray" nullok="FALSE"/>
+    <parameter id="USE_SSL" _name="Require SSL" _descr="Whether or not to use SSL to establish the connection" gdatype="gboolean"/>
+  </parameters>
+</data-set-spec>
diff --git a/tools/gda-sql.c b/tools/gda-sql.c
index 5c03a08..7b7b5e2 100644
--- a/tools/gda-sql.c
+++ b/tools/gda-sql.c
@@ -38,6 +38,7 @@
 
 #ifndef G_OS_WIN32
 #include <signal.h>
+typedef void (*sighandler_t)(int);
 #include <pwd.h>
 #else
 #include <stdlib.h>
@@ -1243,6 +1244,7 @@ typedef struct {
 static gpointer thread_start_update_meta_store (MetaUpdateData *data);
 static void thread_ok_cb_update_meta_store (GdaThreader *threader, guint job, MetaUpdateData *data);
 static void thread_cancelled_cb_update_meta_store (GdaThreader *threader, guint job, MetaUpdateData *data);
+static void conn_closed_cb (GdaConnection *cnc, ConnectionSetting *cs);
 
 /*
  * Open a connection
@@ -1453,6 +1455,11 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
 		g_free (dict_file_name);
 	}
 
+	if (cs) {
+		g_signal_connect (cs->cnc, "conn-closed",
+				  G_CALLBACK (conn_closed_cb), cs);
+	}
+
 	return cs;
 }
 
@@ -1514,8 +1521,11 @@ static void
 connection_settings_free (ConnectionSetting *cs)
 {
 	g_free (cs->name);
-	if (cs->cnc)
+	if (cs->cnc) {
+		g_signal_handlers_disconnect_by_func (cs->cnc,
+						      G_CALLBACK (conn_closed_cb), cs);
 		g_object_unref (cs->cnc);
+	}
 	if (cs->parser)
 		g_object_unref (cs->parser);
 	if (cs->query_buffer)
@@ -1641,7 +1651,7 @@ output_string (const gchar *str)
 	gboolean append_nl = FALSE;
 	gint length;
 	static gint force_no_pager = -1;
-	
+
 	if (!str)
 		return;
 
@@ -1666,13 +1676,15 @@ output_string (const gchar *str)
 		/* use pager */
 		FILE *pipe;
 		const char *pager;
-		
+#ifndef G_OS_WIN32
+		sighandler_t phandler;
+#endif
 		pager = getenv ("PAGER");
 		if (!pager)
 			pager = "more";
 		pipe = popen (pager, "w");
 #ifndef G_OS_WIN32
-		signal (SIGPIPE, SIG_IGN);
+		phandler = signal (SIGPIPE, SIG_IGN);
 #endif
 		if (append_nl)
 			g_fprintf (pipe, "%s\n", str);
@@ -1680,7 +1692,7 @@ output_string (const gchar *str)
 			g_fprintf (pipe, "%s", str);
 		pclose (pipe);
 #ifndef G_OS_WIN32
-		signal(SIGPIPE, SIG_DFL);
+		signal (SIGPIPE, phandler);
 #endif
 	}
 	else {
@@ -3010,12 +3022,18 @@ extra_command_manage_cnc (SqlConsole *console, GdaConnection *cnc, const gchar *
 	}
 }
 
+static void
+conn_closed_cb (GdaConnection *cnc, ConnectionSetting *cs)
+{
+	extra_command_close_cnc (NULL, cnc, NULL, NULL, NULL);
+}
+
 static 
 GdaInternalCommandResult *
 extra_command_close_cnc (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	ConnectionSetting *cs = NULL;
-	if (args[0] && *args[0]) {
+	if (args && args[0] && *args[0]) {
 		cs = find_connection_from_name (args[0]);
 		if (!cs) {
 			g_set_error (error, 0, 0,



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