1 /*
2 * Copyright (C) 2006 - 2011 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2007 Armin Burgmeier <armin@openismus.com>
4 * Copyright (C) 2010 David King <davidk@openismus.com>
5 * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include <libgda/gda-transaction-status.h>
24 #include <libgda/gda-transaction-status-private.h>
25 #include <string.h>
26 #include <libgda/gda-decl.h>
27 #include <libgda/gda-connection-event.h>
28
29 #define PARENT_TYPE G_TYPE_OBJECT
30
31 static void gda_transaction_status_class_init (GdaTransactionStatusClass *klass);
32 static void gda_transaction_status_init (GdaTransactionStatus *tstatus, GdaTransactionStatusClass *klass);
33 static void gda_transaction_status_finalize (GObject *object);
34
35 static GObjectClass* parent_class = NULL;
36
37 /*
38 * GdaTransactionStatus class implementation
39 */
40
41 static void
gda_transaction_status_class_init(GdaTransactionStatusClass * klass)42 gda_transaction_status_class_init (GdaTransactionStatusClass *klass)
43 {
44 GObjectClass *object_class = G_OBJECT_CLASS (klass);
45
46 parent_class = g_type_class_peek_parent (klass);
47 object_class->finalize = gda_transaction_status_finalize;
48 }
49
50 static void
gda_transaction_status_init(GdaTransactionStatus * tstatus,G_GNUC_UNUSED GdaTransactionStatusClass * klass)51 gda_transaction_status_init (GdaTransactionStatus *tstatus, G_GNUC_UNUSED GdaTransactionStatusClass *klass)
52 {
53 tstatus->name = NULL;
54 tstatus->isolation_level = GDA_TRANSACTION_ISOLATION_UNKNOWN;
55 tstatus->events = NULL;
56 tstatus->state = GDA_TRANSACTION_STATUS_STATE_OK;
57 }
58
59 static void
event_free(GdaTransactionStatusEvent * event)60 event_free (GdaTransactionStatusEvent *event)
61 {
62 switch (event->type) {
63 case GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT:
64 g_free (event->pl.svp_name);
65 break;
66 case GDA_TRANSACTION_STATUS_EVENT_SQL:
67 g_free (event->pl.sql);
68 break;
69 case GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION:
70 g_object_unref (event->pl.sub_trans);
71 break;
72 default:
73 g_assert_not_reached ();
74 }
75 if (event->conn_event)
76 g_object_unref (event->conn_event);
77
78 g_free (event);
79 }
80
81 static void
gda_transaction_status_finalize(GObject * object)82 gda_transaction_status_finalize (GObject *object)
83 {
84 GdaTransactionStatus *tstatus = (GdaTransactionStatus *) object;
85
86 g_return_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus));
87
88 /* free memory */
89 if (tstatus->name) {
90 g_free (tstatus->name);
91 tstatus->name = NULL;
92 }
93
94 if (tstatus->events) {
95 g_list_foreach (tstatus->events, (GFunc) event_free, NULL);
96 g_list_free (tstatus->events);
97 tstatus->events = NULL;
98 }
99
100 /* chain to parent class */
101 parent_class->finalize (object);
102 }
103
104 GType
gda_transaction_status_get_type(void)105 gda_transaction_status_get_type (void)
106 {
107 static GType type = 0;
108
109 if (G_UNLIKELY (type == 0)) {
110 static GMutex registering;
111 static GTypeInfo info = {
112 sizeof (GdaTransactionStatusClass),
113 (GBaseInitFunc) NULL,
114 (GBaseFinalizeFunc) NULL,
115 (GClassInitFunc) gda_transaction_status_class_init,
116 NULL, NULL,
117 sizeof (GdaTransactionStatus),
118 0,
119 (GInstanceInitFunc) gda_transaction_status_init,
120 0
121 };
122 g_mutex_lock (®istering);
123 if (type == 0)
124 type = g_type_register_static (PARENT_TYPE, "GdaTransactionStatus", &info, 0);
125 g_mutex_unlock (®istering);
126 }
127
128 return type;
129 }
130
131 /**
132 * gda_transaction_status_new:
133 * @name: name for the transaction
134 *
135 * Creates a new #GdaTransactionStatus object, which allows a fine-tune and
136 * full control of transactions to be used with providers.
137 *
138 * Returns: (transfer full): the newly created object.
139 */
140 GdaTransactionStatus *
gda_transaction_status_new(const gchar * name)141 gda_transaction_status_new (const gchar *name)
142 {
143 GdaTransactionStatus *tstatus;
144
145 tstatus = g_object_new (GDA_TYPE_TRANSACTION_STATUS, NULL);
146 if (name)
147 tstatus->name = g_strdup (name);
148
149 return tstatus;
150 }
151
152 /**
153 * gda_transaction_status_add_event_svp: (skip)
154 *
155 */
156 GdaTransactionStatusEvent *
gda_transaction_status_add_event_svp(GdaTransactionStatus * tstatus,const gchar * svp_name)157 gda_transaction_status_add_event_svp (GdaTransactionStatus *tstatus, const gchar *svp_name)
158 {
159 GdaTransactionStatusEvent *ev;
160
161 g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL);
162 g_return_val_if_fail (svp_name, NULL);
163
164 ev = g_new0 (GdaTransactionStatusEvent, 1);
165 ev->trans = tstatus;
166 ev->type = GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT;
167 ev->pl.svp_name = g_strdup (svp_name);
168 tstatus->events = g_list_append (tstatus->events, ev);
169
170 return ev;
171 }
172
173 /**
174 * gda_transaction_status_add_event_sql: (skip)
175 *
176 */
177 GdaTransactionStatusEvent *
gda_transaction_status_add_event_sql(GdaTransactionStatus * tstatus,const gchar * sql,GdaConnectionEvent * conn_event)178 gda_transaction_status_add_event_sql (GdaTransactionStatus *tstatus, const gchar *sql, GdaConnectionEvent *conn_event)
179 {
180 GdaTransactionStatusEvent *ev;
181
182 g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL);
183 g_return_val_if_fail (sql, NULL);
184
185 ev = g_new0 (GdaTransactionStatusEvent, 1);
186 ev->trans = tstatus;
187 ev->type = GDA_TRANSACTION_STATUS_EVENT_SQL;
188 ev->pl.sql = g_strdup (sql);
189 if (conn_event) {
190 ev->conn_event = conn_event;
191 g_object_ref (conn_event);
192 }
193 tstatus->events = g_list_append (tstatus->events, ev);
194
195 return ev;
196 }
197
198 /**
199 * gda_transaction_status_add_event_sub: (skip)
200 *
201 */
202 GdaTransactionStatusEvent *
gda_transaction_status_add_event_sub(GdaTransactionStatus * tstatus,GdaTransactionStatus * sub_trans)203 gda_transaction_status_add_event_sub (GdaTransactionStatus *tstatus, GdaTransactionStatus *sub_trans)
204 {
205 GdaTransactionStatusEvent *ev;
206
207 g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL);
208 g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (sub_trans), NULL);
209
210 ev = g_new0 (GdaTransactionStatusEvent, 1);
211 ev->trans = tstatus;
212 ev->type = GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION;
213 ev->pl.sub_trans = sub_trans;
214 g_object_ref (ev->pl.sub_trans);
215 tstatus->events = g_list_append (tstatus->events, ev);
216
217 return ev;
218 }
219
220 void
gda_transaction_status_free_events(GdaTransactionStatus * tstatus,GdaTransactionStatusEvent * event,gboolean free_after)221 gda_transaction_status_free_events (GdaTransactionStatus *tstatus, GdaTransactionStatusEvent *event,
222 gboolean free_after)
223 {
224 GList *node;
225
226 g_return_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus));
227 node = g_list_find (tstatus->events, event);
228 g_return_if_fail (node);
229
230 if (free_after) {
231 GList *list = g_list_last (tstatus->events);
232 GList *tmp;
233 while (list != node) {
234 event_free ((GdaTransactionStatusEvent *)(list->data));
235 tmp = list->prev;
236 tstatus->events = g_list_delete_link (tstatus->events, list);
237 list = tmp;
238 }
239 }
240 event_free (event);
241 tstatus->events = g_list_delete_link (tstatus->events, node);
242 }
243
244 /**
245 * gda_transaction_status_find:
246 *
247 * Returns: (transfer full) (allow-none):
248 */
249 GdaTransactionStatus *
gda_transaction_status_find(GdaTransactionStatus * tstatus,const gchar * str,GdaTransactionStatusEvent ** destev)250 gda_transaction_status_find (GdaTransactionStatus *tstatus, const gchar *str, GdaTransactionStatusEvent **destev)
251 {
252 GdaTransactionStatus *trans = NULL;
253 GList *evlist;
254
255 if (!tstatus)
256 return NULL;
257
258 g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL);
259 if (destev)
260 *destev = NULL;
261
262 if (!str)
263 return gda_transaction_status_find_current (tstatus, destev, FALSE);
264
265 if (tstatus->name && !strcmp (tstatus->name, str))
266 return tstatus;
267
268 for (evlist = tstatus->events; evlist && !trans; evlist = evlist->next) {
269 GdaTransactionStatusEvent *ev = (GdaTransactionStatusEvent *)(evlist->data);
270 switch (ev->type) {
271 case GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT:
272 if (!strcmp (ev->pl.svp_name, str))
273 trans = tstatus;
274 break;
275 case GDA_TRANSACTION_STATUS_EVENT_SQL:
276 if (!strcmp (ev->pl.sql, str))
277 trans = tstatus;
278 break;
279 case GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION:
280 trans = gda_transaction_status_find (ev->pl.sub_trans, str, NULL);
281 break;
282 default:
283 g_assert_not_reached ();
284 }
285 if (trans && destev)
286 *destev = ev;
287 }
288
289 return trans;
290 }
291
292 /**
293 * gda_transaction_status_find_current:
294 *
295 * Find a pointer to the "current" _unnamed_ transaction, which is the last
296 * transaction if there are several nested transactions
297 *
298 * Returns: (transfer full) (allow-none):
299 */
300 GdaTransactionStatus *
gda_transaction_status_find_current(GdaTransactionStatus * tstatus,GdaTransactionStatusEvent ** destev,gboolean unnamed_only)301 gda_transaction_status_find_current (GdaTransactionStatus *tstatus, GdaTransactionStatusEvent **destev, gboolean unnamed_only)
302 {
303 GdaTransactionStatus *trans = NULL;
304 GList *evlist;
305
306 if (!tstatus)
307 return NULL;
308 g_return_val_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus), NULL);
309 if (destev)
310 *destev = NULL;
311
312 for (evlist = tstatus->events; evlist && !trans; evlist = evlist->next) {
313 GdaTransactionStatusEvent *ev = (GdaTransactionStatusEvent *)(evlist->data);
314 if (ev->type == GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION)
315 trans = gda_transaction_status_find_current (ev->pl.sub_trans, destev, unnamed_only);
316 if (trans && destev && !(*destev))
317 *destev = ev;
318 }
319
320 if (!trans && ((unnamed_only && !tstatus->name) || !unnamed_only))
321 trans = tstatus;
322
323 return trans;
324 }
325
326
327 #ifdef GDA_DEBUG
328 void
gda_transaction_status_dump(GdaTransactionStatus * tstatus,guint offset)329 gda_transaction_status_dump (GdaTransactionStatus *tstatus, guint offset)
330 {
331 gchar *str;
332 GList *evlist;
333 gchar *levels[] = {"GDA_TRANSACTION_ISOLATION_UNKNOWN",
334 "GDA_TRANSACTION_ISOLATION_READ_COMMITTED",
335 "GDA_TRANSACTION_ISOLATION_READ_UNCOMMITTED",
336 "GDA_TRANSACTION_ISOLATION_REPEATABLE_READ",
337 "GDA_TRANSACTION_ISOLATION_SERIALIZABLE"};
338 #define level_str(lev) ((lev < 4) ? levels[lev] : "UNKNOWN ISOLATION LEVEL")
339
340 g_return_if_fail (GDA_IS_TRANSACTION_STATUS (tstatus));
341
342 str = g_new (gchar, offset+1);
343 memset (str, ' ', offset);
344 str [offset] = 0;
345
346 g_print ("%sGdaTransactionStatus: %s (%s, %p)\n", str, tstatus->name ? tstatus->name : "(NONAME)",
347 level_str (tstatus->isolation_level), tstatus);
348 for (evlist = tstatus->events; evlist; evlist = evlist->next) {
349 GdaTransactionStatusEvent *ev = (GdaTransactionStatusEvent *) (evlist->data);
350 switch (ev->type) {
351 case GDA_TRANSACTION_STATUS_EVENT_SAVEPOINT:
352 g_print ("%s *SAVE POINT %s (%p)\n", str, ev->pl.svp_name, ev);
353 break;
354 case GDA_TRANSACTION_STATUS_EVENT_SQL:
355 g_print ("%s *SQL %s (%p): %s\n", str, ev->pl.sql, ev,
356 ev->conn_event ? gda_connection_event_get_description (ev->conn_event) : "_no event_");
357 break;
358 case GDA_TRANSACTION_STATUS_EVENT_SUB_TRANSACTION:
359 g_print ("%s *SUB TRANSACTION (%p)\n", str, ev);
360 gda_transaction_status_dump (ev->pl.sub_trans, offset + 5);
361 break;
362 default:
363 g_assert_not_reached ();
364 }
365 }
366
367 g_free (str);
368 }
369 #endif
370