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, main(int argc,char ** argv)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 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 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 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 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 145 browser_virtual_connection_init (BrowserVirtualConnection *bcnc) 146 { 147 bcnc->priv = g_new0 (BrowserVirtualConnectionPrivate, 1); 148 } 149 150 static void 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) { do_test(GdaSqlParser * parser,const xmlChar * id,const xmlChar * sql,const xmlChar * expected,const xmlChar * error_line,const xmlChar * error_col)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 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 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 } create_parser_for_provider(const gchar * prov_name)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 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 * 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 * 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 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 * 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 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 * 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 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