/* * Copyright (C) 2006 - 2011 Vivien Malerba * Copyright (C) 2007 Armin Burgmeier * Copyright (C) 2010 David King * Copyright (C) 2010 Jonh Wendell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #define PARENT_TYPE G_TYPE_OBJECT static void gda_transaction_status_class_init (GdaTransactionStatusClass *klass); static void gda_transaction_status_init (GdaTransactionStatus *tstatus, GdaTransactionStatusClass *klass); static void gda_transaction_status_finalize (GObject *object); static GObjectClass* parent_class = NULL; /* * GdaTransactionStatus class implementation */ static void gda_transaction_status_class_init (GdaTransactionStatusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = gda_transaction_status_finalize; } static void gda_transaction_status_init (GdaTransactionStatus *tstatus, G_GNUC_UNUSED GdaTransactionStatusClass *klass) { tstatus->name = NULL; tstatus->isolation_level = GDA_TRANSACTION_ISOLATION_UNKNOWN; tstatus->events = NULL; tstatus->state = GDA_TRANSACTION_STATUS_STATE_OK; } static void event_free (GdaTransactionStatusEvent *event) { switch (event->type) { case GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT: g_free (event->pl.svp_name); break; case GDA_TRANSACTION_STATUS_EVENT_SQL: g_free (event->pl.sql); break; case GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION: g_object_unref (event->pl.sub_trans); break; default: g_assert_not_reached (); } if (event->conn_event) g_object_unref (event->conn_event); g_free (event); } static void gda_transaction_status_finalize (GObject *object) { GdaTransactionStatus *tstatus = (GdaTransactionStatus *) object; g_return_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus)); /* free memory */ if (tstatus->name) { g_free (tstatus->name); tstatus->name = NULL; } if (tstatus->events) { g_list_foreach (tstatus->events, (GFunc) event_free, NULL); g_list_free (tstatus->events); tstatus->events = NULL; } /* chain to parent class */ parent_class->finalize (object); } GType gda_transaction_status_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static GMutex registering; static GTypeInfo info = { sizeof (GdaTransactionStatusClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gda_transaction_status_class_init, NULL, NULL, sizeof (GdaTransactionStatus), 0, (GInstanceInitFunc) gda_transaction_status_init, 0 }; g_mutex_lock (®istering); if (type == 0) type = g_type_register_static (PARENT_TYPE, "GdaTransactionStatus", &info, 0); g_mutex_unlock (®istering); } return type; } /** * gda_transaction_status_new: * @name: name for the transaction * * Creates a new #GdaTransactionStatus object, which allows a fine-tune and * full control of transactions to be used with providers. * * Returns: (transfer full): the newly created object. */ GdaTransactionStatus * gda_transaction_status_new (const gchar *name) { GdaTransactionStatus *tstatus; tstatus = g_object_new (GDA_TYPE_TRANSACTION_STATUS, NULL); if (name) tstatus->name = g_strdup (name); return tstatus; } /** * gda_transaction_status_add_event_svp: (skip) * */ GdaTransactionStatusEvent * gda_transaction_status_add_event_svp (GdaTransactionStatus *tstatus, const gchar *svp_name) { GdaTransactionStatusEvent *ev; g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL); g_return_val_if_fail (svp_name, NULL); ev = g_new0 (GdaTransactionStatusEvent, 1); ev->trans = tstatus; ev->type = GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT; ev->pl.svp_name = g_strdup (svp_name); tstatus->events = g_list_append (tstatus->events, ev); return ev; } /** * gda_transaction_status_add_event_sql: (skip) * */ GdaTransactionStatusEvent * gda_transaction_status_add_event_sql (GdaTransactionStatus *tstatus, const gchar *sql, GdaConnectionEvent *conn_event) { GdaTransactionStatusEvent *ev; g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL); g_return_val_if_fail (sql, NULL); ev = g_new0 (GdaTransactionStatusEvent, 1); ev->trans = tstatus; ev->type = GDA_TRANSACTION_STATUS_EVENT_SQL; ev->pl.sql = g_strdup (sql); if (conn_event) { ev->conn_event = conn_event; g_object_ref (conn_event); } tstatus->events = g_list_append (tstatus->events, ev); return ev; } /** * gda_transaction_status_add_event_sub: (skip) * */ GdaTransactionStatusEvent * gda_transaction_status_add_event_sub (GdaTransactionStatus *tstatus, GdaTransactionStatus *sub_trans) { GdaTransactionStatusEvent *ev; g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL); g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (sub_trans), NULL); ev = g_new0 (GdaTransactionStatusEvent, 1); ev->trans = tstatus; ev->type = GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION; ev->pl.sub_trans = sub_trans; g_object_ref (ev->pl.sub_trans); tstatus->events = g_list_append (tstatus->events, ev); return ev; } void gda_transaction_status_free_events (GdaTransactionStatus *tstatus, GdaTransactionStatusEvent *event, gboolean free_after) { GList *node; g_return_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus)); node = g_list_find (tstatus->events, event); g_return_if_fail (node); if (free_after) { GList *list = g_list_last (tstatus->events); GList *tmp; while (list != node) { event_free ((GdaTransactionStatusEvent *)(list->data)); tmp = list->prev; tstatus->events = g_list_delete_link (tstatus->events, list); list = tmp; } } event_free (event); tstatus->events = g_list_delete_link (tstatus->events, node); } /** * gda_transaction_status_find: * * Returns: (transfer full) (allow-none): */ GdaTransactionStatus * gda_transaction_status_find (GdaTransactionStatus *tstatus, const gchar *str, GdaTransactionStatusEvent **destev) { GdaTransactionStatus *trans = NULL; GList *evlist; if (!tstatus) return NULL; g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL); if (destev) *destev = NULL; if (!str) return gda_transaction_status_find_current (tstatus, destev, FALSE); if (tstatus->name && !strcmp (tstatus->name, str)) return tstatus; for (evlist = tstatus->events; evlist && !trans; evlist = evlist->next) { GdaTransactionStatusEvent *ev = (GdaTransactionStatusEvent *)(evlist->data); switch (ev->type) { case GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT: if (!strcmp (ev->pl.svp_name, str)) trans = tstatus; break; case GDA_TRANSACTION_STATUS_EVENT_SQL: if (!strcmp (ev->pl.sql, str)) trans = tstatus; break; case GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION: trans = gda_transaction_status_find (ev->pl.sub_trans, str, NULL); break; default: g_assert_not_reached (); } if (trans && destev) *destev = ev; } return trans; } /** * gda_transaction_status_find_current: * * Find a pointer to the "current" _unnamed_ transaction, which is the last * transaction if there are several nested transactions * * Returns: (transfer full) (allow-none): */ GdaTransactionStatus * gda_transaction_status_find_current (GdaTransactionStatus *tstatus, GdaTransactionStatusEvent **destev, gboolean unnamed_only) { GdaTransactionStatus *trans = NULL; GList *evlist; if (!tstatus) return NULL; g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL); if (destev) *destev = NULL; for (evlist = tstatus->events; evlist && !trans; evlist = evlist->next) { GdaTransactionStatusEvent *ev = (GdaTransactionStatusEvent *)(evlist->data); if (ev->type == GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION) trans = gda_transaction_status_find_current (ev->pl.sub_trans, destev, unnamed_only); if (trans && destev && !(*destev)) *destev = ev; } if (!trans && ((unnamed_only && !tstatus->name) || !unnamed_only)) trans = tstatus; return trans; } #ifdef GDA_DEBUG void gda_transaction_status_dump (GdaTransactionStatus *tstatus, guint offset) { gchar *str; GList *evlist; gchar *levels[] = {"GDA_TRANSACTION_ISOLATION_UNKNOWN", "GDA_TRANSACTION_ISOLATION_READ_COMMITTED", "GDA_TRANSACTION_ISOLATION_READ_UNCOMMITTED", "GDA_TRANSACTION_ISOLATION_REPEATABLE_READ", "GDA_TRANSACTION_ISOLATION_SERIALIZABLE"}; #define level_str(lev) ((lev < 4) ? levels[lev] : "UNKNOWN ISOLATION LEVEL") g_return_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus)); str = g_new (gchar, offset+1); memset (str, ' ', offset); str [offset] = 0; g_print ("%sGdaTransactionStatus: %s (%s, %p)\n", str, tstatus->name ? tstatus->name : "(NONAME)", level_str (tstatus->isolation_level), tstatus); for (evlist = tstatus->events; evlist; evlist = evlist->next) { GdaTransactionStatusEvent *ev = (GdaTransactionStatusEvent *) (evlist->data); switch (ev->type) { case GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT: g_print ("%s *SAVE POINT %s (%p)\n", str, ev->pl.svp_name, ev); break; case GDA_TRANSACTION_STATUS_EVENT_SQL: g_print ("%s *SQL %s (%p): %s\n", str, ev->pl.sql, ev, ev->conn_event ? gda_connection_event_get_description (ev->conn_event) : "_no event_"); break; case GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION: g_print ("%s *SUB TRANSACTION (%p)\n", str, ev); gda_transaction_status_dump (ev->pl.sub_trans, offset + 5); break; default: g_assert_not_reached (); } } g_free (str); } #endif