1 /*
2 * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include <string.h>
21 #include <glib/gi18n-lib.h>
22 #include "browser-virtual-connection.h"
23 #include "browser-connection-priv.h"
24 #include <virtual/libgda-virtual.h>
25 #include "../tool-utils.h"
26
27 /*
28 * Main static functions
29 */
30 static void browser_virtual_connection_class_init (BrowserVirtualConnectionClass *klass);
31 static void browser_virtual_connection_init (BrowserVirtualConnection *stmt);
32 static void browser_virtual_connection_dispose (GObject *object);
33 static void browser_virtual_connection_set_property (GObject *object,
34 guint param_id,
35 const GValue *value,
36 GParamSpec *pspec);
37 static void browser_virtual_connection_get_property (GObject *object,
38 guint param_id,
39 GValue *value,
40 GParamSpec *pspec);
41
42 struct _BrowserVirtualConnectionPrivate
43 {
44 GtkTable *layout_table;
45 BrowserVirtualConnectionSpecs *specs;
46 };
47
48 /* get a pointer to the parents to be able to call their destructor */
49 static GObjectClass *parent_class = NULL;
50
51 /* properties */
52 enum {
53 PROP_0,
54 PROP_SPECS
55 };
56
57 GType
browser_virtual_connection_get_type(void)58 browser_virtual_connection_get_type (void)
59 {
60 static GType type = 0;
61
62 if (G_UNLIKELY (type == 0)) {
63 static GMutex registering;
64 static const GTypeInfo info = {
65 sizeof (BrowserVirtualConnectionClass),
66 (GBaseInitFunc) NULL,
67 (GBaseFinalizeFunc) NULL,
68 (GClassInitFunc) browser_virtual_connection_class_init,
69 NULL,
70 NULL,
71 sizeof (BrowserVirtualConnection),
72 0,
73 (GInstanceInitFunc) browser_virtual_connection_init,
74 0
75 };
76
77 g_mutex_lock (®istering);
78 if (type == 0)
79 type = g_type_register_static (BROWSER_TYPE_CONNECTION, "BrowserVirtualConnection", &info, 0);
80 g_mutex_unlock (®istering);
81 }
82 return type;
83 }
84
85 static void
source_cnc_busy_cb(G_GNUC_UNUSED BrowserConnection * bcnc,gboolean is_busy,G_GNUC_UNUSED const gchar * reason,BrowserConnection * virtual)86 source_cnc_busy_cb (G_GNUC_UNUSED BrowserConnection *bcnc, gboolean is_busy,
87 G_GNUC_UNUSED const gchar *reason, BrowserConnection *virtual)
88 {
89 if (browser_connection_is_busy (virtual, NULL) != is_busy)
90 g_signal_emit_by_name (virtual, "busy", is_busy,
91 is_busy ? _("Bound connection is used") : NULL);
92 }
93
94 static void
m_busy(BrowserConnection * bcnc,gboolean is_busy,const gchar * reason)95 m_busy (BrowserConnection *bcnc, gboolean is_busy, const gchar *reason)
96 {
97 /*
98 * declare all the source connections as busy
99 */
100 GSList *list;
101 if (! BROWSER_VIRTUAL_CONNECTION (bcnc)->priv->specs)
102 return;
103
104 for (list = BROWSER_VIRTUAL_CONNECTION (bcnc)->priv->specs->parts; list; list = list->next) {
105 BrowserVirtualConnectionPart *part;
106 part = (BrowserVirtualConnectionPart*) list->data;
107 if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
108 BrowserVirtualConnectionCnc *cnc;
109 cnc = &(part->u.cnc);
110 g_signal_handlers_block_by_func (cnc->source_cnc,
111 G_CALLBACK (source_cnc_busy_cb),
112 bcnc);
113 if (browser_connection_is_busy (cnc->source_cnc, NULL) != is_busy)
114 g_signal_emit_by_name (cnc->source_cnc, "busy", is_busy,
115 is_busy ? _("Virtual connection using this connection is busy") : NULL);
116 g_signal_handlers_unblock_by_func (cnc->source_cnc,
117 G_CALLBACK (source_cnc_busy_cb),
118 bcnc);
119 }
120 }
121
122 BROWSER_CONNECTION_CLASS (parent_class)->busy (bcnc, is_busy, reason);
123 }
124
125 static void
browser_virtual_connection_class_init(BrowserVirtualConnectionClass * klass)126 browser_virtual_connection_class_init (BrowserVirtualConnectionClass * klass)
127 {
128 GObjectClass *object_class = G_OBJECT_CLASS (klass);
129 parent_class = g_type_class_peek_parent (klass);
130
131 BROWSER_CONNECTION_CLASS (klass)->busy = m_busy;
132
133 /* Properties */
134 object_class->set_property = browser_virtual_connection_set_property;
135 object_class->get_property = browser_virtual_connection_get_property;
136 g_object_class_install_property (object_class, PROP_SPECS,
137 g_param_spec_pointer ("specs", NULL,
138 "Specifications as a BrowserVirtualConnectionSpecs pointer",
139 G_PARAM_READABLE | G_PARAM_WRITABLE |
140 G_PARAM_CONSTRUCT_ONLY));
141 object_class->dispose = browser_virtual_connection_dispose;
142 }
143
144 static void
browser_virtual_connection_init(BrowserVirtualConnection * bcnc)145 browser_virtual_connection_init (BrowserVirtualConnection *bcnc)
146 {
147 bcnc->priv = g_new0 (BrowserVirtualConnectionPrivate, 1);
148 }
149
150 static void
browser_virtual_connection_dispose(GObject * object)151 browser_virtual_connection_dispose (GObject *object)
152 {
153 BrowserVirtualConnection *bcnc;
154
155 g_return_if_fail (object != NULL);
156 g_return_if_fail (BROWSER_IS_VIRTUAL_CONNECTION (object));
157
158 bcnc = BROWSER_VIRTUAL_CONNECTION (object);
159
160 if (bcnc->priv) {
161 if (bcnc->priv->specs) {
162 GSList *list;
163 for (list = bcnc->priv->specs->parts; list; list = list->next) {
164 BrowserVirtualConnectionPart *part;
165 part = (BrowserVirtualConnectionPart*) list->data;
166 if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
167 BrowserVirtualConnectionCnc *cnc;
168 cnc = &(part->u.cnc);
169 g_signal_handlers_disconnect_by_func (cnc->source_cnc,
170 G_CALLBACK (source_cnc_busy_cb),
171 bcnc);
172 }
173 }
174 browser_virtual_connection_specs_free (bcnc->priv->specs);
175 }
176
177 g_free (bcnc->priv);
178 bcnc->priv = NULL;
179 }
180
181 /* parent class */
182 parent_class->dispose (object);
183 }
184
185 static void
browser_virtual_connection_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)186 browser_virtual_connection_set_property (GObject *object,
187 guint param_id,
188 const GValue *value,
189 GParamSpec *pspec)
190 {
191 BrowserVirtualConnection *bcnc;
192
193 bcnc = BROWSER_VIRTUAL_CONNECTION (object);
194 if (bcnc->priv) {
195 switch (param_id) {
196 case PROP_SPECS:
197 bcnc->priv->specs = browser_virtual_connection_specs_copy (g_value_get_pointer (value));
198 GSList *list;
199 for (list = bcnc->priv->specs->parts; list; list = list->next) {
200 BrowserVirtualConnectionPart *part;
201 part = (BrowserVirtualConnectionPart*) list->data;
202 if (part->part_type == BROWSER_VIRTUAL_CONNECTION_PART_CNC) {
203 BrowserVirtualConnectionCnc *cnc;
204 cnc = &(part->u.cnc);
205 g_signal_connect (cnc->source_cnc, "busy",
206 G_CALLBACK (source_cnc_busy_cb), bcnc);
207 }
208 }
209
210 break;
211 default:
212 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
213 break;
214 }
215 }
216 }
217
218 static void
browser_virtual_connection_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)219 browser_virtual_connection_get_property (GObject *object,
220 guint param_id,
221 GValue *value,
222 GParamSpec *pspec)
223 {
224 BrowserVirtualConnection *bcnc;
225
226 bcnc = BROWSER_VIRTUAL_CONNECTION (object);
227 if (bcnc->priv) {
228 switch (param_id) {
229 case PROP_SPECS:
230 g_value_set_pointer (value, bcnc->priv->specs);
231 break;
232 default:
233 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
234 break;
235 }
236 }
237 }
238
239
240
241 typedef struct {
242 guint cncid;
243 GMainLoop *loop;
244 GError **error;
245 GdaThreadWrapper *wrapper;
246
247 /* out */
248 GdaConnection *cnc;
249 } MainloopData;
250
251 static gboolean
check_for_cnc(MainloopData * data)252 check_for_cnc (MainloopData *data)
253 {
254 GdaConnection *cnc;
255 GError *lerror = NULL;
256 cnc = gda_thread_wrapper_fetch_result (data->wrapper, FALSE, data->cncid, &lerror);
257 if (cnc || (!cnc && lerror)) {
258 /* waiting is finished! */
259 data->cnc = cnc;
260 if (lerror)
261 g_propagate_error (data->error, lerror);
262 g_main_loop_quit (data->loop);
263 return FALSE;
264 }
265 return TRUE;
266 }
267
268 /*
269 * executed in a sub thread
270 */
271 static GdaConnection *
sub_thread_open_cnc(BrowserVirtualConnectionSpecs * specs,GError ** error)272 sub_thread_open_cnc (BrowserVirtualConnectionSpecs *specs, GError **error)
273 {
274 #ifndef DUMMY
275 /* create GDA virtual connection */
276 static GdaVirtualProvider *provider = NULL;
277 GdaConnection *virtual;
278 if (!provider)
279 provider = gda_vprovider_hub_new ();
280
281 virtual = gda_virtual_connection_open_extended (provider, GDA_CONNECTION_OPTIONS_THREAD_SAFE |
282 GDA_CONNECTION_OPTIONS_AUTO_META_DATA, NULL);
283
284 /* add parts to connection as specified by @specs */
285 GSList *list;
286 for (list = specs->parts; list; list = list->next) {
287 BrowserVirtualConnectionPart *part;
288 part = (BrowserVirtualConnectionPart*) list->data;
289 switch (part->part_type) {
290 case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
291 BrowserVirtualConnectionModel *pm;
292 pm = &(part->u.model);
293 if (! gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (virtual),
294 pm->model, pm->table_name, error)) {
295 g_object_unref (virtual);
296 return NULL;
297 }
298 break;
299 }
300 case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
301 BrowserVirtualConnectionCnc *scnc;
302 scnc = &(part->u.cnc);
303 if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (virtual),
304 scnc->source_cnc->priv->cnc,
305 scnc->table_schema, error)) {
306 g_object_unref (virtual);
307 return NULL;
308 }
309 break;
310 }
311 default:
312 g_assert_not_reached ();
313 }
314 }
315
316 return virtual;
317 #else /* DUMMY defined */
318 sleep (5);
319 g_set_error (error, GDA_TOOLS_ERROR, TOOLS_INTERNAL_COMMAND_ERROR,
320 "%s", "Timeout!!!");
321 return NULL;
322 #endif
323 }
324
325 /**
326 * browser_virtual_connection_new
327 * @specs: the specifications of the virtual connection's contents
328 * @error: a place to store errors, or %NULL
329 *
330 * Creates a new #BrowserVirtualConnection connection.
331 *
332 * Returns: the new connection
333 */
334 BrowserConnection *
browser_virtual_connection_new(const BrowserVirtualConnectionSpecs * specs,GError ** error)335 browser_virtual_connection_new (const BrowserVirtualConnectionSpecs *specs, GError **error)
336 {
337 /* open virtual GdaConnection in sub thread */
338 GdaThreadWrapper *wrapper;
339 guint cncid;
340
341 g_return_val_if_fail (specs, NULL);
342
343 wrapper = gda_thread_wrapper_new ();
344 cncid = gda_thread_wrapper_execute (wrapper,
345 (GdaThreadWrapperFunc) sub_thread_open_cnc,
346 (gpointer) specs,
347 (GDestroyNotify) NULL,
348 error);
349 if (cncid == 0)
350 return NULL;
351
352 GMainLoop *loop;
353 MainloopData data;
354
355 loop = g_main_loop_new (NULL, FALSE);
356 data.wrapper = wrapper;
357 data.cncid = cncid;
358 data.error = error;
359 data.loop = loop;
360 data.cnc = NULL;
361
362 g_timeout_add (200, (GSourceFunc) check_for_cnc, &data);
363 g_main_loop_run (loop);
364 g_main_loop_unref (loop);
365 g_object_unref (wrapper);
366
367 /* create the BrowserConnection object */
368 if (data.cnc) {
369 BrowserConnection *nbcnc;
370 g_object_set (data.cnc, "monitor-wrapped-in-mainloop", TRUE, NULL);
371 nbcnc = g_object_new (BROWSER_TYPE_VIRTUAL_CONNECTION, "specs", specs,
372 "gda-connection", data.cnc, NULL);
373 g_object_unref (data.cnc);
374 /*g_print ("BrowserVirtualConnection %p had TMP wrapper %p\n", nbcnc, wrapper);*/
375
376 return nbcnc;
377 }
378 else
379 return NULL;
380 }
381
382 /**
383 * browser_virtual_connection_modify_specs
384 * @bcnc: a #BrowserVirtualConnection connection
385 * @new_specs: a pointer to a #BrowserVirtualConnectionSpecs for the new specs
386 * @error: a place to store errors, or %NULL
387 *
388 * Returns: %TRUE if no error occurred
389 */
390 gboolean
browser_virtual_connection_modify_specs(BrowserVirtualConnection * bcnc,const BrowserVirtualConnectionSpecs * new_specs,GError ** error)391 browser_virtual_connection_modify_specs (BrowserVirtualConnection *bcnc,
392 const BrowserVirtualConnectionSpecs *new_specs, GError **error)
393 {
394 g_return_val_if_fail (BROWSER_IS_VIRTUAL_CONNECTION (bcnc), FALSE);
395 g_return_val_if_fail (new_specs, FALSE);
396
397 /* undo the current specs */
398 GSList *list;
399 GdaConnection *cnc;
400 cnc = g_object_get_data (G_OBJECT (BROWSER_CONNECTION (bcnc)->priv->cnc), "gda-virtual-connection");
401 for (list = bcnc->priv->specs->parts; list; list = bcnc->priv->specs->parts) {
402 BrowserVirtualConnectionPart *part;
403 part = (BrowserVirtualConnectionPart*) list->data;
404 switch (part->part_type) {
405 case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
406 BrowserVirtualConnectionModel *pm;
407 pm = &(part->u.model);
408 if (! gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (cnc),
409 pm->table_name, error))
410 return FALSE;
411 break;
412 }
413 case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
414 BrowserVirtualConnectionCnc *scnc;
415 scnc = &(part->u.cnc);
416 if (!gda_vconnection_hub_remove (GDA_VCONNECTION_HUB (cnc), scnc->source_cnc->priv->cnc,
417 error))
418 return FALSE;
419 break;
420 }
421 default:
422 g_assert_not_reached ();
423 }
424
425 /* get rid of @part */
426 browser_virtual_connection_part_free (part);
427 bcnc->priv->specs->parts = g_slist_remove (bcnc->priv->specs->parts, part);
428 }
429
430 /* apply the new specs */
431 browser_virtual_connection_specs_free (bcnc->priv->specs);
432 bcnc->priv->specs = g_new0 (BrowserVirtualConnectionSpecs, 1);
433
434 for (list = new_specs->parts; list; list = list->next) {
435 BrowserVirtualConnectionPart *part;
436 part = (BrowserVirtualConnectionPart*) list->data;
437 switch (part->part_type) {
438 case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
439 BrowserVirtualConnectionModel *pm;
440 pm = &(part->u.model);
441 if (! gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (cnc),
442 pm->model, pm->table_name, error))
443 return FALSE;
444 break;
445 }
446 case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
447 BrowserVirtualConnectionCnc *scnc;
448 scnc = &(part->u.cnc);
449 if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (cnc), scnc->source_cnc->priv->cnc,
450 scnc->table_schema, error))
451 return FALSE;
452 break;
453 }
454 default:
455 g_assert_not_reached ();
456 }
457
458 /* add @part to bcnc->priv->specs */
459 bcnc->priv->specs->parts = g_slist_append (bcnc->priv->specs->parts,
460 browser_virtual_connection_part_copy (part));
461 }
462
463 return TRUE;
464 }
465
466
467 /*
468 * Spec manipulations
469 */
470
471 /**
472 * browser_virtual_connection_part_copy
473 */
474 BrowserVirtualConnectionPart *
browser_virtual_connection_part_copy(const BrowserVirtualConnectionPart * part)475 browser_virtual_connection_part_copy (const BrowserVirtualConnectionPart *part)
476 {
477 BrowserVirtualConnectionPart *npart;
478 g_return_val_if_fail (part, NULL);
479
480 npart = g_new0 (BrowserVirtualConnectionPart, 1);
481 npart->part_type = part->part_type;
482
483 switch (part->part_type) {
484 case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
485 const BrowserVirtualConnectionModel *spm;
486 BrowserVirtualConnectionModel *npm;
487 spm = &(part->u.model);
488 npm = &(npart->u.model);
489 if (spm->table_name)
490 npm->table_name = g_strdup (spm->table_name);
491 if (spm->model)
492 npm->model = g_object_ref (G_OBJECT (spm->model));
493 break;
494 }
495 case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
496 const BrowserVirtualConnectionCnc *scnc;
497 BrowserVirtualConnectionCnc *ncnc;
498 scnc = &(part->u.cnc);
499 ncnc = &(npart->u.cnc);
500 if (scnc->table_schema)
501 ncnc->table_schema = g_strdup (scnc->table_schema);
502 if (scnc->source_cnc)
503 ncnc->source_cnc = g_object_ref (G_OBJECT (scnc->source_cnc));
504 break;
505 }
506 default:
507 g_assert_not_reached ();
508 }
509
510 return npart;
511 }
512
513 /**
514 * browser_virtual_connection_part_free
515 */
516 void
browser_virtual_connection_part_free(BrowserVirtualConnectionPart * part)517 browser_virtual_connection_part_free (BrowserVirtualConnectionPart *part)
518 {
519 if (!part)
520 return;
521
522 switch (part->part_type) {
523 case BROWSER_VIRTUAL_CONNECTION_PART_MODEL: {
524 BrowserVirtualConnectionModel *pm;
525 pm = &(part->u.model);
526 g_free (pm->table_name);
527 if (pm->model)
528 g_object_unref (pm->model);
529 break;
530 }
531 case BROWSER_VIRTUAL_CONNECTION_PART_CNC: {
532 BrowserVirtualConnectionCnc *cnc;
533 cnc = &(part->u.cnc);
534 g_free (cnc->table_schema);
535 if (cnc->source_cnc)
536 g_object_unref (cnc->source_cnc);
537 break;
538 }
539 default:
540 g_assert_not_reached ();
541 }
542 }
543
544 /**
545 * browser_virtual_connection_specs_copy
546 */
547 BrowserVirtualConnectionSpecs *
browser_virtual_connection_specs_copy(const BrowserVirtualConnectionSpecs * specs)548 browser_virtual_connection_specs_copy (const BrowserVirtualConnectionSpecs *specs)
549 {
550 BrowserVirtualConnectionSpecs *ns;
551 GSList *list;
552
553 g_return_val_if_fail (specs, NULL);
554
555 ns = g_new0 (BrowserVirtualConnectionSpecs, 1);
556 for (list = specs->parts; list; list = list->next) {
557 BrowserVirtualConnectionPart *npart;
558 npart = browser_virtual_connection_part_copy ((BrowserVirtualConnectionPart*) list->data);
559 ns->parts = g_slist_prepend (ns->parts, npart);
560 }
561 ns->parts = g_slist_reverse (ns->parts);
562
563 return ns;
564 }
565
566 /**
567 * browser_virtual_connection_specs_free
568 */
569 void
browser_virtual_connection_specs_free(BrowserVirtualConnectionSpecs * specs)570 browser_virtual_connection_specs_free (BrowserVirtualConnectionSpecs *specs)
571 {
572 if (!specs)
573 return;
574 g_slist_foreach (specs->parts, (GFunc) browser_virtual_connection_part_free, NULL);
575 g_slist_free (specs->parts);
576 g_free (specs);
577 }
578