libgda r3158 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite providers/postgres providers/skel-implementation/capi



Author: vivien
Date: Tue Jun  3 19:26:50 2008
New Revision: 3158
URL: http://svn.gnome.org/viewvc/libgda?rev=3158&view=rev

Log:
2008-06-03  Vivien Malerba <malerba gnome-db org>

	* configure.in: use pg_config if possible to get PostgreSQL's
	compilation arguments
	* providers/postgres/gda-postgres-meta.c: check that the server's
	version is at least 8.2 before using pg_is_other_temp_schema() in SQL
	* libgda/Makefile.am:
	* libgda/gda-xa-transaction.[ch]: new distributed transaction manager
	* libgda/gda-server-provider.[ch]:
	* libgda/libgda.h.in:
	* libgda/sqlite/gda-sqlite-provider.c:
	* providers/postgres/gda-postgres-provider.c:
	* providers/skel-implementation/capi/gda-capi-provider.c: support for
	the new distributed transaction manager object
	* doc/C: doc updates


Added:
   trunk/doc/C/tmpl/gda-xa-transaction.sgml
   trunk/libgda/gda-xa-transaction.c
   trunk/libgda/gda-xa-transaction.h
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/doc/C/libgda-4.0-docs.sgml
   trunk/doc/C/libgda-4.0-sections.txt
   trunk/doc/C/tmpl/gda-server-provider.sgml
   trunk/doc/C/tmpl/gda-transaction-status.sgml
   trunk/libgda/Makefile.am
   trunk/libgda/gda-server-provider.c
   trunk/libgda/gda-server-provider.h
   trunk/libgda/libgda.h.in
   trunk/libgda/sqlite/gda-sqlite-provider.c
   trunk/providers/postgres/gda-postgres-meta.c
   trunk/providers/postgres/gda-postgres-provider.c
   trunk/providers/skel-implementation/capi/gda-capi-provider.c

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Tue Jun  3 19:26:50 2008
@@ -551,27 +551,39 @@
 	then
 		AC_MSG_WARN(Postgres backend not used)
 	else
-		if test -f ${postgresdir}/include/libpq-fe.h
+		if test -f ${postgresdir}/bin/pg_config
 		then
-			POSTGRES_CFLAGS=-I${postgresdir}/include
-		elif test -f ${postgresdir}/include/pgsql/libpq-fe.h
-		then
-			POSTGRES_CFLAGS=-I${postgresdir}/include/pgsql
-		elif test -f ${postgresdir}/include/postgresql/libpq-fe.h
-		then
-			POSTGRES_CFLAGS=-I${postgresdir}/include/postgresql
-                elif test -f ${postgresdir}/include/postgresql/8.0/libpq-fe.h
-		then
-			POSTGRES_CFLAGS=-I${postgresdir}/include/8.0/postgresql
-                elif test -f ${postgresdir}/include/postgresql/7.4/libpq-fe.h
-		then
-			POSTGRES_CFLAGS=-I${postgresdir}/include/7.4/postgresql
+			PG_CONFIG=${postgresdir}/bin/pg_config
+			POSTGRES_CFLAGS=-I`${PG_CONFIG} --includedir`
+			POSTGRES_LIBS="-L`${PG_CONFIG} --libdir` -lpq"
+			AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
 		else
-			AC_MSG_WARN(Postgres include files not found, backend not used)
-			postgresdir=""
+			if test -f ${postgresdir}/include/libpq-fe.h
+			then
+				POSTGRES_CFLAGS=-I${postgresdir}/include
+				AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
+			elif test -f ${postgresdir}/include/pgsql/libpq-fe.h
+			then
+				POSTGRES_CFLAGS=-I${postgresdir}/include/pgsql
+				AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
+			elif test -f ${postgresdir}/include/postgresql/libpq-fe.h
+			then
+				POSTGRES_CFLAGS=-I${postgresdir}/include/postgresql
+				AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
+                	elif test -f ${postgresdir}/include/postgresql/8.2/libpq-fe.h
+			then
+				POSTGRES_CFLAGS=-I${postgresdir}/include/8.2/postgresql
+				AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
+                	elif test -f ${postgresdir}/include/postgresql/8.3/libpq-fe.h
+			then
+				POSTGRES_CFLAGS=-I${postgresdir}/include/8.3/postgresql
+				AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
+			else
+				AC_MSG_WARN(Postgres include files not found, backend not used)
+				postgresdir=""
+			fi
+			POSTGRES_LIBS="-L${postgresdir}/lib -lpq"
 		fi
-		POSTGRES_LIBS="-L${postgresdir}/lib -lpq"
-		AC_DEFINE(HAVE_POSTGRES, 1, [Have PostgreSQL])
 	fi
 fi
 

Modified: trunk/doc/C/libgda-4.0-docs.sgml
==============================================================================
--- trunk/doc/C/libgda-4.0-docs.sgml	(original)
+++ trunk/doc/C/libgda-4.0-docs.sgml	Tue Jun  3 19:26:50 2008
@@ -70,6 +70,7 @@
 <!ENTITY libgda-serverop-intro SYSTEM "server-operation.xml">
 <!ENTITY libgda-virtual SYSTEM "virtual.xml">
 <!ENTITY libgda-virtual-notice SYSTEM "virtual-notice.xml">
+<!ENTITY libgda-xa-transaction SYSTEM "xml/gda-xa-transaction.xml">
 <!ENTITY libgda-transaction-status SYSTEM "xml/gda-transaction-status.xml">
 <!ENTITY libgda-util SYSTEM "xml/gda-util.xml">
 <!ENTITY libgda-GValue SYSTEM "xml/gda-value.xml">
@@ -503,6 +504,7 @@
       &libgda-GdaSqlStatement;
       &libgda-GdaConnection-event;
       &libgda-transaction-status;
+      &libgda-xa-transaction;
       &GdaStoreMetaType;
     </chapter>
 

Modified: trunk/doc/C/libgda-4.0-sections.txt
==============================================================================
--- trunk/doc/C/libgda-4.0-sections.txt	(original)
+++ trunk/doc/C/libgda-4.0-sections.txt	Tue Jun  3 19:26:50 2008
@@ -662,15 +662,38 @@
 </SECTION>
 
 <SECTION>
+<FILE>gda-xa-transaction</FILE>
+<TITLE>GdaXaTransaction</TITLE>
+GdaXaTransaction
+GdaXaTransactionError
+GdaXaTransactionId
+gda_xa_transaction_new
+gda_xa_transaction_register_connection
+gda_xa_transaction_unregister_connection
+gda_xa_transaction_begin
+gda_xa_transaction_commit
+gda_xa_transaction_rollback
+gda_xa_transaction_commit_recovered
+<SUBSECTION>
+gda_xa_transaction_id_to_string
+gda_xa_transaction_string_to_id
+<SUBSECTION Standard>
+GDA_IS_XA_TRANSACTION
+GDA_IS_XA_TRANSACTION_CLASS
+GDA_XA_TRANSACTION
+GDA_XA_TRANSACTION_CLASS
+GDA_TYPE_XA_TRANSACTION
+gda_xa_transaction_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gda-transaction-status</FILE>
 <TITLE>GdaTransactionStatus</TITLE>
 GdaTransactionStatus
-_GdaTransactionStatus
 GdaTransactionIsolation
 GdaTransactionStatusEvent
 GdaTransactionStatusEventType
 GdaTransactionStatusState
-gda_transaction_status_new
 <SUBSECTION Standard>
 GDA_IS_TRANSACTION_STATUS
 GDA_IS_TRANSACTION_STATUS_CLASS

Modified: trunk/doc/C/tmpl/gda-server-provider.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-server-provider.sgml	(original)
+++ trunk/doc/C/tmpl/gda-server-provider.sgml	Tue Jun  3 19:26:50 2008
@@ -63,6 +63,7 @@
 @cancel: 
 @create_connection: 
 @meta_funcs: 
+ xa_funcs: 
 @_gda_reserved1: 
 @_gda_reserved2: 
 @_gda_reserved3: 
@@ -84,6 +85,7 @@
 @GDA_SERVER_PROVIDER_INTERNAL_ERROR: 
 @GDA_SERVER_PROVIDER_BUSY_ERROR: 
 @GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR: 
+ GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR: 
 
 <!-- ##### STRUCT GdaServerProviderMeta ##### -->
 <para>

Modified: trunk/doc/C/tmpl/gda-transaction-status.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-transaction-status.sgml	(original)
+++ trunk/doc/C/tmpl/gda-transaction-status.sgml	Tue Jun  3 19:26:50 2008
@@ -6,16 +6,20 @@
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
-On any connection (as a #GdaConnection object), if the database provider used by the connection
-supports it, transactions may be started, committed or rolledback, or savepoints added, removed or rolledback.
-These operations can be performed using Libgda's API (such as gda_connection_begin_transaction()), or directly
-using some SQL on the connection (usually a "BEGIN;" command). The #GdaTransactionStatus's aim is to 
-make it easy to keep track of all the commands which have been issued on a connection regarding transactions.
+  On any connection (as a #GdaConnection object), if the database provider used by the connection
+  supports it, transactions may be started, committed or rolledback, or savepoints added, removed or rolledback.
+  These operations can be performed using Libgda's API (such as gda_connection_begin_transaction()), or directly
+  using some SQL on the connection (usually a "BEGIN;" command). The #GdaTransactionStatus's aim is to 
+  make it easy to keep track of all the commands which have been issued on a connection regarding transactions.
 </para>
 <para>
-One #GdaTransactionStatus object is automatically attached to a #GdaConnection when a transaction is started, and
-is destroyed when the transaction is finished. A pointer to this object can be fetched using
-gda_connection_get_transaction_status() (beware that it should then not be modified).
+  One #GdaTransactionStatus object is automatically attached to a #GdaConnection when a transaction is started, and
+  is destroyed when the transaction is finished. A pointer to this object can be fetched using
+  gda_connection_get_transaction_status() (beware that it should then not be modified). The end user is not
+  supposed to instantiate #GdaTransactionStatus objects
+</para>
+<para>
+  #GdaTransactionStatus's attributes are directly accessible using the public members of the object.
 </para>
 
 <!-- ##### SECTION See_Also ##### -->
@@ -71,12 +75,3 @@
 @GDA_TRANSACTION_STATUS_STATE_OK: 
 @GDA_TRANSACTION_STATUS_STATE_FAILED: 
 
-<!-- ##### FUNCTION gda_transaction_status_new ##### -->
-<para>
-
-</para>
-
- name: 
- Returns: 
-
-

Added: trunk/doc/C/tmpl/gda-xa-transaction.sgml
==============================================================================
--- (empty file)
+++ trunk/doc/C/tmpl/gda-xa-transaction.sgml	Tue Jun  3 19:26:50 2008
@@ -0,0 +1,152 @@
+<!-- ##### SECTION Title ##### -->
+GdaXaTransaction
+
+<!-- ##### SECTION Short_Description ##### -->
+Distributed transaction manager
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+  The #GdaXaTransaction object acts as a distributed transaction manager: to make sure local transactions on several
+  connections (to possibly different databases and database types) either all succeed or all fail. For more information,
+  see the X/Open CAE document Distributed Transaction Processing: The XA Specification. 
+  This document is published by The Open Group and available at 
+  <ulink url="http://www.opengroup.org/public/pubs/catalog/c193.htm";>http://www.opengroup.org/public/pubs/catalog/c193.htm</ulink>.
+</para>
+<para>
+  The two phases commit protocol is implemented during the execution of a distributed transaction: modifications
+  made on any connection are first <emphasis>prepared</emphasis> (which means that they are store in the database), and
+  if that phase succeeded for all the involved connections, then the <emphasis>commit</emphasis> phase is executed
+  (where all the data previously stored during the <emphasis>prepare</emphasis> phase are actually committed).
+  That second phase may actually fail, but the distributed transaction will still be considered as sucessfull
+  as the data stored during the <emphasis>prepare</emphasis> phase can be committed afterwards.
+</para>
+<para>
+  A distributed transaction involves the following steps:
+  <orderedlist>
+    <listitem><para>Create a #GdaXaTransaction object</para></listitem>
+    <listitem><para>Register the connections which will be part of the distributed transaction with that object
+	using gda_xa_transaction_register_connection()</para></listitem>
+    <listitem><para>Beging the distributed transaction using gda_xa_transaction_begin()</para></listitem>
+    <listitem><para>Work individually on each connection as normally (make modifications)</para></listitem>
+    <listitem><para>Commit the distributed transaction using gda_xa_transaction_commit()</para></listitem>
+    <listitem><para>Discard the #GdaXaTransaction object using g_object_unref()</para></listitem>
+  </orderedlist>
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GdaXaTransaction ##### -->
+<para>
+
+</para>
+
+ object: 
+ priv: 
+
+<!-- ##### ENUM GdaXaTransactionError ##### -->
+<para>
+
+</para>
+
+ GDA_XA_TRANSACTION_ALREADY_REGISTERED_ERROR: 
+ GDA_XA_TRANSACTION_DTP_NOT_SUPPORTED_ERROR: 
+
+<!-- ##### STRUCT GdaXaTransactionId ##### -->
+<para>
+Distributed transaction identification, composed of a global format identifier, a global transaction identifier, and a
+branch qualifier. For any connection participating in the global transactio, the format and global transaction identifier
+should be the same, and the branch qualifier should vary between the connections.
+</para>
+
+ format: Format ID, should be 0 if OSI CCR naming is used, otherwise any positive integer
+ gtrid_length: length of the global transaction ID (1-64)
+ bqual_length: length of the branch qualification (1-64)
+ data: concatenated global transaction ID (bytes 0 to (@gtrid_length -1) included) and branch qualification (bytes @gtrid_length to (@gtrid_length+ bqual_length - 1) included)
+
+<!-- ##### FUNCTION gda_xa_transaction_new ##### -->
+<para>
+
+</para>
+
+ format: 
+ global_transaction_id: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_register_connection ##### -->
+<para>
+
+</para>
+
+ xa_trans: 
+ cnc: 
+ branch: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_unregister_connection ##### -->
+<para>
+
+</para>
+
+ xa_trans: 
+ cnc: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_begin ##### -->
+<para>
+
+</para>
+
+ xa_trans: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_commit ##### -->
+<para>
+
+</para>
+
+ xa_trans: 
+ cnc_to_recover: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_commit_recovered ##### -->
+<para>
+
+</para>
+
+ xa_trans: 
+ cnc_to_recover: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_id_to_string ##### -->
+<para>
+
+</para>
+
+ xid: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_xa_transaction_string_to_id ##### -->
+<para>
+
+</para>
+
+ str: 
+ Returns: 
+
+

Modified: trunk/libgda/Makefile.am
==============================================================================
--- trunk/libgda/Makefile.am	(original)
+++ trunk/libgda/Makefile.am	Tue Jun  3 19:26:50 2008
@@ -69,6 +69,7 @@
 	gda-transaction-status-private.h \
 	gda-util.h \
 	gda-value.h \
+	gda-xa-transaction.h \
 	libgda.h 
 
 libgda_sources =  \
@@ -116,6 +117,7 @@
 	gda-transaction-status.c \
 	gda-util.c \
 	gda-value.c \
+	gda-xa-transaction.c \
 	global.h \
 	md5.h \
 	md5c.c

Modified: trunk/libgda/gda-server-provider.c
==============================================================================
--- trunk/libgda/gda-server-provider.c	(original)
+++ trunk/libgda/gda-server-provider.c	Tue Jun  3 19:26:50 2008
@@ -114,6 +114,7 @@
 	klass->cancel = NULL;
 	klass->create_connection = NULL;
 	memset (&(klass->meta_funcs), 0, sizeof (GdaServerProviderMeta));
+	klass->xa_funcs = NULL;
 
 	 /* Properties */
         object_class->set_property = gda_server_provider_set_property;

Modified: trunk/libgda/gda-server-provider.h
==============================================================================
--- trunk/libgda/gda-server-provider.h	(original)
+++ trunk/libgda/gda-server-provider.h	Tue Jun  3 19:26:50 2008
@@ -31,6 +31,7 @@
 #include <libgda/gda-quark-list.h>
 #include <libgda/gda-statement.h>
 #include <libgda/gda-meta-store.h>
+#include <libgda/gda-xa-transaction.h>
 
 G_BEGIN_DECLS
 
@@ -54,7 +55,8 @@
 	GDA_SERVER_PROVIDER_OPERATION_ERROR,
 	GDA_SERVER_PROVIDER_INTERNAL_ERROR,
 	GDA_SERVER_PROVIDER_BUSY_ERROR,
-	GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR
+	GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
+	GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR
 } GdaServerProviderError;
 
 struct _GdaServerProvider {
@@ -177,6 +179,19 @@
 	
 } GdaServerProviderMeta;
 
+/* distributed transaction support */
+typedef struct {
+	gboolean (*xa_start)    (GdaServerProvider *, GdaConnection *, const GdaXaTransactionId *, GError **);
+
+	gboolean (*xa_end)      (GdaServerProvider *, GdaConnection *, const GdaXaTransactionId *, GError **);
+	gboolean (*xa_prepare)  (GdaServerProvider *, GdaConnection *, const GdaXaTransactionId *, GError **);
+
+	gboolean (*xa_commit)   (GdaServerProvider *, GdaConnection *, const GdaXaTransactionId *, GError **);
+	gboolean (*xa_rollback) (GdaServerProvider *, GdaConnection *, const GdaXaTransactionId *, GError **);
+
+	GList   *(*xa_recover)  (GdaServerProvider *, GdaConnection *, GError **);
+} GdaServerProviderXa;
+
 typedef void (*GdaServerProviderAsyncCallback) (GdaServerProvider *provider, GdaConnection *cnc, guint task_id, 
 						gboolean result_status, gpointer data);
 
@@ -250,8 +265,13 @@
 	gboolean                (* cancel)               (GdaServerProvider *provider, GdaConnection *cnc, 
 							  guint task_id, GError **error);
 	GdaConnection          *(* create_connection)    (GdaServerProvider *provider);
+
+	/* meta data reporting */
 	GdaServerProviderMeta      meta_funcs;
 
+	/* distributed transaction */
+	GdaServerProviderXa       *xa_funcs; /* it is a pointer! => set to %NULL if unsupported by provider */
+
 	/* Padding for future expansion */
 	void                    (*_gda_reserved1)        (void);
 	void                    (*_gda_reserved2)        (void);

Added: trunk/libgda/gda-xa-transaction.c
==============================================================================
--- (empty file)
+++ trunk/libgda/gda-xa-transaction.c	Tue Jun  3 19:26:50 2008
@@ -0,0 +1,843 @@
+/* GDA library
+ * 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 <glib/gi18n-lib.h>
+#include <libgda/gda-xa-transaction.h>
+#include <libgda/gda-connection.h>
+#include <libgda/gda-server-provider.h>
+#include <libgda/gda-value.h>
+#include <string.h>
+#include <libgda/gda-decl.h>
+
+static void gda_xa_transaction_class_init (GdaXaTransactionClass *klass);
+static void gda_xa_transaction_init       (GdaXaTransaction *xa_trans, GdaXaTransactionClass *klass);
+static void gda_xa_transaction_dispose   (GObject *object);
+
+static void gda_xa_transaction_set_property (GObject *object,
+					     guint param_id,
+					     const GValue *value,
+					     GParamSpec *pspec);
+static void gda_xa_transaction_get_property (GObject *object,
+					     guint param_id,
+					     GValue *value,
+					     GParamSpec *pspec);
+
+
+static GObjectClass* parent_class = NULL;
+#define PROV_CLASS(provider) (GDA_SERVER_PROVIDER_CLASS (G_OBJECT_GET_CLASS (provider)))
+
+struct _GdaXaTransactionPrivate {
+	GdaXaTransactionId  xid;
+	GHashTable         *cnc_hash; /* key = cnc, value = branch qualifier as a GdaBinary */
+	GList              *cnc_list;
+	GdaConnection      *non_xa_cnc; /* connection which does not support distributed transaction (also in @cnc_list) */
+};
+
+/* properties */
+enum
+{
+        PROP_0,
+        PROP_FORMAT_ID,
+	PROP_TRANSACT_ID
+};
+
+
+/*
+ * GdaXaTransaction class implementation
+ */
+
+static void
+gda_xa_transaction_class_init (GdaXaTransactionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+	object_class->dispose = gda_xa_transaction_dispose;
+
+	/* Properties */
+        object_class->set_property = gda_xa_transaction_set_property;
+        object_class->get_property = gda_xa_transaction_get_property;
+        g_object_class_install_property (object_class, PROP_FORMAT_ID,
+                                         g_param_spec_uint ("format-id", NULL, NULL,
+							    0, G_MAXUINT32, 1,
+							    G_PARAM_WRITABLE | G_PARAM_READABLE | 
+							    G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (object_class, PROP_TRANSACT_ID,
+                                         g_param_spec_string ("transaction-id", NULL, NULL,
+							      NULL,
+							      G_PARAM_WRITABLE | G_PARAM_READABLE | 
+							      G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gda_xa_transaction_init (GdaXaTransaction *xa_trans, GdaXaTransactionClass *klass)
+{
+	xa_trans->priv = g_new0 (GdaXaTransactionPrivate, 1);
+	xa_trans->priv->xid.format = 1;
+	xa_trans->priv->xid.gtrid_length = 0;
+	xa_trans->priv->xid.bqual_length = 0;
+
+	xa_trans->priv->cnc_hash = g_hash_table_new_full (NULL, NULL, NULL, gda_binary_free);
+	xa_trans->priv->cnc_list = NULL;
+	xa_trans->priv->non_xa_cnc = NULL;
+}
+
+static void
+gda_xa_transaction_dispose (GObject *object)
+{
+	GdaXaTransaction *xa_trans = (GdaXaTransaction *) object;
+
+	g_return_if_fail (GDA_IS_XA_TRANSACTION (xa_trans));
+
+	if (xa_trans->priv->cnc_list) {
+		GList *list;
+		for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+			g_object_set_data (G_OBJECT (list->data), "_gda_xa_transaction", NULL);
+			g_object_unref (G_OBJECT (list->data));
+		}
+		g_list_free (xa_trans->priv->cnc_list);
+		xa_trans->priv->cnc_list = NULL;
+	}
+	if (xa_trans->priv->cnc_hash) {
+		g_hash_table_destroy (xa_trans->priv->cnc_hash);
+		xa_trans->priv->cnc_hash = NULL;
+	}
+
+	/* chain to parent class */
+	parent_class->dispose (object);
+}
+
+static void
+gda_xa_transaction_set_property (GObject *object,
+				 guint param_id,
+				 const GValue *value,
+				 GParamSpec *pspec)
+{
+        GdaXaTransaction *xa_trans;
+
+        xa_trans = GDA_XA_TRANSACTION (object);
+        if (xa_trans->priv) {
+                switch (param_id) {
+                case PROP_FORMAT_ID:
+			xa_trans->priv->xid.format = g_value_get_uint (value);
+                        break;
+                case PROP_TRANSACT_ID: {
+			const gchar *tmp;
+			gint len;
+		    
+			tmp = g_value_get_string (value);
+			if (!tmp) {
+				gchar *dtmp;
+				dtmp = g_strdup_printf ("gda_global_transaction_%p", xa_trans);
+				len = strlen (dtmp);
+				xa_trans->priv->xid.gtrid_length = len;
+				memcpy (xa_trans->priv->xid.data, dtmp, len);
+				g_free (dtmp);
+			}
+			else {
+				len = strlen (tmp);
+				if (len > 64)
+					g_warning (_("Global transaction ID can not have more than 64 bytes"));
+				else {
+					xa_trans->priv->xid.gtrid_length = len;
+					memcpy (xa_trans->priv->xid.data, tmp, len);
+				}
+			}
+                        break;
+		}
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+}
+
+static void
+gda_xa_transaction_get_property (GObject *object,
+				 guint param_id,
+				 GValue *value,
+				 GParamSpec *pspec)
+{
+	GdaXaTransaction *xa_trans;
+
+        xa_trans = GDA_XA_TRANSACTION (object);
+        if (xa_trans->priv) {
+                switch (param_id) {
+                case PROP_FORMAT_ID:
+			g_value_set_uint (value, xa_trans->priv->xid.format);
+                        break;
+                case PROP_TRANSACT_ID: {
+			gchar *tmp;
+
+			tmp = g_new0 (gchar, xa_trans->priv->xid.gtrid_length + 1);
+			memcpy (tmp, xa_trans->priv->xid.data, xa_trans->priv->xid.gtrid_length);
+			g_value_take_string (value, tmp);
+                        break;
+		}
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+}
+
+
+/* module error */
+GQuark gda_xa_transaction_error_quark (void)
+{
+        static GQuark quark;
+        if (!quark)
+                quark = g_quark_from_static_string ("gda_xa_transaction_error");
+        return quark;
+}
+
+GType
+gda_xa_transaction_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		if (type == 0) {
+			static GTypeInfo info = {
+				sizeof (GdaXaTransactionClass),
+				(GBaseInitFunc) NULL,
+				(GBaseFinalizeFunc) NULL,
+				(GClassInitFunc) gda_xa_transaction_class_init,
+				NULL, NULL,
+				sizeof (GdaXaTransaction),
+				0,
+				(GInstanceInitFunc) gda_xa_transaction_init
+			};
+			type = g_type_register_static (G_TYPE_OBJECT, "GdaXaTransaction", &info, 0);
+		}
+	}
+
+	return type;
+}
+
+/**
+ * gda_xa_transaction_new
+ * @format: a format ID
+ * @global_transaction_id: the global transaction ID
+ *
+ * Creates a new #GdaXaTransaction object, which will control the processus of
+ * performing a distributed transaction across several connections.
+ *
+ * Returns: the newly created object.
+ */
+GdaXaTransaction *
+gda_xa_transaction_new (guint32 format, const gchar *global_transaction_id)
+{
+	g_return_val_if_fail (global_transaction_id && *global_transaction_id, NULL);
+	return (GdaXaTransaction*) g_object_new (GDA_TYPE_XA_TRANSACTION, "format", format, 
+						 "trans", global_transaction_id, NULL);
+}
+
+/**
+ * gda_xa_transaction_register_connection
+ * @xa_trans: a #GdaXaTransaction object
+ * @cnc: the connection to add to @xa_trans
+ * @branch: the branch qualifier
+ * @error: a place to store errors, or %NULL
+ *
+ * Registers @cnc to be used by @xa_trans to create a distributed transaction.
+ *
+ * Note: any #GdaConnection object can only be registered with at most one #GdaXaTransaction object; also
+ * some connections may not be registered at all with a #GdaXaTransaction object because the database
+ * provider being used does not support it.
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_xa_transaction_register_connection  (GdaXaTransaction *xa_trans, GdaConnection *cnc, 
+					 const gchar *branch, GError **error)
+{
+	GdaBinary *bin;
+
+	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (branch && *branch, FALSE);
+
+	const GdaBinary *ebranch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+	if (ebranch) {
+		bin = g_new0 (GdaBinary, 1);
+		bin->data = g_strdup (branch);
+		bin->binary_length = strlen (branch) + 1;
+		g_hash_table_insert (xa_trans->priv->cnc_hash, cnc, bin);
+		return TRUE;
+	}
+
+	/* check that @cnc is not already registered with another GdaXaTransaction object */
+	if (g_object_get_data (G_OBJECT (cnc), "_gda_xa_transaction")) {
+		g_set_error (error, GDA_XA_TRANSACTION_ERROR,
+			     GDA_XA_TRANSACTION_ALREADY_REGISTERED_ERROR,
+			     _("Connection aleardy registered with another GdaXaTransaction object"));
+		return FALSE;
+	}
+
+	/* check that connection supports distributed transaction, only ONE connection in @xa_trans is allowed
+	 * to not support them */
+	GdaServerProvider *prov;
+	prov = gda_connection_get_provider_obj (cnc);
+	if (! PROV_CLASS (prov)->xa_funcs) {
+		/* if another connection does not support distributed transaction, then there is an error */
+		if (xa_trans->priv->non_xa_cnc) {
+			g_set_error (error, GDA_XA_TRANSACTION_ERROR,
+				     GDA_XA_TRANSACTION_DTP_NOT_SUPPORTED_ERROR,
+				     _("Connection does not support distributed transaction"));
+			return FALSE;
+		}
+		else
+			xa_trans->priv->non_xa_cnc = cnc;
+	}
+
+
+	bin = g_new0 (GdaBinary, 1);
+	bin->data = g_strdup (branch);
+	bin->binary_length = strlen (branch) + 1;
+	xa_trans->priv->cnc_list = g_list_prepend (xa_trans->priv->cnc_list, cnc);
+	g_hash_table_insert (xa_trans->priv->cnc_hash, cnc, bin);
+	g_object_ref (cnc);
+	g_object_set_data (G_OBJECT (cnc), "_gda_xa_transaction", xa_trans);
+
+	return TRUE;
+}
+
+/**
+ * gda_xa_transaction_unregister_connection
+ * @xa_trans: a #GdaXaTransaction object
+ * @cnc: the connection to add to @xa_trans
+ *
+ * Unregisters @cnc to be used by @xa_trans to create a distributed transaction. This is
+ * the opposite of gda_xa_transaction_register_connection().
+ */
+void
+gda_xa_transaction_unregister_connection (GdaXaTransaction *xa_trans, GdaConnection *cnc)
+{
+	g_return_if_fail (GDA_IS_XA_TRANSACTION (xa_trans));
+	g_return_if_fail (GDA_IS_CONNECTION (cnc));
+
+	if (!g_list_find (xa_trans->priv->cnc_list, cnc)) {
+		g_warning (_("Cannot unregister connection not registered with GdaXaTransaction object"));
+		return;
+	}
+	xa_trans->priv->cnc_list = g_list_remove (xa_trans->priv->cnc_list, cnc);
+	g_hash_table_remove (xa_trans->priv->cnc_hash, cnc);
+	g_object_set_data (G_OBJECT (cnc), "_gda_xa_transaction", NULL);
+	g_object_unref (cnc);
+}
+
+/**
+ * gda_xa_transaction_begin
+ * @xa_trans: a #GdaXaTransaction object
+ * @error: a place to store errors, or %NULL
+ *
+ * Begins a distributed transaction (managed by @xa_trans). Please note that this phase may fail
+ * for some connections if a (normal) transaction is already started (this depends on the database
+ * provider being used), so it's better to avoid starting any (normal) transaction on any of the
+ * connections registered with @xa_trans.
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_xa_transaction_begin  (GdaXaTransaction *xa_trans, GError **error)
+{
+	GList *list;
+	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
+	
+	for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+		GdaConnection *cnc;
+		GdaServerProvider *prov;
+		
+		cnc = GDA_CONNECTION (list->data);
+		prov = gda_connection_get_provider_obj (cnc);
+		if (cnc != xa_trans->priv->non_xa_cnc) {
+		       
+			if (!PROV_CLASS (prov)->xa_funcs->xa_start) {
+				g_warning (_("Provider error: method xa_start() not implemented for provider %s"),
+					   gda_server_provider_get_name (prov));
+				break;
+			}
+			else {
+				const GdaBinary *branch;
+				branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+				memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+					branch->data, branch->binary_length);
+				if (!PROV_CLASS (prov)->xa_funcs->xa_start (prov, cnc, &(xa_trans->priv->xid), error))
+					break;
+			}
+		}
+		else {
+			/* do a simple BEGIN */
+			if (! gda_connection_begin_transaction (cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN,
+								error))
+				break;
+		}
+	}
+
+	if (list) {
+		/* something went wrong */
+		for (; list; list = list->prev) {
+			GdaConnection *cnc;
+			GdaServerProvider *prov;
+			
+			cnc = GDA_CONNECTION (list->data);
+			prov = gda_connection_get_provider_obj (cnc);
+			if (cnc != xa_trans->priv->non_xa_cnc) {
+				if (!PROV_CLASS (prov)->xa_funcs->xa_rollback) 
+					g_warning (_("Provider error: method xa_rollback() not implemented for provider %s"),
+						   gda_server_provider_get_name (prov));
+				else {
+					const GdaBinary *branch;
+					branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+					memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+						branch->data, branch->binary_length);
+					PROV_CLASS (prov)->xa_funcs->xa_rollback (prov, cnc, &(xa_trans->priv->xid), NULL);
+				}
+			}
+			else {
+				/* do a simple ROLLBACK */
+				gda_connection_rollback_transaction (cnc, NULL, NULL);
+			}
+		}
+		return FALSE;
+	}
+	return TRUE;
+}
+
+/**
+ * gda_xa_transaction_commit
+ * @xa_trans: a #GdaXaTransaction object
+ * @cnc_to_recover: a place to store the list of connections for which the commit phase failed, or %NULL
+ * @error: a place to store errors, or %NULL
+ *
+ * Commits a distributed transaction (managed by @xa_trans). The commit is composed of two phases:
+ * <itemizedlist>
+ *   <listitem><para>a PREPARE phase where all the connections are required to store their transaction data to a 
+ *     permanent place (to be able to complete the commit should a problem occur afterwards)</para></listitem>
+ *   <listitem><para>a COMMIT phase where the transaction data is actually written to the database</para></listitem>
+ * </itemizedlist>
+ *
+ * If the PREPARE phase fails for any of the connection registered with @xa_trans, then the distributed commit
+ * fails and FALSE is returned. During the COMMIT phase, some commit may actually fail but the transaction can
+ * still be completed because the PREPARE phase succeeded (through the recover method).
+ *
+ * Returns: TRUE if no error occurred (there may be some connections to recover, though)
+ */
+gboolean
+gda_xa_transaction_commit (GdaXaTransaction *xa_trans, GSList **cnc_to_recover, GError **error)
+{
+	GList *list;
+	if (cnc_to_recover)
+		*cnc_to_recover = NULL;
+
+	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
+	
+	/* 
+	 * PREPARE phase 
+	 */
+	for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+		GdaConnection *cnc;
+		GdaServerProvider *prov;
+		const GdaBinary *branch;
+		
+		if (cnc == xa_trans->priv->non_xa_cnc)
+			continue;
+
+		cnc = GDA_CONNECTION (list->data);
+		prov = gda_connection_get_provider_obj (cnc);
+
+		branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+		memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+			branch->data, branch->binary_length);
+
+		if (PROV_CLASS (prov)->xa_funcs->xa_end && 
+		    !PROV_CLASS (prov)->xa_funcs->xa_end (prov, cnc, &(xa_trans->priv->xid), error))
+			break;
+
+		if (!PROV_CLASS (prov)->xa_funcs->xa_prepare) {
+			g_warning (_("Provider error: method xa_prepare() not implemented for provider %s"),
+				   gda_server_provider_get_name (prov));
+			break;
+		}
+		if (!PROV_CLASS (prov)->xa_funcs->xa_commit) {
+			g_warning (_("Provider error: method xa_commit() not implemented for provider %s"),
+				   gda_server_provider_get_name (prov));
+			break;
+		}
+
+		if (!PROV_CLASS (prov)->xa_funcs->xa_prepare (prov, cnc, &(xa_trans->priv->xid), error))
+			break;
+	}
+	if (list) {
+		/* something went wrong during the PREPARE phase => rollback everything */
+		for (; list; list = list->prev) {
+			GdaConnection *cnc;
+			GdaServerProvider *prov;
+			
+			if (cnc == xa_trans->priv->non_xa_cnc) 
+				gda_connection_rollback_transaction (cnc, NULL, NULL);
+			else {
+				const GdaBinary *branch;
+				cnc = GDA_CONNECTION (list->data);
+				prov = gda_connection_get_provider_obj (cnc);
+				branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+				memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+					branch->data, branch->binary_length);
+
+				if (PROV_CLASS (prov)->xa_funcs->xa_rollback)
+					PROV_CLASS (prov)->xa_funcs->xa_rollback (prov, cnc, &(xa_trans->priv->xid), NULL);
+				else
+					g_warning (_("Provider error: method xa_rollback() not implemented for provider %s"),
+						   gda_server_provider_get_name (prov));
+			}
+		}
+		return FALSE;
+	}
+
+	/*
+	 * COMMIT phase 
+	 */
+	if (xa_trans->priv->non_xa_cnc &&
+	    ! gda_connection_commit_transaction (xa_trans->priv->non_xa_cnc, NULL, error)) {
+		/* something went wrong => rollback everything */
+		for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+			GdaConnection *cnc;
+			GdaServerProvider *prov;
+			
+			if (cnc == xa_trans->priv->non_xa_cnc)
+				gda_connection_rollback_transaction (cnc, NULL, NULL);
+			else {
+				const GdaBinary *branch;
+				cnc = GDA_CONNECTION (list->data);
+				prov = gda_connection_get_provider_obj (cnc);
+				branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+				memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+					branch->data, branch->binary_length);
+
+				if (PROV_CLASS (prov)->xa_funcs->xa_rollback)
+					PROV_CLASS (prov)->xa_funcs->xa_rollback (prov, cnc, &(xa_trans->priv->xid), NULL);
+				else
+					g_warning (_("Provider error: method xa_rollback() not implemented for provider %s"),
+						   gda_server_provider_get_name (prov));
+			}
+		}
+		return FALSE;
+	}
+
+	for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+		GdaConnection *cnc;
+		GdaServerProvider *prov;
+		const GdaBinary *branch;
+		
+		if (cnc == xa_trans->priv->non_xa_cnc)
+			continue;
+
+		cnc = GDA_CONNECTION (list->data);
+		prov = gda_connection_get_provider_obj (cnc);
+		branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+		memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+			branch->data, branch->binary_length);
+		if (!PROV_CLASS (prov)->xa_funcs->xa_commit (prov, cnc, &(xa_trans->priv->xid), error) &&
+		    cnc_to_recover)
+			*cnc_to_recover = g_slist_prepend (*cnc_to_recover, cnc);
+	}
+	
+	return TRUE;
+}
+
+/**
+ * gda_xa_transaction_rollback
+ * @xa_trans: a #GdaXaTransaction object
+ * @error: a place to store errors, or %NULL
+ *
+ * Cancels a distributed transaction (managed by @xa_trans). 
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_xa_transaction_rollback (GdaXaTransaction *xa_trans, GError **error)
+{
+	GList *list;
+	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
+	
+	for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+		GdaConnection *cnc;
+		GdaServerProvider *prov;
+
+		cnc = GDA_CONNECTION (list->data);
+		prov = gda_connection_get_provider_obj (cnc);
+		
+		if (cnc == xa_trans->priv->non_xa_cnc) 
+			gda_connection_rollback_transaction (cnc, NULL, NULL);
+		else {
+			const GdaBinary *branch;
+			branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+			memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+			branch->data, branch->binary_length);
+			if (!PROV_CLASS (prov)->xa_funcs->xa_rollback) 
+				g_warning (_("Provider error: method xa_prepare() not implemented for provider %s"),
+					   gda_server_provider_get_name (prov));
+			else
+				PROV_CLASS (prov)->xa_funcs->xa_rollback (prov, cnc, &(xa_trans->priv->xid), error);
+		}
+	}
+	return TRUE;
+}
+
+/**
+ * gda_xa_transaction_commit_recovered
+ * @xa_trans: a #GdaXaTransaction object
+ * @cnc_to_recover: a place to store the list of connections for which the there were data to recover and which failed
+ * to be actually committed, or %NULL
+ * @error: a place to store errors, or %NULL
+ *
+ * Tries to commit the data prepared but which failed to commit (see gda_xa_transaction_commit()). This
+ * method allows one to terminate a distributed transaction which succedded but for which some
+ * connections needed to be recovered.
+ *
+ * Returns: TRUE if all the data which was still uncommitted has been committed
+ */
+gboolean
+gda_xa_transaction_commit_recovered (GdaXaTransaction *xa_trans, GSList **cnc_to_recover, GError **error)
+{
+	GList *list;
+	gboolean retval = TRUE;
+	
+	if (cnc_to_recover)
+		*cnc_to_recover = NULL;
+	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
+	
+	for (list = xa_trans->priv->cnc_list; list; list = list->next) {
+		GdaConnection *cnc;
+		GdaServerProvider *prov;
+
+		cnc = GDA_CONNECTION (list->data);
+		prov = gda_connection_get_provider_obj (cnc);
+		
+		if (cnc == xa_trans->priv->non_xa_cnc) 
+			continue;
+		else {
+			GList *recov_xid_list;
+			
+
+			if (!PROV_CLASS (prov)->xa_funcs->xa_recover) 
+				g_warning (_("Provider error: method xa_recover() not implemented for provider %s"),
+					   gda_server_provider_get_name (prov));
+			else {
+				const GdaBinary *branch;
+				GList *xlist;
+				gboolean commit_needed = FALSE;
+				
+				recov_xid_list = PROV_CLASS (prov)->xa_funcs->xa_recover (prov, cnc, error);
+				if (!recov_xid_list)
+					continue;
+
+				branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc);
+				memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length,
+					branch->data, branch->binary_length);
+				for (xlist = recov_xid_list; xlist; xlist = xlist->next) {
+					GdaXaTransactionId *xid = (GdaXaTransactionId*) xlist->data;
+					if (!xid)
+						/* ignore unknown XID format */
+						continue;
+
+					if (!commit_needed &&
+					    (xid->format == xa_trans->priv->xid.format) &&
+					    (xid->gtrid_length == xa_trans->priv->xid.gtrid_length) &&
+					    (xid->bqual_length == xa_trans->priv->xid.bqual_length) &&
+					    ! memcmp (xa_trans->priv->xid.data, xid->data, xid->bqual_length + xid->bqual_length)) 
+						/* found a transaction to commit */
+						commit_needed = TRUE;
+						
+					g_free (xid);
+				}
+				g_list_free (recov_xid_list);
+
+				if (commit_needed) {
+					if (!PROV_CLASS (prov)->xa_funcs->xa_commit) {
+						g_warning (_("Provider error: method xa_commit() not implemented for provider %s"),
+							   gda_server_provider_get_name (prov));
+						retval = FALSE;
+					}
+					else {
+						retval = PROV_CLASS (prov)->xa_funcs->xa_commit (prov, cnc, 
+												 &(xa_trans->priv->xid), 
+												 error);
+						if (!retval)
+							if (cnc_to_recover)
+								*cnc_to_recover = g_slist_prepend (*cnc_to_recover, cnc);
+					}
+				}
+			}
+		}
+	}
+	return retval;	
+}
+
+/**
+ * gda_xa_transaction_id_to_string
+ * @xid: a #GdaXaTransactionId pointer
+ *
+ * Creates a string representation of @xid, in the format &lt;gtrid&gt;,&lt;bqual&gt;,&lt;formatID&gt; the 
+ * &lt;gtrid&gt; and &lt;bqual&gt; strings contain alphanumeric characters, and non alphanumeric characters
+ * are converted to "%ab" where ab is the hexadecimal representation of the character.
+ *
+ * Returns: a read-only string representation of @xid
+ */
+const gchar *
+gda_xa_transaction_id_to_string (const GdaXaTransactionId *xid)
+{
+#define XID_SIZE (128 * 3 +15)
+	static gchar str[XID_SIZE];
+	int index = 0, i;
+	
+	g_return_val_if_fail (xid, NULL);
+
+	memset (str, 0, XID_SIZE);
+	
+	/* global transaction ID */
+	for (i = 0; i < xid->gtrid_length; i++) {
+		if (g_ascii_isalnum (xid->data[i])) {
+			str [index] = xid->data[i];
+			index++;
+		}
+		else 
+			index += sprintf (str+index, "%%%02x", xid->data[i]);
+	}
+
+	/* branch qualifier */
+	str [index++] = ',';
+	for (i = 0; i < xid->bqual_length; i++) {
+		if (g_ascii_isalnum (xid->data[xid->gtrid_length + i])) {
+			str [index] = xid->data[xid->gtrid_length + i];
+			index++;
+		}
+		else 
+			index += sprintf (str+index, "%%%02x", xid->data[xid->gtrid_length + i]);
+	}
+
+	/* Format ID */
+	str [index++] = ',';
+	sprintf (str, "%d", xid->format);
+
+	return str;
+}
+
+/**
+ * gda_xa_transaction_string_to_id
+ * @str: a string representation of a #GdaXaTransactionId, in the "gtrid,bqual,formatID" format
+ *
+ * Creates a new #GdaXaTransactionId structure from its string representation, it's the opposite
+ * of gda_xa_transaction_id_to_string().
+ *
+ * Returns: a new #GdaXaTransactionId structure, or %NULL in @str has a wrong format
+ */
+GdaXaTransactionId *
+gda_xa_transaction_string_to_id (const gchar *str)
+{
+	GdaXaTransactionId *xid;
+	const gchar *ptr;
+	int index = 0;
+
+	g_return_val_if_fail (str, NULL);
+
+	xid = g_new0 (GdaXaTransactionId, 1);
+
+	/* global transaction ID */
+	for (ptr = str; *ptr && (*ptr != ','); ptr++, index++) {
+		if (*ptr == '%') {
+			ptr++;
+			if (*ptr && (((*ptr >= 'a') && (*ptr <= 'f')) ||
+				     ((*ptr >= '0') && (*ptr <= '9')))) {
+				if ((*ptr >= 'a') && (*ptr <= 'f'))
+					xid->data [index] = (*ptr - 'a' + 10) * 16;
+				else
+					xid->data [index] = (*ptr - '0') * 16;
+				ptr++;
+				if (*ptr && (((*ptr >= 'a') && (*ptr <= 'f')) ||
+					     ((*ptr >= '0') && (*ptr <= '9')))) {
+					if ((*ptr >= 'a') && (*ptr <= 'f'))
+						xid->data [index] = (*ptr - 'a' + 10);
+					else
+						xid->data [index] = (*ptr - '0');
+				}
+				else
+					goto onerror;
+			}
+			else
+				goto onerror;
+		}
+		else if (g_ascii_isalnum (*ptr))
+			xid->data [index] = *ptr;
+		else
+			goto onerror;
+			 
+	}
+	xid->gtrid_length = index;
+
+	/* branch qualifier */
+	if (*ptr != ',') 
+		goto onerror;
+	for (ptr++; *ptr && (*ptr != ','); ptr++, index++) {
+		if (*ptr == '%') {
+			ptr++;
+			if (*ptr && (((*ptr >= 'a') && (*ptr <= 'f')) ||
+				     ((*ptr >= '0') && (*ptr <= '9')))) {
+				if ((*ptr >= 'a') && (*ptr <= 'f'))
+					xid->data [index] = (*ptr - 'a' + 10) * 16;
+				else
+					xid->data [index] = (*ptr - '0') * 16;
+				ptr++;
+				if (*ptr && (((*ptr >= 'a') && (*ptr <= 'f')) ||
+					     ((*ptr >= '0') && (*ptr <= '9')))) {
+					if ((*ptr >= 'a') && (*ptr <= 'f'))
+						xid->data [index] = (*ptr - 'a' + 10);
+					else
+						xid->data [index] = (*ptr - '0');
+				}
+				else
+					goto onerror;
+			}
+			else
+				goto onerror;
+		}
+		else if (g_ascii_isalnum (*ptr))
+			xid->data [index] = *ptr;
+		else
+			goto onerror;
+			 
+	}
+	xid->bqual_length = index - xid->gtrid_length;
+
+	/* Format ID */
+	if (*ptr != ',') 
+		goto onerror;
+	ptr++;
+	xid->format = atoi (ptr);
+	
+	return xid;
+
+ onerror:
+	g_free (xid);
+	return NULL;
+}

Added: trunk/libgda/gda-xa-transaction.h
==============================================================================
--- (empty file)
+++ trunk/libgda/gda-xa-transaction.h	Tue Jun  3 19:26:50 2008
@@ -0,0 +1,88 @@
+/* GDA library
+ * 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.
+ */
+
+#ifndef __GDA_XA_TRANSACTION_H__
+#define __GDA_XA_TRANSACTION_H__
+
+#include <glib-object.h>
+#include <libgda/gda-enums.h>
+#include <libgda/gda-decl.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_XA_TRANSACTION            (gda_xa_transaction_get_type())
+#define GDA_XA_TRANSACTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_XA_TRANSACTION, GdaXaTransaction))
+#define GDA_XA_TRANSACTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_XA_TRANSACTION, GdaXaTransactionClass))
+#define GDA_IS_XA_TRANSACTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_XA_TRANSACTION))
+#define GDA_IS_XA_TRANSACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_XA_TRANSACTION))
+
+typedef struct _GdaXaTransaction        GdaXaTransaction;
+typedef struct _GdaXaTransactionClass   GdaXaTransactionClass;
+typedef struct _GdaXaTransactionPrivate GdaXaTransactionPrivate;
+typedef struct _GdaXaTransactionId      GdaXaTransactionId;
+
+/* error reporting */
+extern GQuark gda_xa_transaction_error_quark (void);
+#define GDA_XA_TRANSACTION_ERROR gda_xa_transaction_error_quark ()
+
+typedef enum
+{
+        GDA_XA_TRANSACTION_ALREADY_REGISTERED_ERROR,
+	GDA_XA_TRANSACTION_DTP_NOT_SUPPORTED_ERROR
+} GdaXaTransactionError;
+
+struct _GdaXaTransaction {
+	GObject                  object;
+	GdaXaTransactionPrivate *priv;
+};
+
+struct _GdaXaTransactionClass {
+	GObjectClass             parent_class;
+};
+
+struct _GdaXaTransactionId {
+	guint32  format;       /* any number */
+	gushort  gtrid_length; /* 1-64 */
+	gushort  bqual_length; /* 1-64 */
+	char     data [128];
+};
+
+GType                     gda_xa_transaction_get_type             (void) G_GNUC_CONST;
+GdaXaTransaction         *gda_xa_transaction_new                  (guint32 format, const gchar *global_transaction_id);
+
+gboolean                  gda_xa_transaction_register_connection  (GdaXaTransaction *xa_trans, GdaConnection *cnc, 
+								   const gchar *branch, GError **error);
+void                      gda_xa_transaction_unregister_connection (GdaXaTransaction *xa_trans, GdaConnection *cnc);
+
+gboolean                  gda_xa_transaction_begin  (GdaXaTransaction *xa_trans, GError **error);
+gboolean                  gda_xa_transaction_commit (GdaXaTransaction *xa_trans, GSList **cnc_to_recover, GError **error);gboolean                  gda_xa_transaction_rollback (GdaXaTransaction *xa_trans, GError **error);
+
+gboolean                  gda_xa_transaction_commit_recovered (GdaXaTransaction *xa_trans, GSList **cnc_to_recover, 
+							       GError **error);
+
+/* utility functions */
+const gchar              *gda_xa_transaction_id_to_string (const GdaXaTransactionId *xid);
+GdaXaTransactionId       *gda_xa_transaction_string_to_id (const gchar *str);
+
+G_END_DECLS
+
+#endif

Modified: trunk/libgda/libgda.h.in
==============================================================================
--- trunk/libgda/libgda.h.in	(original)
+++ trunk/libgda/libgda.h.in	Tue Jun  3 19:26:50 2008
@@ -48,6 +48,7 @@
 #include <libgda/gda-server-operation.h>
 #include <libgda/gda-server-provider.h>
 #include <libgda/gda-threader.h>
+#include <libgda/gda-xa-transaction.h>
 #include <libgda/gda-transaction-status.h>
 #include <libgda/gda-transaction-status-private.h>
 #include <libgda/gda-util.h>

Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c	Tue Jun  3 19:26:50 2008
@@ -269,6 +269,9 @@
 	provider_class->meta_funcs.routine_col = _gda_sqlite_meta_routine_col;
 	provider_class->meta_funcs._routine_par = _gda_sqlite_meta__routine_par;
 	provider_class->meta_funcs.routine_par = _gda_sqlite_meta_routine_par;
+
+	/* SQLite doe not support distributed transactions */
+	provider_class->xa_funcs = NULL;
 }
 
 static void

Modified: trunk/providers/postgres/gda-postgres-meta.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-meta.c	(original)
+++ trunk/providers/postgres/gda-postgres-meta.c	Tue Jun  3 19:26:50 2008
@@ -462,6 +462,17 @@
 	GdaDataModel *model;
 	gboolean retval;
 
+	/* check correct postgres server version */
+	PostgresConnectionData *cdata;
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
+
 	model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_EL_TYPES_ALL], NULL, 
 							 error);
 	if (!model)
@@ -483,9 +494,20 @@
 
 	gda_holder_set_value (gda_set_get_holder (i_set, "name"), specific_name);
 	cstr = g_value_get_string (specific_name);
-	if (*cstr == 'C')
+	if (*cstr == 'C') {
+		/* check correct postgres server version */
+		PostgresConnectionData *cdata;
+		cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+		if (!cdata)
+			return FALSE;
+		if (cdata->version_float < 8.2) {
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+				     _("PostgreSQL version 8.2.0 at least is required"));
+			return FALSE;
+		}
 		model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_EL_TYPES_COL], i_set, 
 								 error);
+	}
 	else if (*cstr == 'D')
 		model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_EL_TYPES_DOM], i_set, 
 								 error);
@@ -591,6 +613,17 @@
 	GdaDataModel *tables_model, *views_model;
 	gboolean retval = TRUE;
 
+	/* check correct postgres server version */
+	PostgresConnectionData *cdata;
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
+
 	tables_model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_TABLES_ALL], i_set, error);
 	if (!tables_model)
 		return FALSE;
@@ -625,6 +658,17 @@
 	GdaDataModel *tables_model, *views_model;
 	gboolean retval = TRUE;
 
+	/* check correct postgres server version */
+	PostgresConnectionData *cdata;
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
+
 	gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog);
 	gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema);
 	if (!table_name_n) {
@@ -730,9 +774,15 @@
 		G_TYPE_INT, G_TYPE_NONE
 	};
 
+	/* check correct postgres server version */
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata)
 		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
 
 	/* use a prepared statement for the "base" model */
 	gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog);
@@ -781,6 +831,17 @@
 	GdaDataModel *model;
 	gboolean retval;
 
+	/* check correct postgres server version */
+	PostgresConnectionData *cdata;
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
+
 	model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_VIEWS_COLUMNS_ALL], i_set, error);
 	if (!model)
 		return FALSE;
@@ -989,6 +1050,17 @@
 	GdaDataModel *model;
 	gboolean retval;
 
+	/* check correct postgres server version */
+	PostgresConnectionData *cdata;
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
+
 	model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_TRIGGERS_ALL], NULL, 
 							 error);
 	if (!model)
@@ -1008,6 +1080,17 @@
 	GdaDataModel *model;
 	gboolean retval = TRUE;
 
+	/* check correct postgres server version */
+	PostgresConnectionData *cdata;
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+	if (cdata->version_float < 8.2) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_SERVER_VERSION_ERROR,
+			     _("PostgreSQL version 8.2.0 at least is required"));
+		return FALSE;
+	}
+
 	gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog);
 	gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema);
 	gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name);

Modified: trunk/providers/postgres/gda-postgres-provider.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-provider.c	(original)
+++ trunk/providers/postgres/gda-postgres-provider.c	Tue Jun  3 19:26:50 2008
@@ -119,6 +119,23 @@
 								     guint *task_id, GdaServerProviderAsyncCallback async_cb, 
 								     gpointer cb_data, GError **error);
 
+/* distributed transactions */
+static gboolean gda_postgres_provider_xa_start    (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_postgres_provider_xa_end      (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_postgres_provider_xa_prepare  (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_postgres_provider_xa_commit   (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+
+static GList   *gda_postgres_provider_xa_recover  (GdaServerProvider *provider, GdaConnection *cnc, 
+						   GError **error);
+
 /* 
  * private connection data destroy 
  */
@@ -136,12 +153,20 @@
 	I_STMT_BEGIN,
 	I_STMT_COMMIT,
 	I_STMT_ROLLBACK,
+	I_STMT_XA_PREPARE,
+	I_STMT_XA_COMMIT,
+	I_STMT_XA_ROLLBACK,
+	I_STMT_XA_RECOVER
 } InternalStatementItem;
 
 gchar *internal_sql[] = {
 	"BEGIN",
 	"COMMIT",
-	"ROLLBACK"
+	"ROLLBACK",
+	"PREPARE TRANSACTION ##xid::string",
+	"COMMIT PREPARED ##xid::string",
+	"ROLLBACK PREPARED ##xid::string",
+	"SELECT gid FROM pg_catalog.pg_prepared_xacts"
 };
 
 /*
@@ -227,6 +252,14 @@
 	provider_class->meta_funcs.routine_col = _gda_postgres_meta_routine_col;
 	provider_class->meta_funcs._routine_par = _gda_postgres_meta__routine_par;
 	provider_class->meta_funcs.routine_par = _gda_postgres_meta_routine_par;
+	
+	provider_class->xa_funcs = g_new0 (GdaServerProviderXa, 1);
+	provider_class->xa_funcs->xa_start = gda_postgres_provider_xa_start;
+	provider_class->xa_funcs->xa_end = gda_postgres_provider_xa_end;
+	provider_class->xa_funcs->xa_prepare = gda_postgres_provider_xa_prepare;
+	provider_class->xa_funcs->xa_commit = gda_postgres_provider_xa_commit;
+	provider_class->xa_funcs->xa_rollback = gda_postgres_provider_xa_rollback;
+	provider_class->xa_funcs->xa_recover = gda_postgres_provider_xa_recover;
 }
 
 static void
@@ -2112,6 +2145,165 @@
 }
 
 /*
+ * starts a distributed transaction: put the XA transaction in the ACTIVE state
+ */
+static gboolean
+gda_postgres_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc, 
+				const GdaXaTransactionId *xid, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	return gda_postgres_provider_begin_transaction (provider, cnc, NULL, 
+							GDA_TRANSACTION_ISOLATION_READ_COMMITTED, error);
+}
+
+/*
+ * 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_postgres_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc, 
+			      const GdaXaTransactionId *xid, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	/* nothing to do for PostgreSQL here */
+	return TRUE;
+}
+
+/*
+ * prepares the distributed transaction: put the XA transaction in the PREPARED state
+ */
+static gboolean
+gda_postgres_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc, 
+				  const GdaXaTransactionId *xid, GError **error)
+{
+	GdaSet *params;
+	gint affected;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	if (!gda_statement_get_parameters (internal_stmt [I_STMT_XA_PREPARE], &params, error))
+		return FALSE;
+	if (!gda_set_set_holder_value (params, "xid", gda_xa_transaction_id_to_string (xid))) {
+		g_object_unref (params);
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+			     _("Could not set the XA transaction ID parameter"));
+		return FALSE;
+	}
+	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_PREPARE], params, 
+								NULL, error);
+	g_object_unref (params);
+	if (affected == -1)
+		return FALSE;
+	else
+		return TRUE;
+}
+
+/*
+ * commits the distributed transaction: actually write the prepared data to the database and
+ * terminates the XA transaction
+ */
+static gboolean
+gda_postgres_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc, 
+				 const GdaXaTransactionId *xid, GError **error)
+{
+	GdaSet *params;
+	gint affected;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	if (!gda_statement_get_parameters (internal_stmt [I_STMT_XA_PREPARE], &params, error))
+		return FALSE;
+	if (!gda_set_set_holder_value (params, "xid", gda_xa_transaction_id_to_string (xid))) {
+		g_object_unref (params);
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+			     _("Could not set the XA transaction ID parameter"));
+		return FALSE;
+	}
+	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_COMMIT], params, 
+								NULL, error);
+	g_object_unref (params);
+	if (affected == -1)
+		return FALSE;
+	else
+		return TRUE;
+}
+
+/*
+ * Rolls back an XA transaction, possible only if in the ACTIVE, IDLE or PREPARED state
+ */
+static gboolean
+gda_postgres_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+				   const GdaXaTransactionId *xid, GError **error)
+{
+	GdaSet *params;
+	gint affected;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	if (!gda_statement_get_parameters (internal_stmt [I_STMT_XA_PREPARE], &params, error))
+		return FALSE;
+	if (!gda_set_set_holder_value (params, "xid", gda_xa_transaction_id_to_string (xid))) {
+		g_object_unref (params);
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
+			     _("Could not set the XA transaction ID parameter"));
+		return FALSE;
+	}
+	affected = gda_connection_statement_execute_non_select (cnc, internal_stmt [I_STMT_XA_ROLLBACK], params, 
+								NULL, error);
+	g_object_unref (params);
+	if (affected == -1)
+		return FALSE;
+	else
+		return TRUE;
+}
+
+/*
+ * 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_postgres_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+				  GError **error)
+{
+	GdaDataModel *model;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, NULL);
+
+	model = gda_connection_statement_execute_select (cnc, internal_stmt [I_STMT_XA_RECOVER], NULL, error);
+	if (!model)
+		return NULL;
+	else {
+		GList *list = NULL;
+		gint i, nrows;
+		
+		nrows = gda_data_model_get_n_rows (model);
+		for (i = 0; i < nrows; i++) {
+			const GValue *cvalue = gda_data_model_get_value_at (model, 0, i);
+			if (cvalue && !gda_value_is_null (cvalue))
+				list = g_list_prepend (list, 
+						       gda_xa_transaction_string_to_id (g_value_get_string (cvalue)));
+		}
+		g_object_unref (model);
+		return list;
+	}
+}
+
+
+/*
  * Free connection's specific data
  */
 static void

Modified: trunk/providers/skel-implementation/capi/gda-capi-provider.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-provider.c	(original)
+++ trunk/providers/skel-implementation/capi/gda-capi-provider.c	Tue Jun  3 19:26:50 2008
@@ -112,6 +112,23 @@
 								 guint *task_id, GdaServerProviderAsyncCallback async_cb, 
 								 gpointer cb_data, GError **error);
 
+/* distributed transactions */
+static gboolean gda_capi_provider_xa_start    (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_capi_provider_xa_end      (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_capi_provider_xa_prepare  (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+
+static gboolean gda_capi_provider_xa_commit   (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+static gboolean gda_capi_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+						   const GdaXaTransactionId *xid, GError **error);
+
+static GList   *gda_capi_provider_xa_recover  (GdaServerProvider *provider, GdaConnection *cnc, 
+						   GError **error);
+
 /* 
  * private connection data destroy 
  */
@@ -219,6 +236,15 @@
 	provider_class->meta_funcs.routine_col = _gda_capi_meta_routine_col;
 	provider_class->meta_funcs._routine_par = _gda_capi_meta__routine_par;
 	provider_class->meta_funcs.routine_par = _gda_capi_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_capi_provider_xa_start;
+	provider_class->xa_funcs->xa_end = gda_capi_provider_xa_end;
+	provider_class->xa_funcs->xa_prepare = gda_capi_provider_xa_prepare;
+	provider_class->xa_funcs->xa_commit = gda_capi_provider_xa_commit;
+	provider_class->xa_funcs->xa_rollback = gda_capi_provider_xa_rollback;
+	provider_class->xa_funcs->xa_recover = gda_capi_provider_xa_recover;
 }
 
 static void
@@ -1038,6 +1064,135 @@
 }
 
 /*
+ * starts a distributed transaction: put the XA transaction in the ACTIVE state
+ */
+static gboolean
+gda_capi_provider_xa_start (GdaServerProvider *provider, GdaConnection *cnc, 
+				const GdaXaTransactionId *xid, GError **error)
+{
+	CapiConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (CapiConnectionData*) 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_capi_provider_xa_end (GdaServerProvider *provider, GdaConnection *cnc, 
+			      const GdaXaTransactionId *xid, GError **error)
+{
+	CapiConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (CapiConnectionData*) 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_capi_provider_xa_prepare (GdaServerProvider *provider, GdaConnection *cnc, 
+				  const GdaXaTransactionId *xid, GError **error)
+{
+	CapiConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (CapiConnectionData*) 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_capi_provider_xa_commit (GdaServerProvider *provider, GdaConnection *cnc, 
+				 const GdaXaTransactionId *xid, GError **error)
+{
+	CapiConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (CapiConnectionData*) 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_capi_provider_xa_rollback (GdaServerProvider *provider, GdaConnection *cnc, 
+				   const GdaXaTransactionId *xid, GError **error)
+{
+	CapiConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, FALSE);
+	g_return_val_if_fail (xid, FALSE);
+
+	cdata = (CapiConnectionData*) 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_capi_provider_xa_recover (GdaServerProvider *provider, GdaConnection *cnc,
+				  GError **error)
+{
+	CapiConnectionData *cdata;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (gda_connection_get_provider_obj (cnc) == provider, NULL);
+
+	cdata = (CapiConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata) 
+		return NULL;
+
+	TO_IMPLEMENT;
+	return NULL;
+}
+
+/*
  * Free connection's specific data
  */
 static void



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