/** * gda_xa_transaction_rollback: * @xa_trans: a #GdaXaTransaction object * @error: (allow-none): 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 (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, /* Flawfinder: ignore */ branch->data, branch->binary_length); if (!PROV_CLASS (prov)->xa_funcs->xa_rollback) g_warning (_("Provider error: %s method not implemented for provider %s"), "xa_prepare()", 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_rollback: * @xa_trans: a #GdaXaTransaction object * @error: (nullable): 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); GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans); for (list = priv->cnc_list; list; list = list->next) { GdaConnection *cnc; GdaServerProvider *prov; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); if (cnc == priv->non_xa_cnc) gda_connection_rollback_transaction (cnc, NULL, NULL); else { GdaBinary *branch; branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); GError *lerror = NULL; _gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), &lerror); if (error && !*error) g_propagate_error (error, lerror); else g_clear_error (&lerror); } } return TRUE; }
static void gda_xa_transaction_dispose (GObject *object) { GdaXaTransaction *xa_trans = (GdaXaTransaction *) object; g_return_if_fail (GDA_IS_XA_TRANSACTION (xa_trans)); GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans); if (priv->cnc_list) { GList *list; for (list = 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 (priv->cnc_list); priv->cnc_list = NULL; } if (priv->cnc_hash) { g_hash_table_destroy (priv->cnc_hash); priv->cnc_hash = NULL; } /* chain to parent class */ G_OBJECT_CLASS (gda_xa_transaction_parent_class)->dispose (object); }
/** * gda_xa_transaction_register_connection: * @xa_trans: a #GdaXaTransaction object * @cnc: the connection to add to @xa_trans * @branch: the branch qualifier * @error: (allow-none): 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); if (strlen (branch) >= 64) { g_set_error (error, GDA_XA_TRANSACTION_ERROR, GDA_XA_TRANSACTION_CONNECTION_BRANCH_LENGTH_ERROR, "%s", _("Connection branch cannot exceed 63 bytes")); return FALSE; } const GdaBinary *ebranch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc); if (ebranch) { bin = g_new0 (GdaBinary, 1); bin->data = (guchar*) 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, "%s", _("Connection already 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 (cnc); if (!gda_server_provider_supports_feature (prov, cnc, GDA_CONNECTION_FEATURE_XA_TRANSACTIONS)) { /* 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, "%s", _("Connection does not support distributed transaction")); return FALSE; } else xa_trans->priv->non_xa_cnc = cnc; } bin = g_new0 (GdaBinary, 1); bin->data = (guchar*) 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_begin: * @xa_trans: a #GdaXaTransaction object * @error: (nullable): 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); GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans); for (list = priv->cnc_list; list; list = list->next) { GdaConnection *cnc; GdaServerProvider *prov; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); if (cnc != priv->non_xa_cnc) { GdaBinary *branch; branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); if (! _gda_server_provider_xa_start (prov, cnc, &(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 (cnc); if (cnc != priv->non_xa_cnc) { GdaBinary *branch; branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); _gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), NULL); } else { /* do a simple ROLLBACK */ gda_connection_rollback_transaction (cnc, NULL, NULL); } } return FALSE; } 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_commit_recovered: * @xa_trans: a #GdaXaTransaction object * @cnc_to_recover: (allow-none) (element-type Gda.Connection) (out callee-allocates): 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: (allow-none): 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 succeeded 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 (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: %s method not implemented for provider %s"), "xa_recover()", 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, /* Flawfinder: ignore */ 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: %s method not implemented for provider %s"), "xa_commit()", 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_commit: * @xa_trans: a #GdaXaTransaction object * @cnc_to_recover: (allow-none) (element-type Gda.Connection) (out callee-allocates): 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 = NULL; GdaServerProvider *prov; const GdaBinary *branch; if (cnc == xa_trans->priv->non_xa_cnc) continue; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc); memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length, /* Flawfinder: ignore */ 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: %s method not implemented for provider %s"), "xa_prepare()", gda_server_provider_get_name (prov)); break; } if (!PROV_CLASS (prov)->xa_funcs->xa_commit) { g_warning (_("Provider error: %s method not implemented for provider %s"), "xa_commit()", 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 = NULL; 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 (cnc); branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc); memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length, /* Flawfinder: ignore */ 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: %s method not implemented for provider %s"), "xa_rollback()", 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 = NULL; 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 (cnc); branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc); memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length, /* Flawfinder: ignore */ 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: %s method not implemented for provider %s"), "xa_rollback()", gda_server_provider_get_name (prov)); } } return FALSE; } for (list = xa_trans->priv->cnc_list; list; list = list->next) { GdaConnection *cnc = NULL; GdaServerProvider *prov; const GdaBinary *branch; if (cnc == xa_trans->priv->non_xa_cnc) continue; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); branch = g_hash_table_lookup (xa_trans->priv->cnc_hash, cnc); memcpy (xa_trans->priv->xid.data + xa_trans->priv->xid.gtrid_length, /* Flawfinder: ignore */ 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_begin: * @xa_trans: a #GdaXaTransaction object * @error: (allow-none): 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 (cnc); if (cnc != xa_trans->priv->non_xa_cnc) { if (!PROV_CLASS (prov)->xa_funcs->xa_start) { g_warning (_("Provider error: %s method not implemented for provider %s"), "xa_start()", 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, /* Flawfinder: ignore */ 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 (cnc); if (cnc != xa_trans->priv->non_xa_cnc) { if (!PROV_CLASS (prov)->xa_funcs->xa_rollback) g_warning (_("Provider error: %s method not implemented for provider %s"), "xa_rollback()", 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, /* Flawfinder: ignore */ 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_recovered: * @xa_trans: a #GdaXaTransaction object * @cnc_to_recover: (nullable) (element-type Gda.Connection) (out callee-allocates): 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: (nullable): 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 succeeded 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); GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans); for (list = priv->cnc_list; list; list = list->next) { GdaConnection *cnc; GdaServerProvider *prov; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); if (cnc == priv->non_xa_cnc) continue; else { GList *recov_xid_list; GdaBinary *branch; GList *xlist; gboolean commit_needed = FALSE; recov_xid_list = _gda_server_provider_xa_recover (prov, cnc, error); if (!recov_xid_list) continue; branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); 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 == priv->xid.format) && (xid->gtrid_length == priv->xid.gtrid_length) && (xid->bqual_length == priv->xid.bqual_length) && ! memcmp (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) { retval = _gda_server_provider_xa_commit (prov, cnc, &(priv->xid), error); if (!retval) if (cnc_to_recover) *cnc_to_recover = g_slist_prepend (*cnc_to_recover, cnc); } } } return retval; }
/** * gda_xa_transaction_commit: * @xa_trans: a #GdaXaTransaction object * @cnc_to_recover: (nullable) (element-type Gda.Connection) (out callee-allocates): 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); GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans); /* * PREPARE phase */ for (list = priv->cnc_list; list; list = list->next) { GdaConnection *cnc = NULL; GdaServerProvider *prov; GdaBinary *branch; if (cnc == priv->non_xa_cnc) continue; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); if (!_gda_server_provider_xa_end (prov, cnc, &(priv->xid), error)) break; if (!_gda_server_provider_xa_prepare (prov, cnc, &(priv->xid), error)) break; } if (list) { /* something went wrong during the PREPARE phase => rollback everything */ for (; list; list = list->prev) { GdaConnection *cnc = NULL; GdaServerProvider *prov; if (cnc == priv->non_xa_cnc) gda_connection_rollback_transaction (cnc, NULL, NULL); else { GdaBinary *branch; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); _gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), NULL); } } return FALSE; } /* * COMMIT phase */ if (priv->non_xa_cnc && ! gda_connection_commit_transaction (priv->non_xa_cnc, NULL, error)) { /* something went wrong => rollback everything */ for (list = priv->cnc_list; list; list = list->next) { GdaConnection *cnc = NULL; GdaServerProvider *prov; if (cnc == priv->non_xa_cnc) gda_connection_rollback_transaction (cnc, NULL, NULL); else { GdaBinary *branch; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); _gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), NULL); } } return FALSE; } for (list = priv->cnc_list; list; list = list->next) { GdaConnection *cnc = NULL; GdaServerProvider *prov; GdaBinary *branch; if (cnc == priv->non_xa_cnc) continue; cnc = GDA_CONNECTION (list->data); prov = gda_connection_get_provider (cnc); branch = g_hash_table_lookup (priv->cnc_hash, cnc); memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */ gda_binary_get_data (branch), gda_binary_get_size (branch)); if (! _gda_server_provider_xa_commit (prov, cnc, &(priv->xid), error) && cnc_to_recover) *cnc_to_recover = g_slist_prepend (*cnc_to_recover, cnc); } return TRUE; }