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 (&registering);
123 		if (type == 0)
124 			type = g_type_register_static (PARENT_TYPE, "GdaTransactionStatus", &info, 0);
125 		g_mutex_unlock (&registering);
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