1 /* 2 * Copyright (C) 2007 - 2012 Vivien Malerba <malerba@gnome-db.org> 3 * Copyright (C) 2008 - 2011 Murray Cumming <murrayc@murrayc.com> 4 * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com> 5 * Copyright (C) 2010 David King <davidk@openismus.com> 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 <glib/gi18n-lib.h> 24 #include <string.h> 25 #include "gda-vconnection-hub.h" 26 #include "gda-virtual-provider.h" 27 #include <sql-parser/gda-sql-parser.h> 28 #include <libgda/gda-util.h> 29 #include <libgda/gda-data-select.h> 30 #include <gda-sql-builder.h> 31 #include "../gda-sqlite.h" 32 33 typedef struct { 34 GdaVconnectionHub *hub; 35 GdaConnection *cnc; 36 gchar *ns; /* can be NULL in one case only among all the HubConnection structs */ 37 } HubConnection; 38 39 static void hub_connection_free (HubConnection *hc); 40 static gboolean attach_hub_connection (GdaVconnectionHub *hub, HubConnection *hc, GError **error); 41 static void detach_hub_connection (GdaVconnectionHub *hub, HubConnection *hc); 42 43 static GdaSqlParser *internal_parser; 44 45 struct _GdaVconnectionHubPrivate { 46 GSList *hub_connections; /* list of HubConnection structures */ 47 }; 48 49 static void gda_vconnection_hub_class_init (GdaVconnectionHubClass *klass); 50 static void gda_vconnection_hub_init (GdaVconnectionHub *cnc, GdaVconnectionHubClass *klass); 51 static void gda_vconnection_hub_dispose (GObject *object); 52 static GObjectClass *parent_class = NULL; 53 54 static HubConnection *get_hub_cnc_by_ns (GdaVconnectionHub *hub, const gchar *ns); 55 static HubConnection *get_hub_cnc_by_cnc (GdaVconnectionHub *hub, GdaConnection *cnc); 56 57 /* 58 * GdaVconnectionHub class implementation 59 */ 60 static void 61 gda_vconnection_hub_class_init (GdaVconnectionHubClass *klass) 62 { 63 GObjectClass *object_class = G_OBJECT_CLASS (klass); 64 65 parent_class = g_type_class_peek_parent (klass); 66 67 object_class->dispose = gda_vconnection_hub_dispose; 68 69 /* static objects */ 70 internal_parser = gda_sql_parser_new (); 71 } 72 73 static void 74 gda_vconnection_hub_init (GdaVconnectionHub *cnc, G_GNUC_UNUSED GdaVconnectionHubClass *klass) 75 { 76 cnc->priv = g_new (GdaVconnectionHubPrivate, 1); 77 cnc->priv->hub_connections = NULL; 78 } 79 80 static void 81 gda_vconnection_hub_dispose (GObject *object) 82 { 83 GdaVconnectionHub *cnc = (GdaVconnectionHub *) object; 84 85 g_return_if_fail (GDA_IS_VCONNECTION_HUB (cnc)); 86 87 /* free memory */ 88 if (cnc->priv) { 89 gda_connection_close_no_warning ((GdaConnection *) cnc); 90 g_assert (!cnc->priv->hub_connections); 91 92 g_free (cnc->priv); 93 cnc->priv = NULL; 94 } 95 96 /* chain to parent class */ 97 parent_class->dispose (object); 98 } 99 100 GType 101 gda_vconnection_hub_get_type (void) 102 { 103 static GType type = 0; 104 105 if (G_UNLIKELY (type == 0)) { 106 static GMutex registering; 107 if (type == 0) { 108 static GTypeInfo info = { 109 sizeof (GdaVconnectionHubClass), 110 (GBaseInitFunc) NULL, 111 (GBaseFinalizeFunc) NULL, 112 (GClassInitFunc) gda_vconnection_hub_class_init, 113 NULL, NULL, 114 sizeof (GdaVconnectionHub), 115 0, 116 (GInstanceInitFunc) gda_vconnection_hub_init, 117 0 118 }; 119 120 g_mutex_lock (®istering); 121 if (type == 0) 122 type = g_type_register_static (GDA_TYPE_VCONNECTION_DATA_MODEL, "GdaVconnectionHub", &info, 0); 123 g_mutex_unlock (®istering); 124 } 125 } 126 127 return type; 128 } 129 130 /** 131 * gda_vconnection_hub_add: 132 * @hub: a #GdaVconnectionHub connection 133 * @cnc: a #GdaConnection 134 * @ns: (allow-none): a namespace, or %NULL 135 * @error: a place to store errors, or %NULL 136 * 137 * Make all the tables of @cnc appear as tables (of the same name) in the @hub connection. 138 * If the @ns is not %NULL, then within @hub, the tables will be accessible using the '@ns.@table_name' 139 * notation. 140 * 141 * Within any instance of @hub, there can be only one added connection where @ns is %NULL. 142 * 143 * Returns: TRUE if no error occurred 144 */ 145 gboolean 146 gda_vconnection_hub_add (GdaVconnectionHub *hub, 147 GdaConnection *cnc, const gchar *ns, GError **error) 148 { 149 HubConnection *hc; 150 151 g_return_val_if_fail (GDA_IS_VCONNECTION_HUB (hub), FALSE); 152 g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE); 153 154 /* check for constraints */ 155 hc = get_hub_cnc_by_ns (hub, ns); 156 if (hc && (hc->cnc != cnc)) { 157 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 158 GDA_SERVER_PROVIDER_MISUSE_ERROR, 159 "%s", _("Namespace must be specified")); 160 return FALSE; 161 } 162 163 if (hc) 164 return TRUE; 165 166 if (!gda_connection_is_opened (cnc)) { 167 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 168 GDA_SERVER_PROVIDER_MISUSE_ERROR, 169 "%s", _("Connection is closed")); 170 return FALSE; 171 } 172 173 /* actually adding @cnc */ 174 hc = g_new (HubConnection, 1); 175 hc->hub = hub; 176 hc->cnc = cnc; 177 g_object_ref (cnc); 178 hc->ns = ns ? g_strdup (ns) : NULL; 179 180 if (!attach_hub_connection (hub, hc, error)) { 181 hub_connection_free (hc); 182 return FALSE; 183 } 184 185 return TRUE; 186 } 187 188 /** 189 * gda_vconnection_hub_remove: 190 * @hub: a #GdaVconnectionHub connection 191 * @cnc: a #GdaConnection 192 * @error: a place to store errors, or %NULL 193 * 194 * Remove all the tables in @hub representing @cnc's tables. 195 * 196 * Returns: TRUE if no error occurred 197 */ 198 gboolean 199 gda_vconnection_hub_remove (GdaVconnectionHub *hub, GdaConnection *cnc, GError **error) 200 { 201 HubConnection *hc; 202 203 g_return_val_if_fail (GDA_IS_VCONNECTION_HUB (hub), FALSE); 204 g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE); 205 206 hc = get_hub_cnc_by_cnc (hub, cnc); 207 208 if (!hc) { 209 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 210 GDA_SERVER_PROVIDER_MISUSE_ERROR, 211 "%s", _("Connection was not represented in hub")); 212 return FALSE; 213 } 214 215 /* clean the hub->priv->hub_connections list */ 216 detach_hub_connection (hub, hc); 217 return TRUE; 218 } 219 220 static HubConnection* 221 get_hub_cnc_by_ns (GdaVconnectionHub *hub, const gchar *ns) 222 { 223 GSList *list; 224 for (list = hub->priv->hub_connections; list; list = list->next) { 225 if ((!ns && !((HubConnection*) list->data)->ns)|| 226 (ns && ((HubConnection*) list->data)->ns && !strcmp (((HubConnection*) list->data)->ns, ns))) 227 return (HubConnection*) list->data; 228 } 229 return NULL; 230 } 231 232 static HubConnection * 233 get_hub_cnc_by_cnc (GdaVconnectionHub *hub, GdaConnection *cnc) 234 { 235 GSList *list; 236 for (list = hub->priv->hub_connections; list; list = list->next) { 237 if (((HubConnection*) list->data)->cnc == cnc) 238 return (HubConnection*) list->data; 239 } 240 return NULL; 241 } 242 243 /** 244 * gda_vconnection_hub_get_connection: 245 * @hub: a #GdaVconnectionHub connection 246 * @ns: (allow-none): a name space, or %NULL 247 * 248 * Find the #GdaConnection object in @hub associated to the @ns name space 249 * 250 * Returns: the #GdaConnection, or %NULL if no connection is associated to @ns 251 */ 252 GdaConnection * 253 gda_vconnection_hub_get_connection (GdaVconnectionHub *hub, const gchar *ns) 254 { 255 HubConnection *hc; 256 g_return_val_if_fail (GDA_IS_VCONNECTION_HUB (hub), NULL); 257 g_return_val_if_fail (hub->priv, NULL); 258 259 hc = get_hub_cnc_by_ns (hub, ns); 260 if (hc) 261 return hc->cnc; 262 else 263 return NULL; 264 } 265 266 /** 267 * gda_vconnection_hub_foreach: 268 * @hub: a #GdaVconnectionHub connection 269 * @func: a #GdaVconnectionDataModelFunc function pointer 270 * @data: data to pass to @func calls 271 * 272 * Call @func for each #GdaConnection represented in @hub. 273 */ 274 void 275 gda_vconnection_hub_foreach (GdaVconnectionHub *hub, 276 GdaVConnectionHubFunc func, gpointer data) 277 { 278 GSList *list, *next; 279 g_return_if_fail (GDA_IS_VCONNECTION_HUB (hub)); 280 g_return_if_fail (hub->priv); 281 282 if (!func) 283 return; 284 285 list = hub->priv->hub_connections; 286 while (list) { 287 HubConnection *hc = (HubConnection*) list->data; 288 next = list->next; 289 func (hc->cnc, hc->ns, data); 290 list = next; 291 } 292 } 293 294 static void meta_changed_cb (GdaMetaStore *store, GSList *changes, HubConnection *hc); 295 296 typedef struct { 297 GdaVconnectionDataModelSpec spec; 298 GValue *table_name; 299 HubConnection *hc; 300 301 GError *cols_error; 302 gint ncols; 303 gchar **col_names; /* free using g_strfreev */ 304 GType *col_gtypes;/* free using g_free */ 305 gchar **col_dtypes;/* free using g_strfreev */ 306 307 GHashTable *filters_hash; /* key = string; value = a ComputedFilter pointer */ 308 } LocalSpec; 309 310 static void local_spec_free (LocalSpec *spec) 311 { 312 gda_value_free (spec->table_name); 313 if (spec->col_names) 314 g_strfreev (spec->col_names); 315 if (spec->col_gtypes) 316 g_free (spec->col_gtypes); 317 if (spec->col_dtypes) 318 g_strfreev (spec->col_dtypes); 319 g_clear_error (&(spec->cols_error)); 320 if (spec->filters_hash) 321 g_hash_table_destroy (spec->filters_hash); 322 g_free (spec); 323 } 324 325 static void 326 compute_column_specs (GdaVconnectionDataModelSpec *spec) 327 { 328 LocalSpec *lspec = (LocalSpec *) spec; 329 gint i, nrows; 330 GdaDataModel *model; 331 332 if (lspec->col_names) 333 return; 334 335 model = gda_connection_get_meta_store_data (lspec->hc->cnc, 336 GDA_CONNECTION_META_FIELDS, NULL, 1, "name", lspec->table_name); 337 if (!model) 338 return; 339 340 nrows = gda_data_model_get_n_rows (model); 341 lspec->col_names = g_new0 (gchar *, nrows+1); 342 lspec->col_gtypes = g_new0 (GType, nrows); 343 lspec->col_dtypes = g_new0 (gchar *, nrows+1); 344 345 for (i = 0; i < nrows; i++) { 346 const GValue *v0, *v1, *v2; 347 348 v0 = gda_data_model_get_value_at (model, 0, i, NULL); 349 v1 = gda_data_model_get_value_at (model, 1, i, NULL); 350 v2 = gda_data_model_get_value_at (model, 2, i, NULL); 351 352 if (!v0 || !v1 || !v2) { 353 break; 354 } 355 356 lspec->col_names[i] = g_value_dup_string (v0); 357 lspec->col_gtypes[i] = gda_g_type_from_string (g_value_get_string (v2)); 358 if (lspec->col_gtypes[i] == G_TYPE_INVALID) 359 lspec->col_gtypes[i] = GDA_TYPE_NULL; 360 lspec->col_dtypes[i] = g_value_dup_string (v1); 361 } 362 g_object_unref (model); 363 364 if (i != nrows) { 365 /* there has been an error */ 366 g_strfreev (lspec->col_names); 367 lspec->col_names = NULL; 368 g_free (lspec->col_gtypes); 369 lspec->col_gtypes = NULL; 370 g_strfreev (lspec->col_dtypes); 371 lspec->col_dtypes = NULL; 372 g_set_error (&(lspec->cols_error), GDA_META_STORE_ERROR, GDA_META_STORE_INTERNAL_ERROR, 373 _("Unable to get information about table '%s'"), 374 g_value_get_string (lspec->table_name)); 375 } 376 else 377 lspec->ncols = nrows; 378 } 379 380 static GList * 381 dict_table_create_columns_func (GdaVconnectionDataModelSpec *spec, GError **error) 382 { 383 LocalSpec *lspec = (LocalSpec *) spec; 384 GList *columns = NULL; 385 gint i; 386 387 compute_column_specs (spec); 388 if (lspec->cols_error) { 389 if (error) 390 *error = g_error_copy (lspec->cols_error); 391 return NULL; 392 } 393 394 for (i = 0; i < lspec->ncols; i++) { 395 GdaColumn *col; 396 397 col = gda_column_new (); 398 gda_column_set_name (col, lspec->col_names[i]); 399 gda_column_set_g_type (col, lspec->col_gtypes[i]); 400 gda_column_set_dbms_type (col, lspec->col_dtypes[i]); 401 columns = g_list_prepend (columns, col); 402 } 403 404 return g_list_reverse (columns); 405 } 406 407 static gchar * 408 make_string_for_filter (GdaVconnectionDataModelFilter *info) 409 { 410 GString *string; 411 gint i; 412 413 string = g_string_new (""); 414 for (i = 0; i < info->nConstraint; i++) { 415 const struct GdaVirtualConstraint *cons; 416 cons = &(info->aConstraint [i]); 417 g_string_append_printf (string, "|%d,%d", cons->iColumn, cons->op); 418 } 419 g_string_append_c (string, '/'); 420 for (i = 0; i < info->nOrderBy; i++) { 421 struct GdaVirtualOrderby *order; 422 order = &(info->aOrderBy[i]); 423 g_string_append_printf (string, "|%d,%d", order->iColumn, order->desc ? 1 : 0); 424 } 425 return g_string_free (string, FALSE); 426 } 427 428 typedef struct { 429 GdaStatement *stmt; 430 int orderByConsumed; 431 struct GdaVirtualConstraintUsage *out_const; 432 } ComputedFilter; 433 434 static void 435 computed_filter_free (ComputedFilter *filter) 436 { 437 g_object_unref (filter->stmt); 438 g_free (filter->out_const); 439 g_free (filter); 440 } 441 442 static void 443 dict_table_create_filter (GdaVconnectionDataModelSpec *spec, GdaVconnectionDataModelFilter *info) 444 { 445 LocalSpec *lspec = (LocalSpec *) spec; 446 GdaSqlBuilder *b; 447 gint i; 448 gchar *hash; 449 450 compute_column_specs (spec); 451 if (lspec->cols_error) 452 return; 453 454 hash = make_string_for_filter (info); 455 if (lspec->filters_hash) { 456 ComputedFilter *filter; 457 filter = g_hash_table_lookup (lspec->filters_hash, hash); 458 if (filter) { 459 info->idxPointer = filter->stmt; 460 info->orderByConsumed = filter->orderByConsumed; 461 memcpy (info->aConstraintUsage, 462 filter->out_const, 463 sizeof (struct GdaVirtualConstraintUsage) * info->nConstraint); 464 /*g_print ("Reusing filter %p, hash=[%s]\n", filter, hash);*/ 465 g_free (hash); 466 return; 467 } 468 } 469 470 /* SELECT core */ 471 b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT); 472 for (i = 0; i < lspec->ncols; i++) 473 gda_sql_builder_select_add_field (b, lspec->col_names[i], NULL, NULL); 474 gda_sql_builder_select_add_target_id (b, 475 gda_sql_builder_add_id (b, g_value_get_string (lspec->table_name)), 476 NULL); 477 478 /* WHERE part */ 479 gint argpos; 480 GdaSqlBuilderId *op_ids; 481 op_ids = g_new (GdaSqlBuilderId, info->nConstraint); 482 for (i = 0, argpos = 0; i < info->nConstraint; i++) { 483 const struct GdaVirtualConstraint *cons; 484 cons = &(info->aConstraint [i]); 485 if (cons->iColumn >= lspec->ncols) { 486 g_warning ("Internal error: column known by SQLite's virtual table %d is not known for " 487 "table '%s', which has %d column(s)", cons->iColumn, 488 g_value_get_string (lspec->table_name), lspec->ncols); 489 continue; 490 } 491 492 GdaSqlBuilderId fid, pid, eid; 493 gchar *pname; 494 495 if (lspec->col_gtypes[cons->iColumn] == GDA_TYPE_BLOB) /* ignore BLOBs */ 496 continue; 497 498 fid = gda_sql_builder_add_id (b, lspec->col_names[cons->iColumn]); 499 pname = g_strdup_printf ("param%d", argpos); 500 pid = gda_sql_builder_add_param (b, pname, lspec->col_gtypes[cons->iColumn], TRUE); 501 g_free (pname); 502 eid = gda_sql_builder_add_cond (b, cons->op, fid, pid, 0); 503 op_ids[argpos] = eid; 504 505 /* update info->aConstraintUsage */ 506 info->aConstraintUsage [i].argvIndex = argpos+1; 507 info->aConstraintUsage [i].omit = 1; 508 argpos++; 509 } 510 if (argpos > 0) { 511 GdaSqlBuilderId whid; 512 whid = gda_sql_builder_add_cond_v (b, GDA_SQL_OPERATOR_TYPE_AND, op_ids, argpos); 513 gda_sql_builder_set_where (b, whid); 514 } 515 g_free (op_ids); 516 517 /* ORDER BY part */ 518 info->orderByConsumed = FALSE; 519 for (i = 0; i < info->nOrderBy; i++) { 520 struct GdaVirtualOrderby *ao; 521 GdaSqlBuilderId fid; 522 info->orderByConsumed = TRUE; 523 ao = &(info->aOrderBy [i]); 524 if (ao->iColumn >= lspec->ncols) { 525 g_warning ("Internal error: column known by SQLite's virtual table %d is not known for " 526 "table '%s', which has %d column(s)", ao->iColumn, 527 g_value_get_string (lspec->table_name), lspec->ncols); 528 info->orderByConsumed = FALSE; 529 continue; 530 } 531 fid = gda_sql_builder_add_id (b, lspec->col_names[ao->iColumn]); 532 gda_sql_builder_select_order_by (b, fid, ao->desc ? FALSE : TRUE, NULL); 533 } 534 535 GdaStatement *stmt; 536 stmt = gda_sql_builder_get_statement (b, NULL); 537 g_object_unref (b); 538 if (stmt) { 539 ComputedFilter *filter; 540 541 filter = g_new0 (ComputedFilter, 1); 542 filter->stmt = stmt; 543 filter->orderByConsumed = info->orderByConsumed; 544 filter->out_const = g_new (struct GdaVirtualConstraintUsage, info->nConstraint); 545 memcpy (filter->out_const, 546 info->aConstraintUsage, 547 sizeof (struct GdaVirtualConstraintUsage) * info->nConstraint); 548 549 gchar *sql; 550 sql = gda_statement_to_sql (stmt, NULL, NULL); 551 /*g_print ("Filter %p for [%s] hash=[%s]\n", filter, sql, hash);*/ 552 g_free (sql); 553 554 if (! lspec->filters_hash) 555 lspec->filters_hash = g_hash_table_new_full (g_str_hash, g_str_equal, 556 g_free, 557 (GDestroyNotify) computed_filter_free); 558 559 g_hash_table_insert (lspec->filters_hash, hash, filter); 560 info->idxPointer = filter->stmt; 561 /*g_print ("There are now %d statements in store...\n", g_hash_table_size (lspec->filters_hash));*/ 562 } 563 else { 564 for (i = 0, argpos = 0; i < info->nConstraint; i++) { 565 info->aConstraintUsage[i].argvIndex = 0; 566 info->aConstraintUsage[i].omit = FALSE; 567 } 568 info->idxPointer = NULL; 569 info->orderByConsumed = FALSE; 570 g_free (hash); 571 } 572 } 573 574 575 /* 576 * Takes as input #GValue created when calling spec->create_filtered_model_func() 577 * and creates a GValue with the correct requested type 578 */ 579 static GValue * 580 create_value_from_sqlite3_gvalue (GType type, GValue *svalue, GError **error) 581 { 582 GValue *value; 583 gboolean allok = TRUE; 584 value = g_new0 (GValue, 1); 585 586 g_value_init (value, type); 587 588 if (type == GDA_TYPE_NULL) 589 ; 590 else if (type == G_TYPE_INT) { 591 if (G_VALUE_TYPE (svalue) != G_TYPE_INT64) 592 allok = FALSE; 593 else { 594 gint64 i; 595 i = g_value_get_int64 (svalue); 596 if ((i > G_MAXINT) || (i < G_MININT)) { 597 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 598 GDA_SERVER_PROVIDER_DATA_ERROR, 599 "%s", _("Integer value is out of bounds")); 600 allok = FALSE; 601 } 602 else 603 g_value_set_int (value, (gint) i); 604 } 605 } 606 else if (type == G_TYPE_UINT) { 607 if (G_VALUE_TYPE (svalue) != G_TYPE_INT64) 608 allok = FALSE; 609 else { 610 gint64 i; 611 i = g_value_get_int64 (svalue); 612 if ((i < 0) || (i > G_MAXUINT)) { 613 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 614 GDA_SERVER_PROVIDER_DATA_ERROR, 615 "%s", _("Integer value is out of bounds")); 616 allok = FALSE; 617 } 618 else 619 g_value_set_uint (value, (guint) i); 620 } 621 } 622 else if (type == G_TYPE_INT64) { 623 if (G_VALUE_TYPE (svalue) != G_TYPE_INT64) 624 allok = FALSE; 625 else 626 g_value_set_int64 (value, g_value_get_int64 (svalue)); 627 } 628 else if (type == G_TYPE_UINT64) { 629 if (G_VALUE_TYPE (svalue) != G_TYPE_INT64) 630 allok = FALSE; 631 else 632 g_value_set_uint64 (value, (guint64) g_value_get_int64 (svalue)); 633 } 634 else if (type == G_TYPE_DOUBLE) { 635 if (G_VALUE_TYPE (svalue) != G_TYPE_DOUBLE) 636 allok = FALSE; 637 else 638 g_value_set_double (value, g_value_get_double (svalue)); 639 } 640 else if (type == G_TYPE_STRING) { 641 if (G_VALUE_TYPE (svalue) != G_TYPE_STRING) 642 allok = FALSE; 643 else 644 g_value_set_string (value, g_value_get_string (svalue)); 645 } 646 else if (type == GDA_TYPE_BINARY) { 647 if (G_VALUE_TYPE (svalue) != GDA_TYPE_BINARY) 648 allok = FALSE; 649 else 650 gda_value_set_binary (value, gda_value_get_binary (svalue)); 651 } 652 else if (type == GDA_TYPE_BLOB) { 653 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 654 GDA_SERVER_PROVIDER_DATA_ERROR, 655 "%s", _("Blob constraints are not handled in virtual table condition")); 656 allok = FALSE; 657 } 658 else if (type == G_TYPE_BOOLEAN) { 659 if (G_VALUE_TYPE (svalue) != G_TYPE_INT64) 660 allok = FALSE; 661 else 662 g_value_set_boolean (value, g_value_get_int64 (svalue) == 0 ? FALSE : TRUE); 663 } 664 else if (type == G_TYPE_DATE) { 665 if (G_VALUE_TYPE (svalue) != G_TYPE_STRING) 666 allok = FALSE; 667 else { 668 GDate date; 669 if (!gda_parse_iso8601_date (&date, g_value_get_string (svalue))) { 670 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 671 GDA_SERVER_PROVIDER_DATA_ERROR, 672 _("Invalid date '%s' (date format should be YYYY-MM-DD)"), 673 g_value_get_string (svalue)); 674 allok = FALSE; 675 } 676 else 677 g_value_set_boxed (value, &date); 678 } 679 } 680 else if (type == GDA_TYPE_TIME) { 681 if (G_VALUE_TYPE (svalue) != G_TYPE_STRING) 682 allok = FALSE; 683 else { 684 GdaTime timegda; 685 if (!gda_parse_iso8601_time (&timegda, g_value_get_string (svalue))) { 686 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 687 GDA_SERVER_PROVIDER_DATA_ERROR, 688 _("Invalid time '%s' (time format should be HH:MM:SS[.ms])"), 689 g_value_get_string (svalue)); 690 allok = FALSE; 691 } 692 else 693 gda_value_set_time (value, &timegda); 694 } 695 } 696 else if (type == GDA_TYPE_TIMESTAMP) { 697 if (G_VALUE_TYPE (svalue) != G_TYPE_STRING) 698 allok = FALSE; 699 else { 700 GdaTimestamp timestamp; 701 if (!gda_parse_iso8601_timestamp (×tamp, g_value_get_string (svalue))) { 702 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, 703 GDA_SERVER_PROVIDER_DATA_ERROR, 704 _("Invalid timestamp '%s' (format should be YYYY-MM-DD HH:MM:SS[.ms])"), 705 g_value_get_string (svalue)); 706 allok = FALSE; 707 } 708 else 709 gda_value_set_timestamp (value, ×tamp); 710 } 711 } 712 else 713 g_error ("Unhandled GDA type %s in SQLite recordset", 714 gda_g_type_to_string (type)); 715 716 if (! allok) { 717 g_free (value); 718 return NULL; 719 } 720 else 721 return value; 722 } 723 724 static GdaDataModel * 725 dict_table_create_model_func (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED int idxNum, const char *idxStr, 726 int argc, GValue **argv) 727 { 728 GdaDataModel *model; 729 GdaStatement *stmt = NULL; 730 GdaSet *params = NULL; 731 LocalSpec *lspec = (LocalSpec *) spec; 732 733 if (idxStr) { 734 gint i; 735 GSList *list; 736 stmt = GDA_STATEMENT (idxStr); 737 if (! gda_statement_get_parameters (stmt, ¶ms, NULL)) 738 return NULL; 739 if (argc > 0) { 740 g_assert (params && ((guint)argc == g_slist_length (params->holders))); 741 for (i = 0, list = params->holders; i < argc; i++, list = list->next) { 742 GdaHolder *holder = GDA_HOLDER (list->data); 743 GValue *value; 744 value = create_value_from_sqlite3_gvalue (gda_holder_get_g_type (holder), 745 argv [i], NULL); 746 if (value) 747 g_assert (gda_holder_take_value (holder, value, NULL)); 748 else { 749 g_object_ref (params); 750 return NULL; 751 } 752 } 753 } 754 g_object_ref (stmt); 755 } 756 else { 757 GdaSqlBuilder *b; 758 b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT); 759 if (lspec->ncols > 0) { 760 gint i; 761 for (i = 0; i < lspec->ncols; i++) 762 gda_sql_builder_select_add_field (b, lspec->col_names[i], NULL, NULL); 763 } 764 else 765 gda_sql_builder_add_field_value_id (b, 766 gda_sql_builder_add_id (b, "*"), 0); 767 gda_sql_builder_select_add_target_id (b, 768 gda_sql_builder_add_id (b, g_value_get_string (lspec->table_name)), 769 NULL); 770 stmt = gda_sql_builder_get_statement (b, NULL); 771 g_object_unref (b); 772 g_assert (stmt); 773 } 774 GError *lerror = NULL; 775 #ifdef GDA_DEBUG_NO 776 gchar *sql; 777 sql = gda_statement_to_sql (stmt, params, NULL); 778 g_print ("Executed: [%s]\n", sql); 779 g_free (sql); 780 #endif 781 model = gda_connection_statement_execute_select_full (lspec->hc->cnc, stmt, params, 782 GDA_STATEMENT_MODEL_CURSOR_FORWARD, NULL, 783 &lerror); 784 g_object_unref (stmt); 785 if (params) 786 g_object_unref (params); 787 if (model) 788 gda_data_select_compute_modification_statements (GDA_DATA_SELECT (model), NULL); 789 else { 790 gda_log_message ("Virtual table: data model error: %s", 791 lerror && lerror->message ? lerror->message : "no detail"); 792 g_clear_error (&lerror); 793 } 794 795 return model; 796 } 797 static gboolean table_add (HubConnection *hc, const GValue *table_name, GError **error); 798 static void table_remove (HubConnection *hc, const GValue *table_name); 799 static gchar *get_complete_table_name (HubConnection *hc, const GValue *table_name); 800 801 static gboolean 802 attach_hub_connection (GdaVconnectionHub *hub, HubConnection *hc, GError **error) 803 { 804 gchar *tmp; 805 GdaMetaStore *store; 806 GdaMetaContext context; 807 GdaConnectionOptions options; 808 809 store = gda_connection_get_meta_store (hc->cnc); 810 g_assert (store); 811 g_object_get ((GObject*) hc->cnc, "options", &options, NULL); 812 if (! (options & GDA_CONNECTION_OPTIONS_AUTO_META_DATA)) { 813 /* make sure the meta store is up to date */ 814 context.table_name = "_tables"; 815 context.size = 0; 816 if (!gda_connection_update_meta_store (hc->cnc, &context, error)) 817 return FALSE; 818 } 819 820 /* add a :memory: database */ 821 if (hc->ns) { 822 GdaStatement *stmt; 823 tmp = g_strdup_printf ("ATTACH ':memory:' AS %s", hc->ns); 824 stmt = gda_sql_parser_parse_string (internal_parser, tmp, NULL, NULL); 825 g_free (tmp); 826 g_assert (stmt); 827 if (gda_connection_statement_execute_non_select (GDA_CONNECTION (hub), stmt, NULL, NULL, error) == -1) { 828 g_object_unref (stmt); 829 return FALSE; 830 } 831 g_object_unref (stmt); 832 } 833 834 /* add virtual tables */ 835 GdaDataModel *model; 836 gint i, nrows; 837 model = gda_connection_get_meta_store_data (hc->cnc, GDA_CONNECTION_META_TABLES, error, 0); 838 if (!model) 839 return FALSE; 840 841 nrows = gda_data_model_get_n_rows (model); 842 for (i = 0; i < nrows; i++) { 843 const GValue *cv = gda_data_model_get_value_at (model, 0, i, error); 844 const GValue *cv1 = gda_data_model_get_value_at (model, 2, i, error); 845 if (!cv || !cv1) { 846 g_object_unref (model); 847 return FALSE; 848 } 849 850 /* ignore tables which require a complete name <schema>.<name> */ 851 if (!gda_value_differ (cv, cv1)) 852 continue; 853 854 if (!table_add (hc, cv, error)) { 855 g_object_unref (model); 856 return FALSE; 857 } 858 } 859 g_object_unref (model); 860 861 /* monitor changes */ 862 g_signal_connect (store, "meta-changed", G_CALLBACK (meta_changed_cb), hc); 863 864 hub->priv->hub_connections = g_slist_append (hub->priv->hub_connections, hc); 865 return TRUE; 866 } 867 868 static gchar * 869 get_complete_table_name (HubConnection *hc, const GValue *table_name) 870 { 871 if (hc->ns) 872 return g_strdup_printf ("%s.%s", hc->ns, g_value_get_string (table_name)); 873 else 874 return g_strdup (g_value_get_string (table_name)); 875 } 876 877 static void 878 meta_changed_cb (G_GNUC_UNUSED GdaMetaStore *store, GSList *changes, HubConnection *hc) 879 { 880 GSList *list; 881 for (list = changes; list; list = list->next) { 882 GdaMetaStoreChange *ch = (GdaMetaStoreChange*) list->data; 883 GValue *tsn, *tn; 884 885 /* we are only interested in changes occurring in the "_tables" table */ 886 if (!strcmp (ch->table_name, "_tables")) { 887 switch (ch->c_type) { 888 case GDA_META_STORE_ADD: { 889 /* we only want tables where table_short_name = table_name */ 890 tsn = g_hash_table_lookup (ch->keys, "+6"); 891 tn = g_hash_table_lookup (ch->keys, "+2"); 892 if (tn && tsn && !gda_value_compare (tsn, tn)) 893 table_add (hc, tn, NULL); 894 break; 895 } 896 case GDA_META_STORE_REMOVE: { 897 /* we only want tables where table_short_name = table_name */ 898 tsn = g_hash_table_lookup (ch->keys, "-6"); 899 tn = g_hash_table_lookup (ch->keys, "-2"); 900 if (tn && tsn && !gda_value_compare (tsn, tn)) 901 table_remove (hc, tn); 902 break; 903 } 904 case GDA_META_STORE_MODIFY: { 905 /* we only want tables where table_short_name = table_name */ 906 tsn = g_hash_table_lookup (ch->keys, "-6"); 907 tn = g_hash_table_lookup (ch->keys, "-2"); 908 if (tn && tsn && !gda_value_compare (tsn, tn)) 909 table_remove (hc, tn); 910 tsn = g_hash_table_lookup (ch->keys, "+6"); 911 tn = g_hash_table_lookup (ch->keys, "+2"); 912 if (tn && tsn && !gda_value_compare (tsn, tn)) 913 table_add (hc, tn, NULL); 914 break; 915 } 916 } 917 } 918 else if (!strcmp (ch->table_name, "_columns")) { 919 /* TODO */ 920 } 921 } 922 } 923 924 static gboolean 925 table_add (HubConnection *hc, const GValue *table_name, GError **error) 926 { 927 LocalSpec *lspec; 928 gchar *tmp; 929 930 lspec = g_new0 (LocalSpec, 1); 931 GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->data_model = NULL; 932 GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_columns_func = (GdaVconnectionDataModelCreateColumnsFunc) dict_table_create_columns_func; 933 GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_model_func = NULL; 934 GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_filter_func = dict_table_create_filter; 935 GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_filtered_model_func = dict_table_create_model_func; 936 lspec->table_name = gda_value_copy (table_name); 937 lspec->hc = hc; 938 tmp = get_complete_table_name (hc, lspec->table_name); 939 /*g_print ("%s (HC=%p, table_name=%s) name=%s\n", __FUNCTION__, hc, g_value_get_string (table_name), tmp);*/ 940 if (!gda_vconnection_data_model_add (GDA_VCONNECTION_DATA_MODEL (hc->hub), (GdaVconnectionDataModelSpec*) lspec, 941 (GDestroyNotify) local_spec_free, tmp, error)) { 942 g_free (tmp); 943 return FALSE; 944 } 945 g_free (tmp); 946 return TRUE; 947 } 948 949 static void 950 table_remove (HubConnection *hc, const GValue *table_name) 951 { 952 gchar *name; 953 954 name = get_complete_table_name (hc, table_name); 955 /*g_print ("%s (HC=%p, table_name=%s) name=%s\n", __FUNCTION__, hc, g_value_get_string (table_name), name);*/ 956 gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (hc->hub), name, NULL); 957 g_free (name); 958 } 959 960 static void 961 detach_hub_connection (GdaVconnectionHub *hub, HubConnection *hc) 962 { 963 GdaMetaStore *store; 964 GdaDataModel *model; 965 gint i, nrows; 966 967 /* un-monitor changes */ 968 g_object_get (G_OBJECT (hc->cnc), "meta-store", &store, NULL); 969 g_assert (store); 970 g_signal_handlers_disconnect_by_func (store, G_CALLBACK (meta_changed_cb), hc); 971 972 /* remove virtual tables */ 973 model = gda_connection_get_meta_store_data (hc->cnc, GDA_CONNECTION_META_TABLES, NULL, 0); 974 if (!model) 975 return; 976 nrows = gda_data_model_get_n_rows (model); 977 for (i = 0; i < nrows; i++) { 978 const GValue *cv = gda_data_model_get_value_at (model, 0, i, NULL); 979 if (cv) 980 table_remove (hc, cv); 981 } 982 g_object_unref (model); 983 984 /* remove the :memory: database */ 985 if (hc->ns) { 986 GdaStatement *stmt; 987 gchar *tmp; 988 tmp = g_strdup_printf ("DETACH %s", hc->ns); 989 stmt = gda_sql_parser_parse_string (internal_parser, tmp, NULL, NULL); 990 g_free (tmp); 991 g_assert (stmt); 992 gda_connection_statement_execute_non_select (GDA_CONNECTION (hub), stmt, NULL, NULL, NULL); 993 g_object_unref (stmt); 994 } 995 996 hub->priv->hub_connections = g_slist_remove (hub->priv->hub_connections, hc); 997 hub_connection_free (hc); 998 } 999 1000 static void 1001 hub_connection_free (HubConnection *hc) 1002 { 1003 g_object_unref (hc->cnc); 1004 g_free (hc->ns); 1005 g_free (hc); 1006 } 1007