1 /*
2  * Copyright (C) 2007 - 2013 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-data-model.h"
26 #include "gda-vconnection-data-model-private.h"
27 #include "gda-virtual-provider.h"
28 #include <libgda/gda-connection-private.h>
29 #include <libgda/gda-connection-internal.h>
30 #include "../gda-sqlite.h"
31 
32 struct _GdaVconnectionDataModelPrivate {
33 	GSList       *table_data_list; /* list of GdaVConnectionTableData structures */
34 
35 	GRecMutex     lock_context;
36 	GdaStatement *executed_stmt;
37 };
38 
39 static void gda_vconnection_data_model_class_init (GdaVconnectionDataModelClass *klass);
40 static void gda_vconnection_data_model_init       (GdaVconnectionDataModel *cnc, GdaVconnectionDataModelClass *klass);
41 static void gda_vconnection_data_model_dispose   (GObject *object);
42 
43 static gboolean get_rid_of_vtable (GdaVconnectionDataModel *cnc, GdaVConnectionTableData *td, gboolean force, GError **error);
44 
45 enum {
46 	VTABLE_CREATED,
47 	VTABLE_DROPPED,
48 	LAST_SIGNAL
49 };
50 
51 static gint gda_vconnection_data_model_signals[LAST_SIGNAL] = { 0, 0 };
52 
53 static GObjectClass  *parent_class = NULL;
54 
55 #ifdef GDA_DEBUG_NO
56 static void
dump_all_tables(GdaVconnectionDataModel * cnc)57 dump_all_tables (GdaVconnectionDataModel *cnc)
58 {
59 	GSList *list;
60 	g_print ("GdaVconnectionDataModel's tables:\n");
61 	for (list = cnc->priv->table_data_list; list; list = list->next) {
62 		GdaVConnectionTableData *td = (GdaVConnectionTableData *) list->data;
63 		g_print ("    table %s, td=%p, spec=%p\n", td->table_name, td, td->spec);
64 	}
65 }
66 #endif
67 
68 static void
vtable_created(GdaVconnectionDataModel * cnc,const gchar * table_name)69 vtable_created (GdaVconnectionDataModel *cnc, const gchar *table_name)
70 {
71 	_gda_connection_signal_meta_table_update ((GdaConnection *)cnc, table_name);
72 #ifdef GDA_DEBUG_NO
73 	dump_all_tables (cnc);
74 #endif
75 }
76 
77 static void
vtable_dropped(GdaVconnectionDataModel * cnc,const gchar * table_name)78 vtable_dropped (GdaVconnectionDataModel *cnc, const gchar *table_name)
79 {
80 	GdaVConnectionTableData *td;
81 	td = _gda_vconnection_get_table_data_by_name (cnc, table_name);
82 	if (td)
83 		cnc->priv->table_data_list = g_slist_remove (cnc->priv->table_data_list, td);
84 	_gda_connection_signal_meta_table_update ((GdaConnection *)cnc, table_name);
85 #ifdef GDA_DEBUG_NO
86 	dump_all_tables (cnc);
87 #endif
88 }
89 
90 /*
91  * GdaVconnectionDataModel class implementation
92  */
93 static void
gda_vconnection_data_model_class_init(GdaVconnectionDataModelClass * klass)94 gda_vconnection_data_model_class_init (GdaVconnectionDataModelClass *klass)
95 {
96 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
97 
98 	parent_class = g_type_class_peek_parent (klass);
99 
100 	/**
101 	 * GdaVconnectionDataModel::vtable-created
102 	 * @cnc: the #GdaVconnectionDataModel connection
103 	 * @spec: the #GdaVconnectionDataModelSpec for the new virtual table
104 	 *
105 	 * Signal emitted when a new virtual table has been declared
106 	 */
107 	gda_vconnection_data_model_signals[VTABLE_CREATED] =
108 		g_signal_new ("vtable-created",
109                               G_TYPE_FROM_CLASS (object_class),
110                               G_SIGNAL_RUN_LAST,
111                               G_STRUCT_OFFSET (GdaVconnectionDataModelClass, vtable_created),
112                               NULL, NULL,
113                               g_cclosure_marshal_VOID__STRING,
114 			      G_TYPE_NONE, 1, G_TYPE_STRING);
115 	/**
116 	 * GdaVconnectionDataModel::vtable-dropped
117 	 * @cnc: the #GdaVconnectionDataModel connection
118 	 * @spec: the #GdaVconnectionDataModelSpec for the new virtual table
119 	 *
120 	 * Signal emitted when a new virtual table has been undeclared
121 	 */
122 	gda_vconnection_data_model_signals[VTABLE_DROPPED] =
123 		g_signal_new ("vtable-dropped",
124                               G_TYPE_FROM_CLASS (object_class),
125                               G_SIGNAL_RUN_LAST,
126                               G_STRUCT_OFFSET (GdaVconnectionDataModelClass, vtable_dropped),
127                               NULL, NULL,
128                               g_cclosure_marshal_VOID__STRING,
129 			      G_TYPE_NONE, 1, G_TYPE_STRING);
130 
131 	klass->vtable_created = vtable_created;
132 	klass->vtable_dropped = vtable_dropped;
133 
134 	object_class->dispose = gda_vconnection_data_model_dispose;
135 }
136 
137 static void
gda_vconnection_data_model_init(GdaVconnectionDataModel * cnc,G_GNUC_UNUSED GdaVconnectionDataModelClass * klass)138 gda_vconnection_data_model_init (GdaVconnectionDataModel *cnc, G_GNUC_UNUSED GdaVconnectionDataModelClass *klass)
139 {
140 	cnc->priv = g_new (GdaVconnectionDataModelPrivate, 1);
141 	cnc->priv->table_data_list = NULL;
142 	g_rec_mutex_init (& (cnc->priv->lock_context));
143 
144 	g_object_set (G_OBJECT (cnc), "cnc-string", "_IS_VIRTUAL=TRUE", NULL);
145 }
146 
147 static void
gda_vconnection_data_model_dispose(GObject * object)148 gda_vconnection_data_model_dispose (GObject *object)
149 {
150 	GdaVconnectionDataModel *cnc = (GdaVconnectionDataModel *) object;
151 
152 	g_return_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc));
153 
154 	/* free memory */
155 	if (cnc->priv) {
156 		while (cnc->priv->table_data_list) {
157 			GdaVConnectionTableData *td;
158 			td = (GdaVConnectionTableData *) cnc->priv->table_data_list->data;
159 			get_rid_of_vtable (cnc, td, TRUE, NULL);
160 		}
161 		gda_connection_close_no_warning ((GdaConnection *) cnc);
162 
163 		g_rec_mutex_clear (& (cnc->priv->lock_context));
164 		g_free (cnc->priv);
165 		cnc->priv = NULL;
166 	}
167 
168 	/* chain to parent class */
169 	parent_class->dispose (object);
170 }
171 
172 /**
173  * gda_vconnection_data_model_get_type:
174  *
175  * Returns: a new #GType
176  */
177 GType
gda_vconnection_data_model_get_type(void)178 gda_vconnection_data_model_get_type (void)
179 {
180 	static GType type = 0;
181 
182 	if (G_UNLIKELY (type == 0)) {
183 		static GMutex registering;
184 		if (type == 0) {
185 			static GTypeInfo info = {
186 				sizeof (GdaVconnectionDataModelClass),
187 				(GBaseInitFunc) NULL,
188 				(GBaseFinalizeFunc) NULL,
189 				(GClassInitFunc) gda_vconnection_data_model_class_init,
190 				NULL, NULL,
191 				sizeof (GdaVconnectionDataModel),
192 				0,
193 				(GInstanceInitFunc) gda_vconnection_data_model_init,
194 				0
195 			};
196 
197 			g_mutex_lock (&registering);
198 			if (type == 0)
199 				type = g_type_register_static (GDA_TYPE_VIRTUAL_CONNECTION, "GdaVconnectionDataModel", &info, 0);
200 			g_mutex_unlock (&registering);
201 		}
202 	}
203 
204 	return type;
205 }
206 
207 static void
spec_destroy_func(GdaVconnectionDataModelSpec * spec)208 spec_destroy_func (GdaVconnectionDataModelSpec *spec)
209 {
210 	g_object_unref (spec->data_model);
211 	g_free (spec);
212 }
213 
214 static GList *
create_columns(GdaVconnectionDataModelSpec * spec,G_GNUC_UNUSED GError ** error)215 create_columns (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED GError **error)
216 {
217 	g_return_val_if_fail (spec->data_model, NULL);
218 
219 	GList *columns = NULL;
220 	guint i, ncols;
221 	ncols = gda_data_model_get_n_columns (spec->data_model);
222 	for (i = 0; i < ncols; i++) {
223 		GdaColumn *mcol = gda_data_model_describe_column (spec->data_model, i);
224 		GdaColumn *ccol = gda_column_copy (mcol);
225 		columns = g_list_prepend (columns, ccol);
226 	}
227 	return g_list_reverse (columns);
228 }
229 
230 /**
231  * gda_vconnection_data_model_add_model:
232  * @cnc: a #GdaVconnectionDataModel connection
233  * @model: a #GdaDataModel
234  * @table_name: the name of the table
235  * @error: a place to store errors, or %NULL
236  *
237  * Make @model appear as a table named @table_name in the @cnc connection (as if a
238  * "CREATE TABLE..." statement was executed, except that the data contained within @model
239  * is actually used when @table_name's contents is read or written).
240  *
241  * For a more general approach, see the gda_vconnection_data_model_add() method.
242  *
243  * Returns: %TRUE if no error occurred
244  */
245 gboolean
gda_vconnection_data_model_add_model(GdaVconnectionDataModel * cnc,GdaDataModel * model,const gchar * table_name,GError ** error)246 gda_vconnection_data_model_add_model (GdaVconnectionDataModel *cnc,
247 				      GdaDataModel *model, const gchar *table_name, GError **error)
248 {
249 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
250 	GdaVconnectionDataModelSpec *spec;
251 	gboolean retval;
252 
253 	spec = g_new0 (GdaVconnectionDataModelSpec, 1);
254 	spec->data_model = g_object_ref (model);
255 	spec->create_columns_func = create_columns;
256 	retval = gda_vconnection_data_model_add (cnc, spec, (GDestroyNotify) spec_destroy_func, table_name, error);
257 
258 	return retval;
259 }
260 
261 /**
262  * gda_vconnection_data_model_add:
263  * @cnc: a #GdaVconnectionDataModel connection
264  * @spec: a #GdaVconnectionDataModelSpec structure, used AS IS (not copied) and can be modified
265  * @spec_free_func: (allow-none): function to call when freeing @spec, or %NULL
266  * @table_name: the name of the table
267  * @error: a place to store errors, or %NULL
268  *
269  * Create a new virtual table named @table_name in @cnc. The contents of that new table
270  * is dictated by what's in @spec.
271  *
272  * If there is just one #GdaDataModel to make appear as a table
273  * then the gda_vconnection_data_model_add_model() method is easier to use.
274  *
275  * The @spec_free_func can (depending on your code) be used to clean memory allocated for @spec or
276  * @spec->data_model.
277  *
278  * If an error occurs, then the @spec_free_func function is called using @spec as argument.
279  *
280  * Returns: %TRUE if no error occurred
281  */
282 gboolean
gda_vconnection_data_model_add(GdaVconnectionDataModel * cnc,GdaVconnectionDataModelSpec * spec,GDestroyNotify spec_free_func,const gchar * table_name,GError ** error)283 gda_vconnection_data_model_add (GdaVconnectionDataModel *cnc, GdaVconnectionDataModelSpec *spec,
284 				GDestroyNotify spec_free_func, const gchar *table_name, GError **error)
285 {
286 	GdaVirtualProvider *prov;
287 	gchar *str;
288 	int rc;
289 	char *zErrMsg = NULL;
290 	gboolean retval = TRUE;
291 	SqliteConnectionData *scnc;
292 
293 	static gint counter = 0;
294 
295 	g_return_val_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc), FALSE);
296 	g_return_val_if_fail (table_name && *table_name, FALSE);
297 	g_return_val_if_fail (spec, FALSE);
298 	g_return_val_if_fail (spec->data_model || (spec->create_columns_func && (spec->create_model_func || spec->create_filtered_model_func)), FALSE);
299 
300 	/* cleaning functions */
301 	if (spec->data_model) {
302 		g_return_val_if_fail (GDA_IS_DATA_MODEL (spec->data_model), FALSE);
303 		spec->create_columns_func = create_columns;
304 		spec->create_model_func = NULL;
305 		spec->create_filter_func = NULL;
306 		spec->create_filtered_model_func = NULL;
307 	}
308 	else if (spec->create_filter_func)
309 		spec->create_model_func = NULL;
310 	else {
311 		spec->create_filter_func = NULL;
312 		spec->create_filtered_model_func = NULL;
313 	}
314 
315 	scnc = (SqliteConnectionData*) gda_connection_internal_get_provider_data_error ((GdaConnection *) cnc, error);
316 	if (!scnc)
317 		return FALSE;
318 
319 	/* create a new GdaVConnectionTableData structure for this virtual table */
320 	GdaVConnectionTableData *td;
321 	td = g_new0 (GdaVConnectionTableData, 1);
322 	td->spec = spec;
323 	td->spec_free_func = spec_free_func;
324 	td->table_name = _gda_connection_compute_table_virtual_name (GDA_CONNECTION (cnc), table_name);
325 	td->unique_name = g_strdup_printf ("Spec%d", counter++);
326 	cnc->priv->table_data_list = g_slist_append (cnc->priv->table_data_list, td);
327 
328 	/* actually create the virtual table in @cnc */
329 	prov = (GdaVirtualProvider *) gda_connection_get_provider (GDA_CONNECTION (cnc));
330 	str = g_strdup_printf ("CREATE VIRTUAL TABLE %s USING %s ('%s')", td->table_name, G_OBJECT_TYPE_NAME (prov), td->unique_name);
331 	rc = SQLITE3_CALL (sqlite3_exec) (scnc->connection, str, NULL, 0, &zErrMsg);
332 	g_free (str);
333 	if (rc != SQLITE_OK) {
334 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
335 			     GDA_SERVER_PROVIDER_INTERNAL_ERROR,
336 			     "%s", zErrMsg);
337 		SQLITE3_CALL (sqlite3_free) (zErrMsg);
338 		_gda_vconnection_data_model_table_data_free (td);
339 		cnc->priv->table_data_list = g_slist_remove (cnc->priv->table_data_list, td);
340 		retval = FALSE;
341 	}
342 	else {
343 		g_signal_emit (G_OBJECT (cnc), gda_vconnection_data_model_signals[VTABLE_CREATED], 0,
344 			       td->table_name);
345 		/*g_print ("Virtual connection: added table %s (spec = %p)\n", td->table_name, td->spec);*/
346 	}
347 
348 	return retval;
349 }
350 
351 static gboolean
get_rid_of_vtable(GdaVconnectionDataModel * cnc,GdaVConnectionTableData * td,gboolean force,GError ** error)352 get_rid_of_vtable (GdaVconnectionDataModel *cnc, GdaVConnectionTableData *td, gboolean force, GError **error)
353 {
354 	gchar *str;
355 	int rc;
356 	char *zErrMsg = NULL;
357 	gboolean allok = TRUE;
358 
359 	SqliteConnectionData *scnc;
360 	scnc = (SqliteConnectionData*) gda_connection_internal_get_provider_data_error ((GdaConnection *) cnc, error);
361 	if (!scnc && !force)
362 		return FALSE;
363 
364 	if (scnc) {
365 		str = g_strdup_printf ("DROP TABLE %s", td->table_name);
366 		rc = SQLITE3_CALL (sqlite3_exec) (scnc->connection, str, NULL, 0, &zErrMsg);
367 		g_free (str);
368 
369 		if (rc != SQLITE_OK) {
370 			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
371 				     GDA_SERVER_PROVIDER_INTERNAL_ERROR,
372 				     "%s", zErrMsg);
373 			SQLITE3_CALL (sqlite3_free) (zErrMsg);
374 			allok = FALSE;
375 			if (!force)
376 				return FALSE;
377 		}
378 	}
379 
380 	/* clean the cnc->priv->table_data_list list */
381 	cnc->priv->table_data_list = g_slist_remove (cnc->priv->table_data_list, td);
382 	g_signal_emit (G_OBJECT (cnc), gda_vconnection_data_model_signals[VTABLE_DROPPED], 0,
383 		       td->table_name);
384 	/*g_print ("Virtual connection: removed table %s (%p)\n", td->table_name, td->spec->data_model);*/
385 	_gda_vconnection_data_model_table_data_free (td);
386 
387 	return allok;
388 }
389 
390 /**
391  * gda_vconnection_data_model_remove:
392  * @cnc: a #GdaVconnectionDataModel connection
393  * @table_name: the name of the table to remove from @cnc
394  * @error: a place to store errors, or %NULL
395  *
396  * Remove the table named @table_name in the @cnc connection (as if a "DROP TABLE..."
397  * statement was executed, except that no data gets destroyed as the associated data model remains the same).
398  *
399  * Returns: %TRUE if no error occurred
400  */
401 gboolean
gda_vconnection_data_model_remove(GdaVconnectionDataModel * cnc,const gchar * table_name,GError ** error)402 gda_vconnection_data_model_remove (GdaVconnectionDataModel *cnc, const gchar *table_name, GError **error)
403 {
404 	GdaVConnectionTableData *td;
405 
406 	g_return_val_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc), FALSE);
407 	g_return_val_if_fail (table_name && *table_name, FALSE);
408 
409 	td = _gda_vconnection_get_table_data_by_name (cnc, table_name);
410 	if (!td) {
411 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
412 			     GDA_SERVER_PROVIDER_MISUSE_ERROR,
413 			     "%s", _("Table to remove not found"));
414 		return FALSE;
415 	}
416 
417 	return get_rid_of_vtable (cnc, td, FALSE, error);
418 }
419 
420 /**
421  * gda_vconnection_data_model_get:
422  * @cnc: a #GdaVconnectionDataModel connection
423  * @table_name: a table name within @cnc
424  *
425  * Find the #GdaVconnectionDataModelSpec specifying how the table named @table_name is represented
426  * in @cnc.
427  *
428  * Returns: (transfer none) (allow-none): a #GdaVconnectionDataModelSpec pointer, of %NULL if there is no table named @table_name
429  *
430  * Since: 4.2.6
431  */
432 GdaVconnectionDataModelSpec *
gda_vconnection_data_model_get(GdaVconnectionDataModel * cnc,const gchar * table_name)433 gda_vconnection_data_model_get (GdaVconnectionDataModel *cnc, const gchar *table_name)
434 {
435 	GdaVConnectionTableData *td;
436 	g_return_val_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc), NULL);
437 	if (!table_name || !(*table_name))
438 		return NULL;
439 	td = _gda_vconnection_get_table_data_by_name (cnc, table_name);
440 	if (td)
441 		return td->spec;
442 	else
443 		return NULL;
444 }
445 
446 /**
447  * gda_vconnection_data_model_get_model:
448  * @cnc: a #GdaVconnectionDataModel connection
449  * @table_name: a table name within @cnc
450  *
451  * Find the #GdaDataModel object representing the @table_name table in @cnc. it can return %NULL
452  * either if no table named @table_name exists, or if that table actually exists but no #GdaDataModel
453  * has yet been created. For a more general approach, use the gda_vconnection_data_model_get().
454  *
455  * Returns: (transfer none) (allow-none): the #GdaDataModel, or %NULL
456  */
457 GdaDataModel *
gda_vconnection_data_model_get_model(GdaVconnectionDataModel * cnc,const gchar * table_name)458 gda_vconnection_data_model_get_model (GdaVconnectionDataModel *cnc, const gchar *table_name)
459 {
460 	GdaVConnectionTableData *td;
461 	g_return_val_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc), NULL);
462 	g_return_val_if_fail (cnc->priv, NULL);
463 	if (!table_name || !(*table_name))
464 		return NULL;
465 
466 	td = _gda_vconnection_get_table_data_by_name (cnc, table_name);
467 	if (td)
468 		return td->spec->data_model;
469 	else
470 		return NULL;
471 }
472 
473 /**
474  * gda_vconnection_data_model_get_table_name:
475  * @cnc: a #GdaVconnectionDataModel connection
476  * @model: a #GdaDataModel representing a table within @cnc
477  *
478  * Find the name of the table associated to @model in @cnc
479  *
480  * Returns: (transfer none) (allow-none): the table name, or %NULL if not found
481  */
482 const gchar *
gda_vconnection_data_model_get_table_name(GdaVconnectionDataModel * cnc,GdaDataModel * model)483 gda_vconnection_data_model_get_table_name (GdaVconnectionDataModel *cnc, GdaDataModel *model)
484 {
485 	GdaVConnectionTableData *td;
486 	g_return_val_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc), NULL);
487 	g_return_val_if_fail (cnc->priv, NULL);
488 	if (!model)
489 		return NULL;
490 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL);
491 
492 	td = _gda_vconnection_get_table_data_by_model (cnc, model);
493 	if (td)
494 		return td->table_name;
495 	else
496 		return NULL;
497 }
498 
499 /**
500  * gda_vconnection_data_model_foreach:
501  * @cnc: a #GdaVconnectionDataModel connection
502  * @func: a #GdaVconnectionDataModelFunc function pointer
503  * @data: data to pass to @func calls
504  *
505  * Call @func for each table in @cnc.
506  *
507  * Warning: @func will be called for any table present in @cnc even if no data
508  * model represents the contents of the table (which means the 1st argument of @func
509  * may be %NULL)
510  */
511 void
gda_vconnection_data_model_foreach(GdaVconnectionDataModel * cnc,GdaVconnectionDataModelFunc func,gpointer data)512 gda_vconnection_data_model_foreach (GdaVconnectionDataModel *cnc,
513 				    GdaVconnectionDataModelFunc func, gpointer data)
514 {
515 	GSList *copy, *list;
516 	g_return_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc));
517 	g_return_if_fail (cnc->priv);
518 
519 	if (!func || !cnc->priv->table_data_list)
520 		return;
521 
522 	copy = g_slist_copy (cnc->priv->table_data_list);
523 	for (list = copy; list; list = list->next) {
524 		GdaVConnectionTableData *td = (GdaVConnectionTableData*) list->data;
525 		func (td->spec->data_model, td->table_name, data);
526 	}
527 	g_slist_free (copy);
528 }
529 
530 /*
531  * private
532  */
533 GdaVConnectionTableData *
_gda_vconnection_get_table_data_by_name(GdaVconnectionDataModel * cnc,const gchar * table_name)534 _gda_vconnection_get_table_data_by_name (GdaVconnectionDataModel *cnc, const gchar *table_name)
535 {
536 	GSList *list;
537 	gchar *quoted;
538 	if (!table_name || !*table_name)
539 		return NULL;
540 	quoted = _gda_connection_compute_table_virtual_name (GDA_CONNECTION (cnc), table_name);
541 	for (list = cnc->priv->table_data_list; list; list = list->next) {
542 		if (!strcmp (((GdaVConnectionTableData*) list->data)->table_name, quoted)) {
543 			g_free (quoted);
544 			return (GdaVConnectionTableData*) list->data;
545 		}
546 	}
547 	g_free (quoted);
548 	return NULL;
549 }
550 
551 GdaVConnectionTableData *
_gda_vconnection_get_table_data_by_unique_name(GdaVconnectionDataModel * cnc,const gchar * unique_name)552 _gda_vconnection_get_table_data_by_unique_name (GdaVconnectionDataModel *cnc, const gchar *unique_name)
553 {
554 	GSList *list;
555 	for (list = cnc->priv->table_data_list; list; list = list->next) {
556 		if (!strcmp (((GdaVConnectionTableData*) list->data)->unique_name, unique_name))
557 			return (GdaVConnectionTableData*) list->data;
558 	}
559 	return NULL;
560 }
561 
562 GdaVConnectionTableData *
_gda_vconnection_get_table_data_by_model(GdaVconnectionDataModel * cnc,GdaDataModel * model)563 _gda_vconnection_get_table_data_by_model (GdaVconnectionDataModel *cnc, GdaDataModel *model)
564 {
565 	GSList *list;
566 	for (list = cnc->priv->table_data_list; list; list = list->next) {
567 		if (((GdaVConnectionTableData*) list->data)->real_model == model)
568 			return (GdaVConnectionTableData*) list->data;
569 	}
570 	return NULL;
571 }
572 
573 void
_gda_vconnection_data_model_table_data_free(GdaVConnectionTableData * td)574 _gda_vconnection_data_model_table_data_free (GdaVConnectionTableData *td)
575 {
576 	ParamType i;
577 
578 	if (td->real_model)
579 		g_object_unref (td->real_model);
580 	if (td->columns) {
581 		g_list_foreach (td->columns, (GFunc) g_object_unref, NULL);
582 		g_list_free (td->columns);
583 	}
584 	g_free (td->table_name);
585 	g_free (td->unique_name);
586 	if (td->spec_free_func)
587 		td->spec_free_func (td->spec);
588 	for (i = 0; i < PARAMS_NB; i++) {
589 		if (td->modif_params[i])
590 			g_object_unref (td->modif_params[i]);
591 		if (td->modif_stmt[i])
592 			g_object_unref (td->modif_stmt[i]);
593 	}
594 
595 	if (td->context.hash)
596 		g_hash_table_destroy (td->context.hash);
597 	g_free (td);
598 }
599 
600 static void
vcontext_object_weak_notify_cb(VContext * context,GObject * old_context_object)601 vcontext_object_weak_notify_cb (VContext *context, GObject *old_context_object)
602 {
603 	g_assert (context);
604 	context->context_object = NULL;
605 	g_hash_table_remove (context->vtable->context.hash, old_context_object);
606 }
607 
608 static void
vcontext_free(VContext * context)609 vcontext_free (VContext *context)
610 {
611 	if (context->context_object)
612 		g_object_weak_unref (context->context_object,
613 				     (GWeakNotify) vcontext_object_weak_notify_cb, context);
614 	if (context->context_data) {
615 		g_array_free (context->context_data, TRUE);
616 		context->context_data = NULL;
617 	}
618 	g_free (context);
619 #ifdef DEBUG_VCONTEXT
620 	g_print ("VCFree %p\n", context);
621 #endif
622 }
623 
624 void
_gda_vconnection_set_working_obj(GdaVconnectionDataModel * cnc,GObject * obj)625 _gda_vconnection_set_working_obj (GdaVconnectionDataModel *cnc, GObject *obj)
626 {
627 	GSList *list;
628 	if (obj) {
629 		g_rec_mutex_lock (& (cnc->priv->lock_context));
630 		for (list = cnc->priv->table_data_list; list; list = list->next) {
631 			GdaVConnectionTableData *td = (GdaVConnectionTableData*) list->data;
632 			VContext *vc = NULL;
633 
634 			g_assert (!td->context.current_vcontext);
635 			td->context.mutex = &(cnc->priv->lock_context);
636 			if (! td->context.hash)
637 				td->context.hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
638 									  NULL, (GDestroyNotify) vcontext_free);
639 			else
640 				vc = g_hash_table_lookup (td->context.hash, obj);
641 
642 			if (! vc) {
643 				vc = g_new0 (VContext, 1);
644 				vc->context_object = obj;
645 				vc->context_data = g_array_new (FALSE, FALSE,
646 								sizeof (VirtualFilteredData*));
647 				g_array_set_clear_func (vc->context_data, (GDestroyNotify) _gda_vconnection_virtual_filtered_data_unref);
648 				vc->vtable = td;
649 				g_object_weak_ref (obj, (GWeakNotify) vcontext_object_weak_notify_cb, vc);
650 				g_hash_table_insert (td->context.hash, obj, vc);
651 #ifdef DEBUG_VCONTEXT
652 				g_print ("VCNew %p\n", vc);
653 #endif
654 			}
655 			td->context.current_vcontext = vc;
656 		}
657 	}
658 	else {
659 		for (list = cnc->priv->table_data_list; list; list = list->next) {
660 			GdaVConnectionTableData *td = (GdaVConnectionTableData*) list->data;
661 			/* REM: td->context.current_vcontext may already be NULL in case
662 			 * an exception already occurred */
663 			td->context.current_vcontext = NULL;
664 		}
665 		g_rec_mutex_unlock (& (cnc->priv->lock_context));
666 	}
667 }
668 
669 void
_gda_vconnection_change_working_obj(GdaVconnectionDataModel * cnc,GObject * obj)670 _gda_vconnection_change_working_obj (GdaVconnectionDataModel *cnc, GObject *obj)
671 {
672 	GSList *list;
673 
674 	for (list = cnc->priv->table_data_list; list; list = list->next) {
675 		GdaVConnectionTableData *td = (GdaVConnectionTableData*) list->data;
676 		if (!td->context.hash)
677 			continue;
678 
679 		g_assert (td->context.current_vcontext);
680 
681 		VContext *ovc, *nvc;
682 		ovc = td->context.current_vcontext;
683 		nvc = g_new0 (VContext, 1);
684 		nvc->context_object = obj;
685 		nvc->vtable = ovc->vtable;
686 		nvc->context_data = ovc->context_data;
687 		ovc->context_data = NULL;
688 		g_object_weak_ref (obj, (GWeakNotify) vcontext_object_weak_notify_cb, nvc);
689 		g_hash_table_insert (td->context.hash, obj, nvc);
690 #ifdef DEBUG_VCONTEXT
691 		g_print ("VCNew %p\n", nvc);
692 #endif
693 		g_hash_table_remove (td->context.hash, ovc->context_object);
694 	}
695 }
696