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 (&registering);
121 		if (type == 0)
122 			type = g_type_register_static (GDA_TYPE_VCONNECTION_DATA_MODEL, "GdaVconnectionHub", &info, 0);
123 		g_mutex_unlock (&registering);
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 (&timestamp, 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, &timestamp);
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, &params, 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