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 (®istering);
198 if (type == 0)
199 type = g_type_register_static (GDA_TYPE_VIRTUAL_CONNECTION, "GdaVconnectionDataModel", &info, 0);
200 g_mutex_unlock (®istering);
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