libgda r3158 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite providers/postgres providers/skel-implementation/capi
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3158 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite providers/postgres providers/skel-implementation/capi
- Date: Tue, 3 Jun 2008 19:26:50 +0000 (UTC)
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 <gtrid>,<bqual>,<formatID> the
+ * <gtrid> and <bqual> 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], ¶ms, 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], ¶ms, 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], ¶ms, 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]