1 /*
2  * Copyright (C) 2009 - 2013 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2010 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 
21 #include <string.h>
22 #include <glib/gi18n-lib.h>
23 #include "../tool-utils.h"
24 #include "browser-connection.h"
25 #include <libgda/thread-wrapper/gda-thread-wrapper.h>
26 #include "support.h"
27 #include "marshal.h"
28 #include <sql-parser/gda-sql-parser.h>
29 #include <libgda/gda-sql-builder.h>
30 #include <libgda-ui/gdaui-enums.h>
31 #include "../config-info.h"
32 #include "browser-virtual-connection.h"
33 #include <sqlite/virtual/gda-virtual-connection.h>
34 #include <libgda/gda-debug-macros.h>
35 #include "browser-connection-priv.h"
36 
37 #define CHECK_RESULTS_SHORT_TIMER 200
38 #define CHECK_RESULTS_LONG_TIMER 2
39 
40 typedef struct {
41 	GObject *result;
42 	GError  *error;
43 	GdaSet  *last_inserted_row;
44 } StatementResult;
45 
46 static void
statement_result_free(StatementResult * res)47 statement_result_free (StatementResult *res)
48 {
49 	if (res->result)
50 		g_object_unref (res->result);
51 	if (res->last_inserted_row)
52 		g_object_unref (res->last_inserted_row);
53 	g_clear_error (&(res->error));
54 	g_free (res);
55 }
56 
57 /* signals */
58 enum {
59 	BUSY,
60 	META_CHANGED,
61 	FAV_CHANGED,
62 	TRANSACTION_STATUS_CHANGED,
63 	TABLE_COLUMN_PREF_CHANGED,
64 	LAST_SIGNAL
65 };
66 
67 gint browser_connection_signals [LAST_SIGNAL] = { 0, 0, 0, 0, 0 };
68 
69 /* wrapper jobs handling */
70 static gboolean check_for_wrapper_result (BrowserConnection *bcnc);
71 
72 typedef enum {
73 	JOB_TYPE_META_STORE_UPDATE,
74 	JOB_TYPE_META_STRUCT_SYNC,
75 	JOB_TYPE_STATEMENT_EXECUTE,
76 	JOB_TYPE_CALLBACK
77 } JobType;
78 
79 typedef struct {
80 	guint    job_id;
81 	JobType  job_type;
82 	gchar   *reason;
83 
84 	/* the following may be %NULL for stmt execution and meta store updates */
85 	BrowserConnectionJobCallback callback;
86 	gpointer cb_data;
87 } WrapperJob;
88 
89 static void
wrapper_job_free(WrapperJob * wj)90 wrapper_job_free (WrapperJob *wj)
91 {
92 	g_free (wj->reason);
93 	g_free (wj);
94 }
95 
96 #ifdef GDA_DEBUG_MUTEX
97 static void
my_lock(GMutex * mutex,gint where)98 my_lock (GMutex *mutex, gint where)
99 {
100 	GTimer *timer;
101 	g_print ("wait to lock %p (th %p)@line %d\n", mutex, g_thread_self(), where);
102 	timer = g_timer_new ();
103 	g_mutex_lock (mutex);
104 	g_timer_stop (timer);
105 
106 	if (g_timer_elapsed (timer, NULL) > 2.0)
107 		g_print ("WARN: locking %p (th %p)@line %d: %02f\n", mutex, g_thread_self(), where,
108 			 g_timer_elapsed (timer, NULL));
109 	g_print ("tmp LOC %p (th %p)@line %d took %02f\n", mutex, g_thread_self(), where,
110 		 g_timer_elapsed (timer, NULL));
111 	g_timer_destroy (timer);
112 }
113 static void
my_unlock(GMutex * mutex,gint where)114 my_unlock (GMutex *mutex, gint where)
115 {
116 	g_mutex_unlock (mutex);
117 	g_print ("tmp UNL %p (th %p)@line %d\n", mutex, g_thread_self(), where);
118 }
119 
120   #define MUTEX_LOCK(bcnc) g_mutex_lock (&((bcnc)->priv->mstruct_mutex))
121   #define MUTEX_UNLOCK(bcnc) g_mutex_unlock (&((bcnc)->priv->mstruct_mutex))
122 #else /* GDA_DEBUG_MUTEX */
123   #define MUTEX_LOCK(bcnc) g_mutex_lock (&((bcnc)->priv->mstruct_mutex))
124   #define MUTEX_UNLOCK(bcnc) g_mutex_unlock (&((bcnc)->priv->mstruct_mutex))
125 #endif /* GDA_DEBUG_MUTEX */
126 
127 /*
128  * Returns: %TRUE if current timer should be removed
129  */
130 static gboolean
setup_results_timer(BrowserConnection * bcnc)131 setup_results_timer (BrowserConnection *bcnc)
132 {
133 	gboolean short_timer = TRUE;
134 
135 	if (bcnc->priv->ioc_watch_id != 0)
136 		return FALSE; /* nothing to do, we use notifications */
137 
138 	bcnc->priv->nb_no_job_waits ++;
139 	if (bcnc->priv->nb_no_job_waits > 100)
140 		short_timer = FALSE;
141 
142 	if ((bcnc->priv->wrapper_results_timer > 0) &&
143 	    (bcnc->priv->long_timer != short_timer))
144 		return FALSE; /* nothing to do, timer already correctlyset up */
145 
146 	/* switch to a short/long timer to check for results */
147 	if (bcnc->priv->long_timer == short_timer)
148 		g_source_remove (bcnc->priv->wrapper_results_timer);
149 
150 	bcnc->priv->long_timer = !short_timer;
151 	bcnc->priv->wrapper_results_timer = g_timeout_add (short_timer ? CHECK_RESULTS_SHORT_TIMER : CHECK_RESULTS_LONG_TIMER,
152 							   (GSourceFunc) check_for_wrapper_result,
153 							   bcnc);
154 	bcnc->priv->nb_no_job_waits = 0;
155 	return TRUE;
156 }
157 
158 /*
159  * Pushes a job which has been asked to be exected in a sub thread using gda_thread_wrapper_execute()
160  */
161 static void
push_wrapper_job(BrowserConnection * bcnc,guint job_id,JobType job_type,const gchar * reason,BrowserConnectionJobCallback callback,gpointer cb_data)162 push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const gchar *reason,
163 		  BrowserConnectionJobCallback callback, gpointer cb_data)
164 {
165 	/* handle timers if necessary */
166 	setup_results_timer (bcnc);
167 
168 	/* add WrapperJob structure */
169 	WrapperJob *wj;
170 	wj = g_new0 (WrapperJob, 1);
171 	wj->job_id = job_id;
172 	wj->job_type = job_type;
173 	if (reason)
174 		wj->reason = g_strdup (reason);
175 	wj->callback = callback;
176 	wj->cb_data = cb_data;
177 
178 	bcnc->priv->wrapper_jobs = g_slist_append (bcnc->priv->wrapper_jobs, wj);
179 
180 	if (! bcnc->priv->wrapper_jobs->next)
181 		g_signal_emit (bcnc, browser_connection_signals [BUSY], 0, TRUE, wj->reason);
182 }
183 
184 static void
pop_wrapper_job(BrowserConnection * bcnc,WrapperJob * wj)185 pop_wrapper_job (BrowserConnection *bcnc, WrapperJob *wj)
186 {
187 	bcnc->priv->wrapper_jobs = g_slist_remove (bcnc->priv->wrapper_jobs, wj);
188 	wrapper_job_free (wj);
189 	g_signal_emit (bcnc, browser_connection_signals [BUSY], 0, FALSE, NULL);
190 }
191 
192 
193 /*
194  * Main static functions
195  */
196 static void browser_connection_class_init (BrowserConnectionClass *klass);
197 static void browser_connection_init (BrowserConnection *bcnc);
198 static void browser_connection_dispose (GObject *object);
199 static void browser_connection_set_property (GObject *object,
200 					     guint param_id,
201 					     const GValue *value,
202 					     GParamSpec *pspec);
203 static void browser_connection_get_property (GObject *object,
204 					     guint param_id,
205 					     GValue *value,
206 					     GParamSpec *pspec);
207 /* get a pointer to the parents to be able to call their destructor */
208 static GObjectClass  *parent_class = NULL;
209 
210 /* properties */
211 enum {
212         PROP_0,
213         PROP_GDA_CNC
214 };
215 
216 GType
browser_connection_get_type(void)217 browser_connection_get_type (void)
218 {
219 	static GType type = 0;
220 
221 	if (G_UNLIKELY (type == 0)) {
222 		static GMutex registering;
223 		static const GTypeInfo info = {
224 			sizeof (BrowserConnectionClass),
225 			(GBaseInitFunc) NULL,
226 			(GBaseFinalizeFunc) NULL,
227 			(GClassInitFunc) browser_connection_class_init,
228 			NULL,
229 			NULL,
230 			sizeof (BrowserConnection),
231 			0,
232 			(GInstanceInitFunc) browser_connection_init,
233 			0
234 		};
235 
236 
237 		g_mutex_lock (&registering);
238 		if (type == 0)
239 			type = g_type_register_static (G_TYPE_OBJECT, "BrowserConnection", &info, 0);
240 		g_mutex_unlock (&registering);
241 	}
242 	return type;
243 }
244 
245 static void
browser_connection_class_init(BrowserConnectionClass * klass)246 browser_connection_class_init (BrowserConnectionClass *klass)
247 {
248 	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
249 	parent_class = g_type_class_peek_parent (klass);
250 
251 	browser_connection_signals [BUSY] =
252 		g_signal_new ("busy",
253                               G_TYPE_FROM_CLASS (object_class),
254                               G_SIGNAL_RUN_FIRST,
255                               G_STRUCT_OFFSET (BrowserConnectionClass, busy),
256                               NULL, NULL,
257                               _marshal_VOID__BOOLEAN_STRING, G_TYPE_NONE,
258                               2, G_TYPE_BOOLEAN, G_TYPE_STRING);
259 	browser_connection_signals [META_CHANGED] =
260 		g_signal_new ("meta-changed",
261                               G_TYPE_FROM_CLASS (object_class),
262                               G_SIGNAL_RUN_FIRST,
263                               G_STRUCT_OFFSET (BrowserConnectionClass, meta_changed),
264                               NULL, NULL,
265                               g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE,
266                               1, GDA_TYPE_META_STRUCT);
267 	browser_connection_signals [FAV_CHANGED] =
268 		g_signal_new ("favorites-changed",
269                               G_TYPE_FROM_CLASS (object_class),
270                               G_SIGNAL_RUN_FIRST,
271                               G_STRUCT_OFFSET (BrowserConnectionClass, favorites_changed),
272                               NULL, NULL,
273                               g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
274                               0);
275 	browser_connection_signals [TRANSACTION_STATUS_CHANGED] =
276 		g_signal_new ("transaction-status-changed",
277                               G_TYPE_FROM_CLASS (object_class),
278                               G_SIGNAL_RUN_FIRST,
279                               G_STRUCT_OFFSET (BrowserConnectionClass, transaction_status_changed),
280                               NULL, NULL,
281                               g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
282                               0);
283 	browser_connection_signals [TABLE_COLUMN_PREF_CHANGED] =
284 		g_signal_new ("table-column-pref-changed",
285                               G_TYPE_FROM_CLASS (object_class),
286                               G_SIGNAL_RUN_FIRST,
287                               G_STRUCT_OFFSET (BrowserConnectionClass, table_column_pref_changed),
288                               NULL, NULL,
289 			      _marshal_VOID__POINTER_POINTER_STRING_STRING, G_TYPE_NONE,
290                               4, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING);
291 
292 	klass->busy = browser_connection_set_busy_state;
293 	klass->meta_changed = NULL;
294 	klass->favorites_changed = NULL;
295 	klass->transaction_status_changed = NULL;
296 	klass->table_column_pref_changed = NULL;
297 
298 	/* Properties */
299         object_class->set_property = browser_connection_set_property;
300         object_class->get_property = browser_connection_get_property;
301 	g_object_class_install_property (object_class, PROP_GDA_CNC,
302                                          g_param_spec_object ("gda-connection", NULL, "Connection to use",
303                                                               GDA_TYPE_CONNECTION,
304                                                               G_PARAM_WRITABLE |
305 							      G_PARAM_CONSTRUCT_ONLY));
306 
307 	object_class->dispose = browser_connection_dispose;
308 }
309 
310 static gboolean
wrapper_ioc_cb(GIOChannel * source,GIOCondition condition,BrowserConnection * bcnc)311 wrapper_ioc_cb (GIOChannel *source, GIOCondition condition, BrowserConnection *bcnc)
312 {
313 	GIOStatus status;
314 	gsize nread;
315 	GdaThreadNotification notif;
316 
317 	g_assert (source == bcnc->priv->ioc);
318 //#define DEBUG_POLLING_SWITCH
319 #ifdef DEBUG_POLLING_SWITCH
320 	static guint c = 0;
321 	c++;
322 	if (c == 4)
323 		goto onerror;
324 #endif
325 	if (condition & G_IO_IN) {
326 		status = g_io_channel_read_chars (bcnc->priv->ioc, (gchar*) &notif, sizeof (notif),
327 						  &nread, NULL);
328 		if ((status != G_IO_STATUS_NORMAL) || (nread != sizeof (notif)))
329 			goto onerror;
330 
331 		switch (notif.type) {
332 		case GDA_THREAD_NOTIFICATION_JOB:
333 			check_for_wrapper_result (bcnc);
334 			break;
335 		case GDA_THREAD_NOTIFICATION_SIGNAL:
336 			gda_thread_wrapper_iterate (bcnc->priv->wrapper, FALSE);
337 			break;
338 		default:
339 			/* an error occurred somewhere */
340 			goto onerror;
341 		}
342 	}
343 	if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
344 		goto onerror;
345 
346 	return TRUE; /* keep callback */
347 
348  onerror:
349 #ifdef GDA_DEBUG
350 	g_print ("Switching to polling instead of notifications...\n");
351 #endif
352 	g_source_remove (bcnc->priv->ioc_watch_id);
353 	bcnc->priv->ioc_watch_id = 0;
354 	g_io_channel_shutdown (bcnc->priv->ioc, FALSE, NULL);
355 	g_io_channel_unref (bcnc->priv->ioc);
356 	bcnc->priv->ioc = NULL;
357 
358 	setup_results_timer (bcnc);
359 	return FALSE; /* remove callback */
360 }
361 
362 static void
browser_connection_init(BrowserConnection * bcnc)363 browser_connection_init (BrowserConnection *bcnc)
364 {
365 	static guint index = 1;
366 	bcnc->priv = g_new0 (BrowserConnectionPrivate, 1);
367 	bcnc->priv->wrapper = gda_thread_wrapper_new ();
368 	bcnc->priv->ioc = gda_thread_wrapper_get_io_channel (bcnc->priv->wrapper);
369 	if (bcnc->priv->ioc) {
370 		g_io_channel_ref (bcnc->priv->ioc);
371 		bcnc->priv->ioc_watch_id = g_io_add_watch (bcnc->priv->ioc,
372 							   G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
373 							   (GIOFunc) wrapper_ioc_cb, bcnc);
374 	}
375 	else
376 		bcnc->priv->ioc_watch_id = 0;
377 	bcnc->priv->wrapper_jobs = NULL;
378 	bcnc->priv->wrapper_results_timer = 0;
379 	bcnc->priv->long_timer = FALSE;
380 	bcnc->priv->nb_no_job_waits = 0;
381 	bcnc->priv->executed_statements = NULL;
382 
383 	bcnc->priv->name = g_strdup_printf (_("c%u"), index++);
384 	bcnc->priv->cnc = NULL;
385 	bcnc->priv->parser = NULL;
386 	bcnc->priv->variables = NULL;
387 	memset (&(bcnc->priv->dsn_info), 0, sizeof (GdaDsnInfo));
388 	g_mutex_init (&bcnc->priv->mstruct_mutex);
389 	bcnc->priv->p_mstruct_list = NULL;
390 	bcnc->priv->c_mstruct = NULL;
391 	bcnc->priv->mstruct = NULL;
392 
393 	bcnc->priv->meta_store_signal = 0;
394 
395 	bcnc->priv->bfav = NULL;
396 
397 	bcnc->priv->store_cnc = NULL;
398 
399 	bcnc->priv->variables = NULL;
400 	/*g_print ("Created BrowserConnection %p\n", bcnc);*/
401 }
402 
403 static void
transaction_status_changed_cb(G_GNUC_UNUSED GdaThreadWrapper * wrapper,G_GNUC_UNUSED gpointer instance,G_GNUC_UNUSED const gchar * signame,G_GNUC_UNUSED gint n_param_values,G_GNUC_UNUSED const GValue * param_values,G_GNUC_UNUSED gpointer gda_reserved,BrowserConnection * bcnc)404 transaction_status_changed_cb (G_GNUC_UNUSED GdaThreadWrapper *wrapper, G_GNUC_UNUSED gpointer instance,
405 			       G_GNUC_UNUSED const gchar *signame, G_GNUC_UNUSED gint n_param_values,
406 			       G_GNUC_UNUSED const GValue *param_values, G_GNUC_UNUSED gpointer gda_reserved,
407 			       BrowserConnection *bcnc)
408 {
409 	g_signal_emit (bcnc, browser_connection_signals [TRANSACTION_STATUS_CHANGED], 0);
410 }
411 
412 /* executed in sub @bcnc->priv->wrapper's thread */
413 static gpointer
wrapper_meta_store_update(BrowserConnection * bcnc,GError ** error)414 wrapper_meta_store_update (BrowserConnection *bcnc, GError **error)
415 {
416 	gboolean retval;
417 	GdaMetaContext context = {"_tables", 0, NULL, NULL};
418 	retval = gda_connection_update_meta_store (bcnc->priv->cnc, &context, error);
419 
420 	return GINT_TO_POINTER (retval ? 2 : 1);
421 }
422 
423 /* executed in sub @bcnc->priv->wrapper's thread */
424 static gpointer
wrapper_meta_struct_sync(BrowserConnection * bcnc,GError ** error)425 wrapper_meta_struct_sync (BrowserConnection *bcnc, GError **error)
426 {
427 	gboolean retval = TRUE;
428 	GdaMetaStruct *mstruct;
429 
430 	MUTEX_LOCK (bcnc);
431 	g_assert (bcnc->priv->p_mstruct_list);
432 	mstruct = (GdaMetaStruct *) bcnc->priv->p_mstruct_list->data;
433 	/*g_print ("%s() for GdaMetaStruct %p\n", __FUNCTION__, mstruct);*/
434 	bcnc->priv->p_mstruct_list = g_slist_delete_link (bcnc->priv->p_mstruct_list,
435 							  bcnc->priv->p_mstruct_list);
436 	if (bcnc->priv->p_mstruct_list) {
437 		/* don't care about this one */
438 		g_object_unref (G_OBJECT (mstruct));
439 		MUTEX_UNLOCK (bcnc);
440 		return GINT_TO_POINTER (3);
441 	}
442 	else {
443 		if (bcnc->priv->c_mstruct)
444 			g_object_unref (bcnc->priv->c_mstruct);
445 		bcnc->priv->c_mstruct = mstruct;
446 
447 		/*g_print ("Meta struct sync for %p\n", mstruct);*/
448 		retval = gda_meta_struct_complement_all (mstruct, error);
449 		MUTEX_UNLOCK (bcnc);
450 	}
451 
452 #ifdef GDA_DEBUG_NO
453 	GSList *all, *list;
454 	g_print ("%s() %p:\n", __FUNCTION__, bcnc->priv->mstruct);
455 	all = gda_meta_struct_get_all_db_objects (bcnc->priv->mstruct);
456 	for (list = all; list; list = list->next) {
457 		GdaMetaDbObject *dbo = (GdaMetaDbObject *) list->data;
458 		g_print ("DBO, Type %d: short=>[%s] schema=>[%s] full=>[%s]\n", dbo->obj_type,
459 			 dbo->obj_short_name, dbo->obj_schema, dbo->obj_full_name);
460 	}
461 	g_slist_free (all);
462 #endif
463 
464 	return GINT_TO_POINTER (retval ? 2 : 1);
465 }
466 
467 /*
468  * executed in main thread
469  */
470 static void
meta_changed_cb(G_GNUC_UNUSED GdaThreadWrapper * wrapper,G_GNUC_UNUSED GdaMetaStore * store,G_GNUC_UNUSED const gchar * signame,G_GNUC_UNUSED gint n_param_values,G_GNUC_UNUSED const GValue * param_values,G_GNUC_UNUSED gpointer gda_reserved,BrowserConnection * bcnc)471 meta_changed_cb (G_GNUC_UNUSED GdaThreadWrapper *wrapper,
472 		 G_GNUC_UNUSED GdaMetaStore *store,
473 		 G_GNUC_UNUSED const gchar *signame,
474 		 G_GNUC_UNUSED gint n_param_values,
475 		 G_GNUC_UNUSED const GValue *param_values,
476 		 G_GNUC_UNUSED gpointer gda_reserved,
477 		 BrowserConnection *bcnc)
478 {
479 	guint job_id;
480 	GError *lerror = NULL;
481 	GdaMetaStruct *mstruct;
482 
483 	MUTEX_LOCK (bcnc);
484 	mstruct = gda_meta_struct_new (gda_connection_get_meta_store (bcnc->priv->cnc),
485 				       GDA_META_STRUCT_FEATURE_ALL);
486 	bcnc->priv->p_mstruct_list = g_slist_append (bcnc->priv->p_mstruct_list, mstruct);
487 	/*g_print ("%s() Added %p to p_mstruct_list\n", __FUNCTION__, mstruct);*/
488 	MUTEX_UNLOCK (bcnc);
489 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
490 					     (GdaThreadWrapperFunc) wrapper_meta_struct_sync,
491 					     g_object_ref (bcnc), g_object_unref, &lerror);
492 	if (job_id > 0)
493 		push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STRUCT_SYNC,
494 				  _("Analysing database schema"), NULL, NULL);
495 	else if (lerror) {
496 		browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
497 				    lerror->message ? lerror->message : _("No detail"));
498 		g_error_free (lerror);
499 	}
500 }
501 
502 /**
503  * browser_connection_meta_data_changed
504  * @bcnc: a #BrowserConnection
505  *
506  * Call this function if the meta data has been changed directly (ie. for example after
507  * declaring or undeclaring a foreign key). This call creates a new #GdaMetaStruct internally used.
508  */
509 void
browser_connection_meta_data_changed(BrowserConnection * bcnc)510 browser_connection_meta_data_changed (BrowserConnection *bcnc)
511 {
512 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
513 	meta_changed_cb (NULL, NULL, NULL, 0, NULL, NULL, bcnc);
514 }
515 
516 /*
517  * executed in bcnc->priv->wrapper's thread
518  */
519 static gpointer
wrapper_have_meta_store_ready(BrowserConnection * bcnc,GError ** error)520 wrapper_have_meta_store_ready (BrowserConnection *bcnc, GError **error)
521 {
522 	gchar *dict_file_name = NULL;
523 	gboolean update_store = FALSE;
524 	GdaMetaStore *store;
525 	gchar *cnc_string, *cnc_info;
526 
527 	g_object_get (G_OBJECT (bcnc->priv->cnc),
528 		      "dsn", &cnc_info,
529 		      "cnc-string", &cnc_string, NULL);
530 	dict_file_name = config_info_compute_dict_file_name (cnc_info ? gda_config_get_dsn_info (cnc_info) : NULL,
531 							     cnc_string);
532 	g_free (cnc_string);
533 	if (dict_file_name) {
534 		if (BROWSER_IS_VIRTUAL_CONNECTION (bcnc))
535 			/* force meta store update in case of virtual connection */
536 			update_store = TRUE;
537 		else if (! g_file_test (dict_file_name, G_FILE_TEST_EXISTS))
538 			update_store = TRUE;
539 		store = gda_meta_store_new_with_file (dict_file_name);
540 	}
541 	else {
542 		store = gda_meta_store_new (NULL);
543 		if (store)
544 			update_store = TRUE;
545 	}
546 	config_info_update_meta_store_properties (store, bcnc->priv->cnc);
547 
548 	bcnc->priv->dict_file_name = dict_file_name;
549 	g_object_set (G_OBJECT (bcnc->priv->cnc), "meta-store", store, NULL);
550 	if (update_store) {
551 		gboolean retval;
552 		GdaMetaContext context = {"_tables", 0, NULL, NULL};
553 		retval = gda_connection_update_meta_store (bcnc->priv->cnc, &context, error);
554 		if (!retval) {
555 			g_object_unref (store);
556 			return NULL;
557 		}
558 	}
559 
560 	gboolean retval = TRUE;
561 	GdaMetaStruct *mstruct;
562 	mstruct = gda_meta_struct_new (store, GDA_META_STRUCT_FEATURE_ALL);
563 	MUTEX_LOCK (bcnc);
564 	if (bcnc->priv->c_mstruct) {
565 		g_object_unref (bcnc->priv->c_mstruct);
566 		bcnc->priv->c_mstruct = NULL;
567 	}
568 	bcnc->priv->mstruct = mstruct;
569 	retval = gda_meta_struct_complement_all (mstruct, error);
570 	MUTEX_UNLOCK (bcnc);
571 
572 #ifdef GDA_DEBUG_NO
573 	GSList *all, *list;
574 	g_print ("%s() %p:\n", __FUNCTION__, bcnc->priv->mstruct);
575 	all = gda_meta_struct_get_all_db_objects (bcnc->priv->mstruct);
576 	for (list = all; list; list = list->next) {
577 		GdaMetaDbObject *dbo = (GdaMetaDbObject *) list->data;
578 		g_print ("DBO, Type %d: short=>[%s] schema=>[%s] full=>[%s]\n", dbo->obj_type,
579 			 dbo->obj_short_name, dbo->obj_schema, dbo->obj_full_name);
580 	}
581 	g_slist_free (all);
582 #endif
583 	g_object_unref (store);
584 	return retval ? (void*) 0x01 : NULL;
585 }
586 
587 typedef struct {
588         guint jid;
589         GMainLoop *loop;
590         GError **error;
591         GdaThreadWrapper *wrapper;
592 
593         /* out */
594 	gboolean retval;
595 } MainloopData;
596 
597 static gboolean
check_for_meta_store_ready(MainloopData * data)598 check_for_meta_store_ready (MainloopData *data)
599 {
600 	gpointer retval;
601         GError *lerror = NULL;
602 
603         retval = gda_thread_wrapper_fetch_result (data->wrapper, FALSE, data->jid, &lerror);
604         if (retval || (!retval && lerror)) {
605                 /* waiting is finished! */
606                 data->retval = retval ? TRUE : FALSE;
607                 if (lerror)
608                         g_propagate_error (data->error, lerror);
609                 g_main_loop_quit (data->loop);
610                 return FALSE;
611         }
612         return TRUE;
613 }
614 
615 
616 static void
browser_connection_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)617 browser_connection_set_property (GObject *object,
618 				 guint param_id,
619 				 const GValue *value,
620 				 GParamSpec *pspec)
621 {
622         BrowserConnection *bcnc;
623 
624         bcnc = BROWSER_CONNECTION (object);
625         if (bcnc->priv) {
626                 switch (param_id) {
627                 case PROP_GDA_CNC:
628                         bcnc->priv->cnc = (GdaConnection*) g_value_get_object (value);
629                         if (!bcnc->priv->cnc)
630 				return;
631 
632 			/*g_print ("BrowserConnection %p [%s], wrapper %p, GdaConnection %p\n",
633 			  bcnc, bcnc->priv->name, bcnc->priv->wrapper, bcnc->priv->cnc);*/
634 			g_object_ref (bcnc->priv->cnc);
635 			g_object_set (G_OBJECT (bcnc->priv->cnc), "execution-timer", TRUE, NULL);
636 			bcnc->priv->transaction_status_signal =
637 				gda_thread_wrapper_connect_raw (bcnc->priv->wrapper,
638 								bcnc->priv->cnc,
639 								"transaction-status-changed",
640 								FALSE, FALSE,
641 								(GdaThreadWrapperCallback) transaction_status_changed_cb,
642 								bcnc);
643 
644 
645 			/* meta store, open it in a sub thread to avoid locking the GTK thread */
646 			GError *lerror = NULL;
647 			guint jid;
648 			jid = gda_thread_wrapper_execute (bcnc->priv->wrapper,
649 							  (GdaThreadWrapperFunc) wrapper_have_meta_store_ready,
650 							  (gpointer) bcnc,
651 							  NULL, &lerror);
652 			if (jid == 0) {
653 				browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
654 						    lerror->message ? lerror->message : _("No detail"));
655 				g_clear_error (&lerror);
656 			}
657 			else {
658 				GMainLoop *loop;
659 				MainloopData data;
660 
661 				loop = g_main_loop_new (NULL, FALSE);
662 				data.jid = jid;
663 				data.loop = loop;
664 				data.error = &lerror;
665 				data.wrapper = bcnc->priv->wrapper;
666 				g_timeout_add (200, (GSourceFunc) check_for_meta_store_ready, &data);
667 				g_main_loop_run (loop);
668 				g_main_loop_unref (loop);
669 				if (!data.retval) {
670 					browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
671 							    lerror->message ? lerror->message : _("No detail"));
672 					g_clear_error (&lerror);
673 				}
674 				else {
675 					GdaMetaStore *store;
676 					g_object_get (G_OBJECT (bcnc->priv->cnc), "meta-store", &store, NULL);
677 					bcnc->priv->meta_store_signal =
678 						gda_thread_wrapper_connect_raw (bcnc->priv->wrapper, store, "meta-changed",
679 										FALSE, FALSE,
680 										(GdaThreadWrapperCallback) meta_changed_cb,
681 										bcnc);
682 					g_object_unref (store);
683 				}
684 
685 			}
686                         break;
687 		default:
688 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
689 			break;
690                 }
691         }
692 }
693 
694 
695 
696 static void
browser_connection_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)697 browser_connection_get_property (GObject *object,
698 				 guint param_id,
699 				 GValue *value,
700 				 GParamSpec *pspec)
701 {
702         BrowserConnection *bcnc;
703 
704         bcnc = BROWSER_CONNECTION (object);
705         if (bcnc->priv) {
706                 switch (param_id) {
707                 case PROP_GDA_CNC:
708                         g_value_set_object (value, bcnc->priv->cnc);
709                         break;
710 		default:
711 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
712 			break;
713                 }
714         }
715 }
716 
717 static void
clear_dsn_info(BrowserConnection * bcnc)718 clear_dsn_info (BrowserConnection *bcnc)
719 {
720         g_free (bcnc->priv->dsn_info.name);
721         bcnc->priv->dsn_info.name = NULL;
722 
723         g_free (bcnc->priv->dsn_info.provider);
724         bcnc->priv->dsn_info.provider = NULL;
725 
726         g_free (bcnc->priv->dsn_info.description);
727         bcnc->priv->dsn_info.description = NULL;
728 
729         g_free (bcnc->priv->dsn_info.cnc_string);
730         bcnc->priv->dsn_info.cnc_string = NULL;
731 
732         g_free (bcnc->priv->dsn_info.auth_string);
733         bcnc->priv->dsn_info.auth_string = NULL;
734 }
735 
736 static void
fav_changed_cb(G_GNUC_UNUSED ToolsFavorites * bfav,BrowserConnection * bcnc)737 fav_changed_cb (G_GNUC_UNUSED ToolsFavorites *bfav, BrowserConnection *bcnc)
738 {
739 	g_signal_emit (bcnc, browser_connection_signals [FAV_CHANGED], 0);
740 }
741 
742 static void
browser_connection_dispose(GObject * object)743 browser_connection_dispose (GObject *object)
744 {
745 	BrowserConnection *bcnc;
746 
747 	g_return_if_fail (object != NULL);
748 	g_return_if_fail (BROWSER_IS_CONNECTION (object));
749 
750 	bcnc = BROWSER_CONNECTION (object);
751 	if (bcnc->priv) {
752 		if (bcnc->priv->results_timer_id) {
753 			g_source_remove (bcnc->priv->results_timer_id);
754 			bcnc->priv->results_timer_id = 0;
755 		}
756 		if (bcnc->priv->results_list) {
757 			g_slist_foreach (bcnc->priv->results_list, (GFunc) g_free, NULL);
758 			g_slist_free (bcnc->priv->results_list);
759 			bcnc->priv->results_list = NULL;
760 		}
761 
762 		if (bcnc->priv->variables)
763 			g_object_unref (bcnc->priv->variables);
764 
765 		if (bcnc->priv->store_cnc)
766 			g_object_unref (bcnc->priv->store_cnc);
767 
768 		if (bcnc->priv->executed_statements)
769 			g_hash_table_destroy (bcnc->priv->executed_statements);
770 
771 		clear_dsn_info (bcnc);
772 
773 		g_free (bcnc->priv->dict_file_name);
774 
775 		if (bcnc->priv->wrapper_jobs) {
776 			g_slist_foreach (bcnc->priv->wrapper_jobs, (GFunc) wrapper_job_free, NULL);
777 			g_slist_free (bcnc->priv->wrapper_jobs);
778 		}
779 
780 		if (bcnc->priv->wrapper_results_timer > 0)
781 			g_source_remove (bcnc->priv->wrapper_results_timer);
782 
783 		if (bcnc->priv->meta_store_signal)
784 			gda_thread_wrapper_disconnect (bcnc->priv->wrapper,
785 						       bcnc->priv->meta_store_signal);
786 		if (bcnc->priv->transaction_status_signal)
787 			gda_thread_wrapper_disconnect (bcnc->priv->wrapper,
788 						       bcnc->priv->transaction_status_signal);
789 
790 		g_object_unref (bcnc->priv->wrapper);
791 		g_free (bcnc->priv->name);
792 		if (bcnc->priv->c_mstruct)
793 			g_object_unref (bcnc->priv->c_mstruct);
794 		if (bcnc->priv->mstruct)
795 			g_object_unref (bcnc->priv->mstruct);
796 		if (bcnc->priv->p_mstruct_list) {
797 			g_slist_foreach (bcnc->priv->p_mstruct_list, (GFunc) g_object_unref, NULL);
798 			g_slist_free (bcnc->priv->p_mstruct_list);
799 		}
800 		g_mutex_clear (&bcnc->priv->mstruct_mutex);
801 
802 		if (bcnc->priv->cnc)
803 			g_object_unref (bcnc->priv->cnc);
804 
805 		if (bcnc->priv->parser)
806 			g_object_unref (bcnc->priv->parser);
807 		if (bcnc->priv->bfav) {
808 			g_signal_handlers_disconnect_by_func (bcnc->priv->bfav,
809 							      G_CALLBACK (fav_changed_cb), bcnc);
810 			g_object_unref (bcnc->priv->bfav);
811 		}
812 		browser_connection_set_busy_state (bcnc, FALSE, NULL);
813 
814 		if (bcnc->priv->ioc_watch_id > 0) {
815 			g_source_remove (bcnc->priv->ioc_watch_id);
816 			bcnc->priv->ioc_watch_id = 0;
817 		}
818 
819 		if (bcnc->priv->ioc) {
820 			g_io_channel_unref (bcnc->priv->ioc);
821 			bcnc->priv->ioc = NULL;
822 		}
823 
824 		g_free (bcnc->priv);
825 		bcnc->priv = NULL;
826 		/*g_print ("Disposed BrowserConnection %p\n", bcnc);*/
827 	}
828 
829 	/* parent class */
830 	parent_class->dispose (object);
831 }
832 
833 static gboolean
check_for_wrapper_result(BrowserConnection * bcnc)834 check_for_wrapper_result (BrowserConnection *bcnc)
835 {
836 	GError *lerror = NULL;
837 	gpointer exec_res = NULL;
838 	WrapperJob *wj;
839 	gboolean retval = TRUE; /* return FALSE to interrupt current timer */
840 
841 	retval = !setup_results_timer (bcnc);
842 	if (!bcnc->priv->wrapper_jobs) {
843 		gda_thread_wrapper_iterate (bcnc->priv->wrapper, FALSE);
844 		return retval;
845 	}
846 
847 	wj = (WrapperJob*) bcnc->priv->wrapper_jobs->data;
848 	exec_res = gda_thread_wrapper_fetch_result (bcnc->priv->wrapper,
849 						    FALSE,
850 						    wj->job_id, &lerror);
851 	if (exec_res) {
852 		switch (wj->job_type) {
853 		case JOB_TYPE_META_STORE_UPDATE: {
854 			if (GPOINTER_TO_INT (exec_res) == 1) {
855 				browser_show_error (NULL, _("Error while analysing database schema: %s"),
856 						    lerror && lerror->message ? lerror->message : _("No detail"));
857 				g_clear_error (&lerror);
858 			}
859 			else if (! bcnc->priv->meta_store_signal) {
860 				GdaMetaStore *store;
861 				store = gda_connection_get_meta_store (bcnc->priv->cnc);
862 				meta_changed_cb (bcnc->priv->wrapper, store,
863 						 NULL, 0, NULL, NULL, bcnc);
864 				bcnc->priv->meta_store_signal =
865 					gda_thread_wrapper_connect_raw (bcnc->priv->wrapper, store, "meta-changed",
866 									FALSE, FALSE,
867 									(GdaThreadWrapperCallback) meta_changed_cb,
868 									bcnc);
869 
870 			}
871 			break;
872 		}
873 		case JOB_TYPE_META_STRUCT_SYNC: {
874 			if (GPOINTER_TO_INT (exec_res) == 1) {
875 				browser_show_error (NULL, _("Error while analysing database schema: %s"),
876 						    lerror && lerror->message ? lerror->message : _("No detail"));
877 				g_clear_error (&lerror);
878 			}
879 			else if (GPOINTER_TO_INT (exec_res) == 3) {
880 				/* nothing to do */
881 			}
882 			else {
883 				MUTEX_LOCK (bcnc);
884 
885 				if (bcnc->priv->c_mstruct) {
886 					GdaMetaStruct *old_mstruct;
887 					old_mstruct = bcnc->priv->mstruct;
888 					bcnc->priv->mstruct = bcnc->priv->c_mstruct;
889 					bcnc->priv->c_mstruct = NULL;
890 					if (old_mstruct)
891 						g_object_unref (old_mstruct);
892 #ifdef GDA_DEBUG_NO
893 					GSList *all, *list;
894 					g_print ("Signalling change for GdaMetaStruct %p:\n", bcnc->priv->mstruct);
895 					all = gda_meta_struct_get_all_db_objects (bcnc->priv->mstruct);
896 					for (list = all; list; list = list->next) {
897 						GdaMetaDbObject *dbo = (GdaMetaDbObject *) list->data;
898 						g_print ("DBO, Type %d: short=>[%s] schema=>[%s] full=>[%s]\n", dbo->obj_type,
899 							 dbo->obj_short_name, dbo->obj_schema, dbo->obj_full_name);
900 					}
901 					g_slist_free (all);
902 #endif
903 					g_signal_emit (bcnc, browser_connection_signals [META_CHANGED], 0, bcnc->priv->mstruct);
904 				}
905 				MUTEX_UNLOCK (bcnc);
906 			}
907 			break;
908 		}
909 		case JOB_TYPE_STATEMENT_EXECUTE: {
910 			guint *id;
911 			StatementResult *res;
912 
913 			if (! bcnc->priv->executed_statements)
914 				bcnc->priv->executed_statements = g_hash_table_new_full (g_int_hash, g_int_equal,
915 											 g_free,
916 											 (GDestroyNotify) statement_result_free);
917 			id = g_new (guint, 1);
918 			*id = wj->job_id;
919 			res = g_new0 (StatementResult, 1);
920 			if (exec_res == (gpointer) 0x01)
921 				res->error = lerror;
922 			else {
923 				res->result = G_OBJECT (exec_res);
924 				res->last_inserted_row = g_object_get_data (exec_res, "__bcnc_last_inserted_row");
925 				if (res->last_inserted_row)
926 					g_object_set_data (exec_res, "__bcnc_last_inserted_row", NULL);
927 			}
928 			g_hash_table_insert (bcnc->priv->executed_statements, id, res);
929 			break;
930 		}
931 		case JOB_TYPE_CALLBACK:
932 			if (wj->callback) {
933 				wj->callback (bcnc, exec_res == (gpointer) 0x01 ? NULL : exec_res,
934 					      wj->cb_data, lerror);
935 				g_clear_error (&lerror);
936 			}
937 			break;
938 		default:
939 			g_assert_not_reached ();
940 			break;
941 		}
942 
943 		pop_wrapper_job (bcnc, wj);
944 	}
945 
946 	if (bcnc->priv->wrapper_jobs) {
947 		wj = (WrapperJob*) bcnc->priv->wrapper_jobs->data;
948 		if (exec_res)
949 			g_signal_emit (bcnc, browser_connection_signals [BUSY], 0, TRUE, wj->reason);
950 	}
951 	return retval;
952 }
953 
954 /**
955  * browser_connection_new
956  * @cnc: a #GdaConnection
957  *
958  * Creates a new #BrowserConnection object wrapping @cnc. The browser_core_take_connection() method
959  * must be called on the new object to mahe it managed by the browser.
960  *
961  * To close the new connection, use browser_core_close_connection().
962  *
963  * Returns: a new object
964  */
965 BrowserConnection*
browser_connection_new(GdaConnection * cnc)966 browser_connection_new (GdaConnection *cnc)
967 {
968 	BrowserConnection *bcnc;
969 
970 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
971 
972 	bcnc = BROWSER_CONNECTION (g_object_new (BROWSER_TYPE_CONNECTION, "gda-connection", cnc, NULL));
973 
974 	return bcnc;
975 }
976 
977 /**
978  * browser_connection_get_name
979  * @bcnc: a #BrowserConnection
980  *
981  * Returns: @bcnc's name
982  */
983 const gchar *
browser_connection_get_name(BrowserConnection * bcnc)984 browser_connection_get_name (BrowserConnection *bcnc)
985 {
986 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
987 	return bcnc->priv->name;
988 }
989 
990 /**
991  * browser_connection_get_long_name:
992  * @bcnc: a #BrowserConnection
993  *
994  * Get the "long" name of @bcnc
995  *
996  * Returns: a new string
997  */
998 gchar *
browser_connection_get_long_name(BrowserConnection * bcnc)999 browser_connection_get_long_name (BrowserConnection *bcnc)
1000 {
1001 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1002 	const gchar *cncname;
1003 	const GdaDsnInfo *dsn;
1004 	GString *title;
1005 
1006 	dsn = browser_connection_get_information (bcnc);
1007 	cncname = browser_connection_get_name (bcnc);
1008 	title = g_string_new (_("Connection"));
1009 	g_string_append (title, " ");
1010 	g_string_append_printf (title, "'%s'", cncname ? cncname : _("unnamed"));
1011 	if (dsn) {
1012 		if (dsn->name)
1013 			g_string_append_printf (title, ", %s '%s'", _("data source"), dsn->name);
1014 		if (dsn->provider)
1015 			g_string_append_printf (title, " (%s)", dsn->provider);
1016 	}
1017 	return g_string_free (title, FALSE);
1018 }
1019 
1020 /**
1021  * browser_connection_get_information
1022  * @bcnc: a #BrowserConnection
1023  *
1024  * Get some information about the connection
1025  *
1026  * Returns: a pointer to the associated #GdaDsnInfo
1027  */
1028 const GdaDsnInfo *
browser_connection_get_information(BrowserConnection * bcnc)1029 browser_connection_get_information (BrowserConnection *bcnc)
1030 {
1031 	gboolean is_wrapper;
1032 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1033 
1034 	clear_dsn_info (bcnc);
1035 	if (!bcnc->priv->cnc)
1036 		return NULL;
1037 
1038 	g_object_get (G_OBJECT (bcnc->priv->cnc), "is-wrapper", &is_wrapper, NULL);
1039 
1040 	if (!is_wrapper && gda_connection_get_provider_name (bcnc->priv->cnc))
1041 		bcnc->priv->dsn_info.provider = g_strdup (gda_connection_get_provider_name (bcnc->priv->cnc));
1042 	if (gda_connection_get_dsn (bcnc->priv->cnc)) {
1043 		bcnc->priv->dsn_info.name = g_strdup (gda_connection_get_dsn (bcnc->priv->cnc));
1044 		if (! bcnc->priv->dsn_info.provider) {
1045 			GdaDsnInfo *cinfo;
1046 			cinfo = gda_config_get_dsn_info (bcnc->priv->dsn_info.name);
1047 			if (cinfo && cinfo->provider)
1048 				bcnc->priv->dsn_info.provider = g_strdup (cinfo->provider);
1049 		}
1050 	}
1051 	if (gda_connection_get_cnc_string (bcnc->priv->cnc))
1052 		bcnc->priv->dsn_info.cnc_string = g_strdup (gda_connection_get_cnc_string (bcnc->priv->cnc));
1053 	if (is_wrapper && bcnc->priv->dsn_info.cnc_string) {
1054 		GdaQuarkList *ql;
1055 		const gchar *prov;
1056 		ql = gda_quark_list_new_from_string (bcnc->priv->dsn_info.cnc_string);
1057 		prov = gda_quark_list_find (ql, "PROVIDER_NAME");
1058 		if (prov)
1059 			bcnc->priv->dsn_info.provider = g_strdup (prov);
1060 		gda_quark_list_free (ql);
1061 	}
1062 	if (gda_connection_get_authentication (bcnc->priv->cnc))
1063 		bcnc->priv->dsn_info.auth_string = g_strdup (gda_connection_get_authentication (bcnc->priv->cnc));
1064 
1065 	return &(bcnc->priv->dsn_info);
1066 }
1067 
1068 /**
1069  * browser_connection_is_virtual
1070  * @bcnc: a #BrowserConnection
1071  *
1072  * Tells if @bcnc is a virtual connection or not
1073  *
1074  * Returns: %TRUE if @bcnc is a virtual connection
1075  */
1076 gboolean
browser_connection_is_virtual(BrowserConnection * bcnc)1077 browser_connection_is_virtual (BrowserConnection *bcnc)
1078 {
1079 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1080 	if (GDA_IS_VIRTUAL_CONNECTION (bcnc->priv->cnc))
1081 		return TRUE;
1082 	else
1083 		return FALSE;
1084 }
1085 
1086 /**
1087  * browser_connection_is_busy
1088  * @bcnc: a #BrowserConnection
1089  * @out_reason: a pointer to store a copy of the reason @bcnc is busy (will be set
1090  *              to %NULL if @bcnc is not busy), or %NULL
1091  *
1092  * Tells if @bcnc is currently busy or not.
1093  *
1094  * Returns: %TRUE if @bcnc is busy
1095  */
1096 gboolean
browser_connection_is_busy(BrowserConnection * bcnc,gchar ** out_reason)1097 browser_connection_is_busy (BrowserConnection *bcnc, gchar **out_reason)
1098 {
1099 	if (out_reason)
1100 		*out_reason = NULL;
1101 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1102 
1103 	if (out_reason && bcnc->priv->busy_reason)
1104 		*out_reason = g_strdup (bcnc->priv->busy_reason);
1105 
1106 	return bcnc->priv->busy;
1107 }
1108 
1109 /**
1110  * browser_connection_update_meta_data
1111  * @bcnc: a #BrowserConnection
1112  *
1113  * Make @bcnc update its meta store in the background.
1114  */
1115 void
browser_connection_update_meta_data(BrowserConnection * bcnc)1116 browser_connection_update_meta_data (BrowserConnection *bcnc)
1117 {
1118 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
1119 
1120 	if (bcnc->priv->wrapper_jobs) {
1121 		WrapperJob *wj;
1122 		wj = (WrapperJob*) g_slist_last (bcnc->priv->wrapper_jobs)->data;
1123 		if (wj->job_type == JOB_TYPE_META_STORE_UPDATE) {
1124 			/* nothing to do */
1125 			return;
1126 		}
1127 	}
1128 
1129 	if (bcnc->priv->meta_store_signal) {
1130 		gda_thread_wrapper_disconnect (bcnc->priv->wrapper,
1131 					       bcnc->priv->meta_store_signal);
1132 		bcnc->priv->meta_store_signal = 0;
1133 	}
1134 
1135 	guint job_id;
1136 	GError *lerror = NULL;
1137 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
1138 					     (GdaThreadWrapperFunc) wrapper_meta_store_update,
1139 					     g_object_ref (bcnc), g_object_unref, &lerror);
1140 	if (job_id > 0)
1141 		push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STORE_UPDATE,
1142 				  _("Getting database schema information"), NULL, NULL);
1143 	else if (lerror) {
1144 		browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
1145 				    lerror->message ? lerror->message : _("No detail"));
1146 		g_error_free (lerror);
1147 	}
1148 }
1149 
1150 /**
1151  * browser_connection_get_meta_struct
1152  * @bcnc: a #BrowserConnection
1153  *
1154  * Get the #GdaMetaStruct maintained up to date by @bcnc.
1155  *
1156  * Returns: a #GdaMetaStruct, the caller does not have any reference to it.
1157  */
1158 GdaMetaStruct *
browser_connection_get_meta_struct(BrowserConnection * bcnc)1159 browser_connection_get_meta_struct (BrowserConnection *bcnc)
1160 {
1161 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1162 	return bcnc->priv->mstruct;
1163 }
1164 
1165 /**
1166  * browser_connection_get_meta_store
1167  * @bcnc: a #BrowserConnection
1168  *
1169  * Returns: @bcnc's #GdaMetaStore, the caller does not have any reference to it.
1170  */
1171 GdaMetaStore *
browser_connection_get_meta_store(BrowserConnection * bcnc)1172 browser_connection_get_meta_store (BrowserConnection *bcnc)
1173 {
1174 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1175 	return gda_connection_get_meta_store (bcnc->priv->cnc);
1176 }
1177 
1178 /**
1179  * browser_connection_get_dictionary_file
1180  * @bcnc: a #BrowserConnection
1181  *
1182  * Returns: the dictionary file name used by @bcnc, or %NULL
1183  */
1184 const gchar *
browser_connection_get_dictionary_file(BrowserConnection * bcnc)1185 browser_connection_get_dictionary_file (BrowserConnection *bcnc)
1186 {
1187 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1188 	return bcnc->priv->dict_file_name;
1189 }
1190 
1191 /**
1192  * browser_connection_get_transaction_status
1193  * @bcnc: a #BrowserConnection
1194  *
1195  * Retuns: the #GdaTransactionStatus of the connection, or %NULL
1196  */
1197 GdaTransactionStatus *
browser_connection_get_transaction_status(BrowserConnection * bcnc)1198 browser_connection_get_transaction_status (BrowserConnection *bcnc)
1199 {
1200 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1201 	return gda_connection_get_transaction_status (bcnc->priv->cnc);
1202 }
1203 
1204 /**
1205  * browser_connection_begin
1206  * @bcnc: a #BrowserConnection
1207  * @error: a place to store errors, or %NULL
1208  *
1209  * Begins a transaction
1210  */
1211 gboolean
browser_connection_begin(BrowserConnection * bcnc,GError ** error)1212 browser_connection_begin (BrowserConnection *bcnc, GError **error)
1213 {
1214 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1215 	return gda_connection_begin_transaction (bcnc->priv->cnc, NULL,
1216 						 GDA_TRANSACTION_ISOLATION_UNKNOWN, error);
1217 }
1218 
1219 /**
1220  * browser_connection_commit
1221  * @bcnc: a #BrowserConnection
1222  * @error: a place to store errors, or %NULL
1223  *
1224  * Commits a transaction
1225  */
1226 gboolean
browser_connection_commit(BrowserConnection * bcnc,GError ** error)1227 browser_connection_commit (BrowserConnection *bcnc, GError **error)
1228 {
1229 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1230 	return gda_connection_commit_transaction (bcnc->priv->cnc, NULL, error);
1231 }
1232 
1233 /**
1234  * browser_connection_rollback
1235  * @bcnc: a #BrowserConnection
1236  * @error: a place to store errors, or %NULL
1237  *
1238  * Rolls back a transaction
1239  */
1240 gboolean
browser_connection_rollback(BrowserConnection * bcnc,GError ** error)1241 browser_connection_rollback (BrowserConnection *bcnc, GError **error)
1242 {
1243 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1244 	return gda_connection_rollback_transaction (bcnc->priv->cnc, NULL, error);
1245 }
1246 
1247 /**
1248  * browser_connection_get_favorites
1249  * @bcnc: a #BrowserConnection
1250  *
1251  * Get @bcnc's favorites handler
1252  *
1253  * Returns: (transfer none): the #ToolsFavorites used by @bcnc
1254  */
1255 ToolsFavorites *
browser_connection_get_favorites(BrowserConnection * bcnc)1256 browser_connection_get_favorites (BrowserConnection *bcnc)
1257 {
1258 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1259 	if (!bcnc->priv->bfav && !BROWSER_IS_VIRTUAL_CONNECTION (bcnc)) {
1260 		bcnc->priv->bfav = gda_tools_favorites_new (gda_connection_get_meta_store (bcnc->priv->cnc));
1261 		g_signal_connect (bcnc->priv->bfav, "favorites-changed",
1262 				  G_CALLBACK (fav_changed_cb), bcnc);
1263 	}
1264 	return bcnc->priv->bfav;
1265 }
1266 
1267 /**
1268  * browser_connection_get_completions
1269  * @bcnc: a #BrowserConnection
1270  * @sql:
1271  * @start:
1272  * @end:
1273  *
1274  * See gda_completion_list_get()
1275  *
1276  * Returns: a new array of strings, or NULL (use g_strfreev() to free the returned array)
1277  */
1278 gchar **
browser_connection_get_completions(BrowserConnection * bcnc,const gchar * sql,gint start,gint end)1279 browser_connection_get_completions (BrowserConnection *bcnc, const gchar *sql,
1280 				    gint start, gint end)
1281 {
1282 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1283 	return gda_completion_list_get (bcnc->priv->cnc, sql, start, end);
1284 }
1285 
1286 
1287 /**
1288  * browser_connection_create_parser
1289  * @bcnc: a #BrowserConnection
1290  *
1291  * Get a new #GdaSqlParser object for @bcnc
1292  *
1293  * Returns: a new #GdaSqlParser
1294  */
1295 GdaSqlParser *
browser_connection_create_parser(BrowserConnection * bcnc)1296 browser_connection_create_parser (BrowserConnection *bcnc)
1297 {
1298 	GdaSqlParser *parser;
1299 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1300 
1301 	parser = gda_connection_create_parser (bcnc->priv->cnc);
1302 	if (!parser)
1303 		parser = gda_sql_parser_new ();
1304 	return parser;
1305 }
1306 
1307 /**
1308  * browser_connection_render_pretty_sql
1309  * @bcnc: a #BrowserConnection
1310  * @stmt: a #GdaStatement
1311  *
1312  * Renders @stmt as SQL well indented
1313  *
1314  * Returns: a new string
1315  */
1316 gchar *
browser_connection_render_pretty_sql(BrowserConnection * bcnc,GdaStatement * stmt)1317 browser_connection_render_pretty_sql (BrowserConnection *bcnc, GdaStatement *stmt)
1318 {
1319 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1320 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
1321 
1322 	return gda_statement_to_sql_extended (stmt, bcnc->priv->cnc, NULL,
1323 					      GDA_STATEMENT_SQL_PRETTY |
1324 					      GDA_STATEMENT_SQL_PARAMS_SHORT,
1325 					      NULL, NULL);
1326 }
1327 
1328 typedef struct {
1329 	GdaConnection *cnc;
1330 	GdaStatement *stmt;
1331 	GdaSet *params;
1332 	GdaStatementModelUsage model_usage;
1333 	gboolean need_last_insert_row;
1334 } StmtExecData;
1335 
1336 /* executed in sub @bcnc->priv->wrapper's thread */
1337 static gpointer
wrapper_statement_execute(StmtExecData * data,GError ** error)1338 wrapper_statement_execute (StmtExecData *data, GError **error)
1339 {
1340 	GObject *obj;
1341 	GdaSet *last_insert_row = NULL;
1342 	GError *lerror = NULL;
1343 	obj = gda_connection_statement_execute (data->cnc, data->stmt,
1344 						data->params, data->model_usage,
1345 						data->need_last_insert_row ? &last_insert_row : NULL,
1346 						&lerror);
1347 	if (obj) {
1348 		if (GDA_IS_DATA_MODEL (obj))
1349 			/* force loading of rows if necessary */
1350 			gda_data_model_get_n_rows ((GdaDataModel*) obj);
1351 		else if (last_insert_row)
1352 			g_object_set_data (obj, "__bcnc_last_inserted_row", last_insert_row);
1353 	}
1354 	else {
1355 		if (lerror)
1356 			g_propagate_error (error, lerror);
1357 		else {
1358 			g_warning (_("Execution reported an undefined error, please report error to "
1359 				     "http://bugzilla.gnome.org/ for the \"libgda\" product"));
1360 			g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_INTERNAL_COMMAND_ERROR,
1361 				     "%s", _("No detail"));
1362 		}
1363 	}
1364 	return obj ? obj : (gpointer) 0x01;
1365 }
1366 
1367 /**
1368  * browser_connection_execute_statement
1369  * @bcnc: a #BrowserConnection
1370  * @stmt: a #GdaStatement
1371  * @params: a #GdaSet as parameters, or %NULL
1372  * @model_usage: how the returned data model (if any) will be used
1373  * @need_last_insert_row: %TRUE if the values of the last interted row must be computed
1374  * @error: a place to store errors, or %NULL
1375  *
1376  * Executes @stmt by @bcnc. Unless specific requirements, it's easier to use
1377  * browser_connection_execute_statement_cb().
1378  *
1379  * Returns: a job ID, to be used with browser_connection_execution_get_result(), or %0 if an
1380  * error occurred
1381  */
1382 guint
browser_connection_execute_statement(BrowserConnection * bcnc,GdaStatement * stmt,GdaSet * params,GdaStatementModelUsage model_usage,gboolean need_last_insert_row,GError ** error)1383 browser_connection_execute_statement (BrowserConnection *bcnc,
1384 				      GdaStatement *stmt,
1385 				      GdaSet *params,
1386 				      GdaStatementModelUsage model_usage,
1387 				      gboolean need_last_insert_row,
1388 				      GError **error)
1389 {
1390 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
1391 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), 0);
1392 	g_return_val_if_fail (!params || GDA_IS_SET (params), 0);
1393 
1394 	StmtExecData *data;
1395 	guint job_id;
1396 
1397 	data = g_new0 (StmtExecData, 1);
1398 	data->cnc = bcnc->priv->cnc;
1399 	data->stmt = stmt;
1400 	data->params = params;
1401 	data->model_usage = model_usage;
1402 	data->need_last_insert_row = need_last_insert_row;
1403 
1404 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
1405 					     (GdaThreadWrapperFunc) wrapper_statement_execute,
1406 					     data, (GDestroyNotify) g_free, error);
1407 	if (job_id > 0)
1408 		push_wrapper_job (bcnc, job_id, JOB_TYPE_STATEMENT_EXECUTE,
1409 				  _("Executing a query"), NULL, NULL);
1410 
1411 	return job_id;
1412 }
1413 
1414 typedef struct {
1415 	GdaConnection *cnc;
1416 	GdaDataModel *model;
1417 } RerunSelectData;
1418 
1419 /* executed in @bcnc->priv->wrapper's sub thread */
1420 static gpointer
wrapper_rerun_select(RerunSelectData * data,GError ** error)1421 wrapper_rerun_select (RerunSelectData *data, GError **error)
1422 {
1423 	gboolean retval;
1424 
1425 	retval = gda_data_select_rerun (GDA_DATA_SELECT (data->model), error);
1426 	return retval ? data->model : (gpointer) 0x01;
1427 }
1428 
1429 /**
1430  * browser_connection_rerun_select
1431  * @bcnc: a #BrowserConnection object
1432  * @model: a #GdaDataModel, which has to ba a #GdaDataSelect
1433  * @error: a place to store errors, or %NULL
1434  *
1435  * Re-execute @model
1436  *
1437  * Returns: a job ID, or %0 if an error occurred
1438  */
1439 guint
browser_connection_rerun_select(BrowserConnection * bcnc,GdaDataModel * model,GError ** error)1440 browser_connection_rerun_select (BrowserConnection *bcnc,
1441 				 GdaDataModel *model,
1442 				 GError **error)
1443 {
1444 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
1445 	g_return_val_if_fail (GDA_IS_DATA_SELECT (model), 0);
1446 
1447 	RerunSelectData *data;
1448 	guint job_id;
1449 
1450 	data = g_new0 (RerunSelectData, 1);
1451 	data->cnc = bcnc->priv->cnc;
1452 	data->model = model;
1453 
1454 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
1455 					     (GdaThreadWrapperFunc) wrapper_rerun_select,
1456 					     data, (GDestroyNotify) g_free, error);
1457 	if (job_id > 0)
1458 		push_wrapper_job (bcnc, job_id, JOB_TYPE_STATEMENT_EXECUTE,
1459 				  _("Executing a query"), NULL, NULL);
1460 
1461 	return job_id;
1462 }
1463 
1464 
1465 /**
1466  * browser_connection_execution_get_result
1467  * @bcnc: a #BrowserConnection
1468  * @exec_id: the ID of the excution
1469  * @last_insert_row: a place to store the last inserted row, if any, or %NULL
1470  * @error: a place to store errors, or %NULL
1471  *
1472  * Pick up the result of the @exec_id's execution.
1473  *
1474  * Returns: the execution result, or %NULL if either an error occurred or the result is not yet ready
1475  */
1476 GObject *
browser_connection_execution_get_result(BrowserConnection * bcnc,guint exec_id,GdaSet ** last_insert_row,GError ** error)1477 browser_connection_execution_get_result (BrowserConnection *bcnc, guint exec_id,
1478 					 GdaSet **last_insert_row, GError **error)
1479 {
1480 	StatementResult *res;
1481 	guint id;
1482 	GObject *retval;
1483 
1484 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
1485 	g_return_val_if_fail (exec_id > 0, NULL);
1486 
1487 	if (! bcnc->priv->executed_statements)
1488 		return NULL;
1489 
1490 	id = exec_id;
1491 	res = g_hash_table_lookup (bcnc->priv->executed_statements, &id);
1492 	if (!res)
1493 		return NULL;
1494 
1495 	retval = res->result;
1496 	res->result = NULL;
1497 
1498 	if (last_insert_row) {
1499 		*last_insert_row = res->last_inserted_row;
1500 		res->last_inserted_row = NULL;
1501 	}
1502 
1503 	if (res->error) {
1504 		g_propagate_error (error, res->error);
1505 		res->error = NULL;
1506 	}
1507 
1508 	g_hash_table_remove (bcnc->priv->executed_statements, &id);
1509 	/*if (GDA_IS_DATA_MODEL (retval))
1510 	  gda_data_model_dump (GDA_DATA_MODEL (retval), NULL);*/
1511 	return retval;
1512 }
1513 
1514 /**
1515  * browser_connection_job_cancel:
1516  * @bcnc: a #BrowserConnection
1517  * @job_id: the job_id to cancel
1518  *
1519  * Cancel a job, from the job ID returned by browser_connection_ldap_describe_entry()
1520  * or browser_connection_ldap_get_entry_children().
1521  */
1522 void
browser_connection_job_cancel(BrowserConnection * bcnc,guint job_id)1523 browser_connection_job_cancel (BrowserConnection *bcnc, guint job_id)
1524 {
1525 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
1526 	g_return_if_fail (job_id > 0);
1527 
1528 	TO_IMPLEMENT;
1529 }
1530 
1531 static gboolean query_exec_fetch_cb (BrowserConnection *bcnc);
1532 
1533 typedef struct {
1534 	guint exec_id;
1535 	gboolean need_last_insert_row;
1536 	BrowserConnectionExecuteCallback callback;
1537 	gpointer cb_data;
1538 } ExecCallbackData;
1539 
1540 /**
1541  * browser_connection_execute_statement_cb
1542  * @bcnc: a #BrowserConnection
1543  * @stmt: a #GdaStatement
1544  * @params: a #GdaSet as parameters, or %NULL
1545  * @model_usage: how the returned data model (if any) will be used
1546  * @need_last_insert_row: %TRUE if the values of the last interted row must be computed
1547  * @callback: the function to call when statement has been executed
1548  * @data: data to pass to @callback, or %NULL
1549  * @error: a place to store errors, or %NULL
1550  *
1551  * Executes @stmt by @bcnc and calls @callback when done. This occurs in the UI thread and avoids
1552  * having to set up a waiting mechanism to call browser_connection_execution_get_result()
1553  * repeatedly.
1554  *
1555  * Returns: a job ID, or %0 if an error occurred
1556  */
1557 guint
browser_connection_execute_statement_cb(BrowserConnection * bcnc,GdaStatement * stmt,GdaSet * params,GdaStatementModelUsage model_usage,gboolean need_last_insert_row,BrowserConnectionExecuteCallback callback,gpointer data,GError ** error)1558 browser_connection_execute_statement_cb (BrowserConnection *bcnc,
1559 					 GdaStatement *stmt,
1560 					 GdaSet *params,
1561 					 GdaStatementModelUsage model_usage,
1562 					 gboolean need_last_insert_row,
1563 					 BrowserConnectionExecuteCallback callback,
1564 					 gpointer data,
1565 					 GError **error)
1566 {
1567 	guint exec_id;
1568 	g_return_val_if_fail (callback, 0);
1569 
1570 	exec_id = browser_connection_execute_statement (bcnc, stmt, params, model_usage,
1571 							need_last_insert_row, error);
1572 	if (!exec_id)
1573 		return 0;
1574 	ExecCallbackData *cbdata;
1575 	cbdata = g_new0 (ExecCallbackData, 1);
1576 	cbdata->exec_id = exec_id;
1577 	cbdata->need_last_insert_row = need_last_insert_row;
1578 	cbdata->callback = callback;
1579 	cbdata->cb_data = data;
1580 
1581 	bcnc->priv->results_list = g_slist_append (bcnc->priv->results_list, cbdata);
1582 	if (! bcnc->priv->results_timer_id)
1583 		bcnc->priv->results_timer_id = g_timeout_add (200,
1584 							      (GSourceFunc) query_exec_fetch_cb,
1585 							      bcnc);
1586 	return exec_id;
1587 }
1588 
1589 /**
1590  * browser_connection_rerun_select_cb
1591  * @bcnc: a #BrowserConnection object
1592  * @model: a #GdaDataModel, which has to ba a #GdaDataSelect
1593  * @callback: the function to call when statement has been executed
1594  * @data: data to pass to @callback, or %NULL
1595  * @error: a place to store errors, or %NULL
1596  *
1597  * Re-execute @model.
1598  *
1599  * Warning: gda_data_model_freeze() and gda_data_model_thaw() should be used
1600  * before and after this call since the model will signal its changes in a thread
1601  * which is not the GUI thread.
1602  *
1603  * Returns: a job ID, or %0 if an error occurred
1604  */
1605 guint
browser_connection_rerun_select_cb(BrowserConnection * bcnc,GdaDataModel * model,BrowserConnectionExecuteCallback callback,gpointer data,GError ** error)1606 browser_connection_rerun_select_cb (BrowserConnection *bcnc,
1607 				    GdaDataModel *model,
1608 				    BrowserConnectionExecuteCallback callback,
1609 				    gpointer data,
1610 				    GError **error)
1611 {
1612 	guint exec_id;
1613 	g_return_val_if_fail (callback, 0);
1614 
1615 	exec_id = browser_connection_rerun_select (bcnc, model, error);
1616 	if (!exec_id)
1617 		return 0;
1618 	ExecCallbackData *cbdata;
1619 	cbdata = g_new0 (ExecCallbackData, 1);
1620 	cbdata->exec_id = exec_id;
1621 	cbdata->need_last_insert_row = FALSE;
1622 	cbdata->callback = callback;
1623 	cbdata->cb_data = data;
1624 
1625 	bcnc->priv->results_list = g_slist_append (bcnc->priv->results_list, cbdata);
1626 	if (! bcnc->priv->results_timer_id)
1627 		bcnc->priv->results_timer_id = g_timeout_add (200,
1628 							      (GSourceFunc) query_exec_fetch_cb,
1629 							      bcnc);
1630 	return exec_id;
1631 }
1632 
1633 
1634 static gboolean
query_exec_fetch_cb(BrowserConnection * bcnc)1635 query_exec_fetch_cb (BrowserConnection *bcnc)
1636 {
1637 	GObject *res;
1638 	GError *lerror = NULL;
1639 	ExecCallbackData *cbdata;
1640 	GdaSet *last_inserted_row = NULL;
1641 
1642 	if (!bcnc->priv->results_list)
1643 		goto out;
1644 
1645 	cbdata = (ExecCallbackData *) bcnc->priv->results_list->data;
1646 
1647 	if (cbdata->need_last_insert_row)
1648 		res = browser_connection_execution_get_result (bcnc,
1649 							       cbdata->exec_id,
1650 							       &last_inserted_row,
1651 							       &lerror);
1652 	else
1653 		res = browser_connection_execution_get_result (bcnc,
1654 							       cbdata->exec_id, NULL,
1655 							       &lerror);
1656 
1657 	if (res || lerror) {
1658 		cbdata->callback (bcnc, cbdata->exec_id, res, last_inserted_row, lerror, cbdata->cb_data);
1659 		if (res)
1660 			g_object_unref (res);
1661 		if (last_inserted_row)
1662 			g_object_unref (last_inserted_row);
1663 		g_clear_error (&lerror);
1664 
1665 		bcnc->priv->results_list = g_slist_remove (bcnc->priv->results_list, cbdata);
1666 		g_free (cbdata);
1667 	}
1668 
1669  out:
1670 	if (! bcnc->priv->results_list) {
1671 		bcnc->priv->results_timer_id = 0;
1672 		return FALSE;
1673 	}
1674 	else
1675 		return TRUE; /* keep timer */
1676 }
1677 
1678 
1679 /**
1680  * browser_connection_normalize_sql_statement
1681  * @bcnc: a #BrowserConnection
1682  * @sqlst: a #GdaSqlStatement
1683  * @error: a place to store errors, or %NULL
1684  *
1685  * See gda_sql_statement_normalize().
1686  *
1687  * Returns: %TRUE if no error occurred
1688  */
1689 gboolean
browser_connection_normalize_sql_statement(BrowserConnection * bcnc,GdaSqlStatement * sqlst,GError ** error)1690 browser_connection_normalize_sql_statement (BrowserConnection *bcnc,
1691 					    GdaSqlStatement *sqlst, GError **error)
1692 {
1693 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1694 
1695 	return gda_sql_statement_normalize (sqlst, bcnc->priv->cnc, error);
1696 }
1697 
1698 /**
1699  * browser_connection_check_sql_statement_validify
1700  */
1701 gboolean
browser_connection_check_sql_statement_validify(BrowserConnection * bcnc,GdaSqlStatement * sqlst,GError ** error)1702 browser_connection_check_sql_statement_validify (BrowserConnection *bcnc,
1703 						 GdaSqlStatement *sqlst, GError **error)
1704 {
1705 	g_return_val_if_fail (sqlst, FALSE);
1706 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1707 
1708 	/* check the structure first */
1709         if (!gda_sql_statement_check_structure (sqlst, error))
1710                 return FALSE;
1711 
1712 	return gda_sql_statement_check_validity_m (sqlst, bcnc->priv->mstruct, error);
1713 }
1714 
1715 
1716 
1717 /*
1718  * DOES NOT emit any signal
1719  */
1720 void
browser_connection_set_busy_state(BrowserConnection * bcnc,gboolean busy,const gchar * busy_reason)1721 browser_connection_set_busy_state (BrowserConnection *bcnc, gboolean busy, const gchar *busy_reason)
1722 {
1723 	if (bcnc->priv->busy_reason) {
1724 		g_free (bcnc->priv->busy_reason);
1725 		bcnc->priv->busy_reason = NULL;
1726 	}
1727 
1728 	bcnc->priv->busy = busy;
1729 	if (busy_reason)
1730 		bcnc->priv->busy_reason = g_strdup (busy_reason);
1731 }
1732 
1733 /*
1734  *
1735  * Preferences
1736  *
1737  */
1738 #define DBTABLE_PREFERENCES_TABLE_NAME "gda_sql_dbtable_preferences"
1739 #define DBTABLE_PREFERENCES_TABLE_DESC \
1740         "<table name=\"" DBTABLE_PREFERENCES_TABLE_NAME "\"> "                            \
1741         "   <column name=\"table_schema\" pkey=\"TRUE\"/>"             \
1742         "   <column name=\"table_name\" pkey=\"TRUE\"/>"                              \
1743         "   <column name=\"table_column\" nullok=\"TRUE\" pkey=\"TRUE\"/>"                              \
1744         "   <column name=\"att_name\"/>"                          \
1745         "   <column name=\"att_value\"/>"                           \
1746         "</table>"
1747 
1748 static gboolean
meta_store_addons_init(BrowserConnection * bcnc,GError ** error)1749 meta_store_addons_init (BrowserConnection *bcnc, GError **error)
1750 {
1751 	GError *lerror = NULL;
1752 	GdaMetaStore *store;
1753 
1754 	if (!bcnc->priv->cnc) {
1755 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR,
1756 			     "%s", _("Connection not yet opened"));
1757 		return FALSE;
1758 	}
1759 	store = gda_connection_get_meta_store (bcnc->priv->cnc);
1760 	if (!gda_meta_store_schema_add_custom_object (store, DBTABLE_PREFERENCES_TABLE_DESC, &lerror)) {
1761                 g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR,
1762 			     "%s", _("Can't initialize dictionary to store table preferences"));
1763 		g_warning ("Can't initialize dictionary to store dbtable_preferences :%s",
1764 			   lerror && lerror->message ? lerror->message : "No detail");
1765 		if (lerror)
1766 			g_error_free (lerror);
1767                 return FALSE;
1768         }
1769 
1770 	bcnc->priv->store_cnc = g_object_ref (gda_meta_store_get_internal_connection (store));
1771 	return TRUE;
1772 }
1773 
1774 
1775 /**
1776  * browser_connection_set_table_column_attribute
1777  * @bcnc:
1778  * @dbo:
1779  * @column:
1780  * @attr_name: attribute name, not %NULL
1781  * @value: value to set, or %NULL to unset
1782  * @error:
1783  *
1784  *
1785  * Returns: %TRUE if no error occurred
1786  */
1787 gboolean
browser_connection_set_table_column_attribute(BrowserConnection * bcnc,GdaMetaTable * table,GdaMetaTableColumn * column,const gchar * attr_name,const gchar * value,GError ** error)1788 browser_connection_set_table_column_attribute (BrowserConnection *bcnc,
1789 					       GdaMetaTable *table,
1790 					       GdaMetaTableColumn *column,
1791 					       const gchar *attr_name,
1792 					       const gchar *value, GError **error)
1793 {
1794 	GdaConnection *store_cnc;
1795 
1796 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1797 	g_return_val_if_fail (table, FALSE);
1798 	g_return_val_if_fail (column, FALSE);
1799 	g_return_val_if_fail (attr_name, FALSE);
1800 
1801 	if (! bcnc->priv->store_cnc &&
1802 	    ! meta_store_addons_init (bcnc, error))
1803 		return FALSE;
1804 
1805 	store_cnc = bcnc->priv->store_cnc;
1806 	if (! gda_lockable_trylock (GDA_LOCKABLE (store_cnc))) {
1807 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR,
1808 			     "%s", _("Can't initialize transaction to access favorites"));
1809 		return FALSE;
1810 	}
1811 	/* begin a transaction */
1812 	if (! gda_connection_begin_transaction (store_cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL)) {
1813 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR,
1814 			     "%s", _("Can't initialize transaction to access favorites"));
1815 		gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
1816                 return FALSE;
1817 	}
1818 
1819 	/* delete existing attribute */
1820 	GdaStatement *stmt;
1821 	GdaSqlBuilder *builder;
1822 	GdaSet *params;
1823 	GdaSqlBuilderId op_ids[4];
1824 	GdaMetaDbObject *dbo = (GdaMetaDbObject *) table;
1825 
1826 	params = gda_set_new_inline (5, "schema", G_TYPE_STRING, dbo->obj_schema,
1827 				     "name", G_TYPE_STRING, dbo->obj_name,
1828 				     "column", G_TYPE_STRING, column->column_name,
1829 				     "attname", G_TYPE_STRING, attr_name,
1830 				     "attvalue", G_TYPE_STRING, value);
1831 
1832 	builder = gda_sql_builder_new (GDA_SQL_STATEMENT_DELETE);
1833 	gda_sql_builder_set_table (builder, DBTABLE_PREFERENCES_TABLE_NAME);
1834 	op_ids[0] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1835 					      gda_sql_builder_add_id (builder, "table_schema"),
1836 					      gda_sql_builder_add_param (builder, "schema", G_TYPE_STRING,
1837 									 FALSE), 0);
1838 	op_ids[1] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1839 					      gda_sql_builder_add_id (builder, "table_name"),
1840 					      gda_sql_builder_add_param (builder, "name", G_TYPE_STRING,
1841 									 FALSE), 0);
1842 	op_ids[2] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1843 					      gda_sql_builder_add_id (builder, "table_column"),
1844 					      gda_sql_builder_add_param (builder, "column", G_TYPE_STRING,
1845 									 FALSE), 0);
1846 	op_ids[3] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1847 					      gda_sql_builder_add_id (builder, "att_name"),
1848 					      gda_sql_builder_add_param (builder, "attname", G_TYPE_STRING,
1849 									 FALSE), 0);
1850 	gda_sql_builder_set_where (builder,
1851 				   gda_sql_builder_add_cond_v (builder, GDA_SQL_OPERATOR_TYPE_AND,
1852 							       op_ids, 4));
1853 	stmt = gda_sql_builder_get_statement (builder, error);
1854 	g_object_unref (G_OBJECT (builder));
1855 	if (!stmt)
1856 		goto err;
1857 	if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
1858 		g_object_unref (stmt);
1859 		goto err;
1860 	}
1861 	g_object_unref (stmt);
1862 
1863 	/* insert new attribute if necessary */
1864 	if (value) {
1865 		builder = gda_sql_builder_new (GDA_SQL_STATEMENT_INSERT);
1866 		gda_sql_builder_set_table (builder, DBTABLE_PREFERENCES_TABLE_NAME);
1867 		gda_sql_builder_add_field_value_id (builder,
1868 					      gda_sql_builder_add_id (builder, "table_schema"),
1869 					      gda_sql_builder_add_param (builder, "schema", G_TYPE_STRING, FALSE));
1870 		gda_sql_builder_add_field_value_id (builder,
1871 					      gda_sql_builder_add_id (builder, "table_name"),
1872 					      gda_sql_builder_add_param (builder, "name", G_TYPE_STRING, FALSE));
1873 		gda_sql_builder_add_field_value_id (builder,
1874 					      gda_sql_builder_add_id (builder, "table_column"),
1875 					      gda_sql_builder_add_param (builder, "column", G_TYPE_STRING, FALSE));
1876 		gda_sql_builder_add_field_value_id (builder,
1877 					      gda_sql_builder_add_id (builder, "att_name"),
1878 					      gda_sql_builder_add_param (builder, "attname", G_TYPE_STRING, FALSE));
1879 		gda_sql_builder_add_field_value_id (builder,
1880 					      gda_sql_builder_add_id (builder, "att_value"),
1881 					      gda_sql_builder_add_param (builder, "attvalue", G_TYPE_STRING, FALSE));
1882 		stmt = gda_sql_builder_get_statement (builder, error);
1883 		g_object_unref (G_OBJECT (builder));
1884 		if (!stmt)
1885 			goto err;
1886 		if (gda_connection_statement_execute_non_select (store_cnc, stmt, params, NULL, error) == -1) {
1887 			g_object_unref (stmt);
1888 			goto err;
1889 		}
1890 		g_object_unref (stmt);
1891 	}
1892 
1893 	if (! gda_connection_commit_transaction (store_cnc, NULL, NULL)) {
1894 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR,
1895 			     "%s", _("Can't commit transaction to access favorites"));
1896 		goto err;
1897 	}
1898 
1899 	g_object_unref (params);
1900 	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
1901 	/*
1902 	g_print ("%s(table=>%s, column=>%s, value=>%s)\n", __FUNCTION__, GDA_META_DB_OBJECT (table)->obj_full_name,
1903 		 column->column_name, value);
1904 	*/
1905 	g_signal_emit (bcnc, browser_connection_signals [TABLE_COLUMN_PREF_CHANGED], 0,
1906 		       table, column, attr_name, value);
1907 
1908 	return TRUE;
1909 
1910  err:
1911 	g_object_unref (params);
1912 	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
1913 	gda_connection_rollback_transaction (store_cnc, NULL, NULL);
1914 	return FALSE;
1915 }
1916 
1917 /**
1918  * browser_connection_get_table_column_attribute
1919  * @bcnc:
1920  * @dbo:
1921  * @column: may be %NULL
1922  * @attr_name: attribute name, not %NULL
1923  * @error:
1924  *
1925  *
1926  * Returns: the requested attribute (as a new string), or %NULL if not set or if an error occurred
1927  */
1928 gchar *
browser_connection_get_table_column_attribute(BrowserConnection * bcnc,GdaMetaTable * table,GdaMetaTableColumn * column,const gchar * attr_name,GError ** error)1929 browser_connection_get_table_column_attribute  (BrowserConnection *bcnc,
1930 						GdaMetaTable *table,
1931 						GdaMetaTableColumn *column,
1932 						const gchar *attr_name,
1933 						GError **error)
1934 {
1935 	GdaConnection *store_cnc;
1936 	gchar *retval = NULL;
1937 
1938 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
1939 	g_return_val_if_fail (table, FALSE);
1940 	g_return_val_if_fail (column, FALSE);
1941 	g_return_val_if_fail (attr_name, FALSE);
1942 
1943 	if (! bcnc->priv->store_cnc &&
1944 	    ! meta_store_addons_init (bcnc, error))
1945 		return FALSE;
1946 
1947 	store_cnc = bcnc->priv->store_cnc;
1948 	if (! gda_lockable_trylock (GDA_LOCKABLE (store_cnc))) {
1949 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_STORED_DATA_ERROR,
1950 			     "%s", _("Can't initialize transaction to access favorites"));
1951 		return FALSE;
1952 	}
1953 
1954 	/* SELECT */
1955 	GdaStatement *stmt;
1956 	GdaSqlBuilder *builder;
1957 	GdaSet *params;
1958 	GdaSqlBuilderId op_ids[4];
1959 	GdaDataModel *model = NULL;
1960 	const GValue *cvalue;
1961 	GdaMetaDbObject *dbo = (GdaMetaDbObject *) table;
1962 
1963 	params = gda_set_new_inline (4, "schema", G_TYPE_STRING, dbo->obj_schema,
1964 				     "name", G_TYPE_STRING, dbo->obj_name,
1965 				     "column", G_TYPE_STRING, column->column_name,
1966 				     "attname", G_TYPE_STRING, attr_name);
1967 
1968 	builder = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
1969 	gda_sql_builder_select_add_target_id (builder,
1970 					      gda_sql_builder_add_id (builder, DBTABLE_PREFERENCES_TABLE_NAME),
1971 					      NULL);
1972 	gda_sql_builder_select_add_field (builder, "att_value", NULL, NULL);
1973 	op_ids[0] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1974 					      gda_sql_builder_add_id (builder, "table_schema"),
1975 					      gda_sql_builder_add_param (builder, "schema", G_TYPE_STRING,
1976 									 FALSE), 0);
1977 	op_ids[1] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1978 					      gda_sql_builder_add_id (builder, "table_name"),
1979 					      gda_sql_builder_add_param (builder, "name", G_TYPE_STRING,
1980 									 FALSE), 0);
1981 	op_ids[2] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1982 					      gda_sql_builder_add_id (builder, "table_column"),
1983 					      gda_sql_builder_add_param (builder, "column", G_TYPE_STRING,
1984 									 FALSE), 0);
1985 	op_ids[3] = gda_sql_builder_add_cond (builder, GDA_SQL_OPERATOR_TYPE_EQ,
1986 					      gda_sql_builder_add_id (builder, "att_name"),
1987 					      gda_sql_builder_add_param (builder, "attname", G_TYPE_STRING,
1988 									 FALSE), 0);
1989 	gda_sql_builder_set_where (builder,
1990 				   gda_sql_builder_add_cond_v (builder, GDA_SQL_OPERATOR_TYPE_AND,
1991 							       op_ids, 4));
1992 	stmt = gda_sql_builder_get_statement (builder, error);
1993 	g_object_unref (G_OBJECT (builder));
1994 	if (!stmt)
1995 		goto out;
1996 
1997 	model = gda_connection_statement_execute_select (store_cnc, stmt, params, error);
1998 	g_object_unref (stmt);
1999 	if (!model)
2000 		goto out;
2001 
2002 	/*gda_data_model_dump (model, NULL);*/
2003 	if (gda_data_model_get_n_rows (model) == 0)
2004 		goto out;
2005 
2006 	cvalue = gda_data_model_get_value_at (model, 0, 0, error);
2007 	if (cvalue)
2008 		retval = g_value_dup_string (cvalue);
2009 
2010  out:
2011 	if (model)
2012 		g_object_unref (model);
2013 	g_object_unref (params);
2014 	gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
2015 
2016 	return retval;
2017 }
2018 
2019 /**
2020  * browser_connection_define_ui_plugins_for_batch
2021  * @bcnc: a #BrowserConnection object
2022  * @batch: a #GdaBatch
2023  * @params: a #GdaSet (usually created with gda_batch_get_parameters())
2024  *
2025  * Calls browser_connection_define_ui_plugins_for_stmt() for each statement in @batch
2026  */
2027 void
browser_connection_define_ui_plugins_for_batch(BrowserConnection * bcnc,GdaBatch * batch,GdaSet * params)2028 browser_connection_define_ui_plugins_for_batch (BrowserConnection *bcnc, GdaBatch *batch, GdaSet *params)
2029 {
2030 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
2031 	g_return_if_fail (GDA_IS_BATCH (batch));
2032 	if (!params)
2033 		return;
2034 	g_return_if_fail (GDA_IS_SET (params));
2035 
2036 	const GSList *list;
2037 	for (list = gda_batch_get_statements (batch); list; list = list->next)
2038 		browser_connection_define_ui_plugins_for_stmt (bcnc, GDA_STATEMENT (list->data), params);
2039 }
2040 
2041 /* remark: the current ABI leaves no room to add a
2042  * validity check to the GdaSqlExpr structure, and the following test
2043  * should be done in gda_sql_expr_check_validity() once the GdaSqlExpr
2044  * has the capacity to hold the information (ie. when ABI is broken)
2045  *
2046  * The code here is a modification from the gda_sql_select_field_check_validity()
2047  * adapted for the GdaSqlExpr.
2048  */
2049 static gboolean
_gda_sql_expr_check_validity(GdaSqlExpr * expr,GdaMetaStruct * mstruct,GdaMetaDbObject ** out_validity_meta_object,GdaMetaTableColumn ** out_validity_meta_table_column,GError ** error)2050 _gda_sql_expr_check_validity (GdaSqlExpr *expr, GdaMetaStruct *mstruct,
2051 			      GdaMetaDbObject **out_validity_meta_object,
2052 			      GdaMetaTableColumn **out_validity_meta_table_column, GError **error)
2053 {
2054 	GdaMetaDbObject *dbo = NULL;
2055 	const gchar *field_name;
2056 
2057 	*out_validity_meta_object = NULL;
2058 	*out_validity_meta_table_column = NULL;
2059 
2060 	if (! expr->value || (G_VALUE_TYPE (expr->value) != G_TYPE_STRING))
2061 		return TRUE;
2062 	field_name = g_value_get_string (expr->value);
2063 
2064 
2065 	GdaSqlAnyPart *any;
2066 	GdaMetaTableColumn *tcol = NULL;
2067 	GValue value;
2068 
2069 	memset (&value, 0, sizeof (GValue));
2070 	for (any = GDA_SQL_ANY_PART(expr)->parent;
2071 	     any && (any->type != GDA_SQL_ANY_STMT_SELECT) && (any->type != GDA_SQL_ANY_STMT_DELETE) &&
2072 		     (any->type != GDA_SQL_ANY_STMT_UPDATE);
2073 	     any = any->parent);
2074 	if (!any) {
2075 		/* not in a structure which can be analysed */
2076 		return TRUE;
2077 	}
2078 
2079 	switch (any->type) {
2080 	case GDA_SQL_ANY_STMT_SELECT: {
2081 		/* go through all the SELECT's targets to see if
2082 		 * there is a table with the corresponding field */
2083 		GSList *targets;
2084 		if (((GdaSqlStatementSelect *)any)->from) {
2085 			for (targets = ((GdaSqlStatementSelect *)any)->from->targets;
2086 			     targets;
2087 			     targets = targets->next) {
2088 				GdaSqlSelectTarget *target = (GdaSqlSelectTarget *) targets->data;
2089 				if (!target->validity_meta_object /*&&
2090 				     * commented out in the current context because
2091 				     * browser_connection_check_sql_statement_validify() has already been
2092 				     * called, will need to be re-added when movind to the
2093 				     * gda-statement-struct.c file.
2094 				     *
2095 				     * !gda_sql_select_target_check_validity (target, data, error)*/)
2096 					return FALSE;
2097 
2098 				g_value_set_string (g_value_init (&value, G_TYPE_STRING), field_name);
2099 				tcol = gda_meta_struct_get_table_column (mstruct,
2100 									 GDA_META_TABLE (target->validity_meta_object),
2101 									 &value);
2102 				g_value_unset (&value);
2103 				if (tcol) {
2104 					/* found a candidate */
2105 					if (dbo) {
2106 						g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
2107 							     _("Could not identify table for field '%s'"), field_name);
2108 						return FALSE;
2109 					}
2110 					dbo = target->validity_meta_object;
2111 				}
2112 			}
2113 		}
2114 		break;
2115 	}
2116 	case GDA_SQL_ANY_STMT_UPDATE: {
2117 		GdaSqlTable *table;
2118 		table = ((GdaSqlStatementUpdate *)any)->table;
2119 		if (!table || !table->validity_meta_object /* ||
2120 		    * commented out in the current context because
2121 		    * browser_connection_check_sql_statement_validify() has already been
2122 		    * called, will need to be re-added when movind to the
2123 		    * gda-statement-struct.c file.
2124 		    *
2125 		    * !gda_sql_select_target_check_validity (target, data, error)*/)
2126 			return FALSE;
2127 		dbo = table->validity_meta_object;
2128 		g_value_set_string (g_value_init (&value, G_TYPE_STRING), field_name);
2129 		tcol = gda_meta_struct_get_table_column (mstruct,
2130 							 GDA_META_TABLE (table->validity_meta_object),
2131 							 &value);
2132 		g_value_unset (&value);
2133 		break;
2134 	}
2135 	case GDA_SQL_ANY_STMT_DELETE: {
2136 		GdaSqlTable *table;
2137 		table = ((GdaSqlStatementDelete *)any)->table;
2138 		if (!table || !table->validity_meta_object /* ||
2139 		    * commented out in the current context because
2140 		    * browser_connection_check_sql_statement_validify() has already been
2141 		    * called, will need to be re-added when movind to the
2142 		    * gda-statement-struct.c file.
2143 		    *
2144 		    * !gda_sql_select_target_check_validity (target, data, error)*/)
2145 			return FALSE;
2146 		dbo = table->validity_meta_object;
2147 		g_value_set_string (g_value_init (&value, G_TYPE_STRING), field_name);
2148 		tcol = gda_meta_struct_get_table_column (mstruct,
2149 							 GDA_META_TABLE (table->validity_meta_object),
2150 							 &value);
2151 		g_value_unset (&value);
2152 		break;
2153 	}
2154 	default:
2155 		g_assert_not_reached ();
2156 		break;
2157 	}
2158 
2159 	if (!dbo) {
2160 		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
2161 			     _("Could not identify table for field '%s'"), field_name);
2162 		return FALSE;
2163 	}
2164 	*out_validity_meta_object = dbo;
2165 	*out_validity_meta_table_column = tcol;
2166 	return TRUE;
2167 }
2168 
2169 typedef struct {
2170 	BrowserConnection *bcnc;
2171 	GdaSet *params;
2172 } ParamsData;
2173 
2174 /*
2175  *
2176  * In this function we try to find for which table's column a parameter is and use
2177  * preferences to set the GdaHolder's plugin attribute
2178  */
2179 static gboolean
foreach_ui_plugins_for_params(GdaSqlAnyPart * part,ParamsData * data,G_GNUC_UNUSED GError ** error)2180 foreach_ui_plugins_for_params (GdaSqlAnyPart *part, ParamsData *data, G_GNUC_UNUSED GError **error)
2181 {
2182 	if (part->type != GDA_SQL_ANY_EXPR)
2183 		return TRUE;
2184 	GdaSqlExpr *expr = (GdaSqlExpr*) part;
2185 	if (!expr->param_spec)
2186 		return TRUE;
2187 
2188 	GdaHolder *holder;
2189 	holder = gda_set_get_holder (data->params, expr->param_spec->name);
2190 	if (! holder)
2191 		return TRUE;
2192 
2193 	GdaSqlAnyPart *uppart;
2194 	gchar *plugin = NULL;
2195 	uppart = part->parent;
2196 	if (!uppart)
2197 		return TRUE;
2198 	else if (uppart->type == GDA_SQL_ANY_SQL_OPERATION) {
2199 		GdaSqlOperation *op = (GdaSqlOperation*) uppart;
2200 		/* look into condition */
2201 		GSList *list;
2202 		for (list = op->operands; list; list = list->next) {
2203 			GdaSqlExpr *oexpr = (GdaSqlExpr*) list->data;
2204 			if (oexpr == expr)
2205 				continue;
2206 
2207 			GdaMetaDbObject    *validity_meta_object;
2208 			GdaMetaTableColumn *validity_meta_table_column;
2209 			if (_gda_sql_expr_check_validity (oexpr,
2210 							  browser_connection_get_meta_struct (data->bcnc),
2211 							  &validity_meta_object,
2212 							  &validity_meta_table_column, NULL)) {
2213 				plugin = browser_connection_get_table_column_attribute (data->bcnc,
2214 											GDA_META_TABLE (validity_meta_object),
2215 											validity_meta_table_column,
2216 											BROWSER_CONNECTION_COLUMN_PLUGIN, NULL);
2217 				break;
2218 			}
2219 		}
2220 	}
2221 	else if (uppart->type == GDA_SQL_ANY_STMT_UPDATE) {
2222 		GdaSqlStatementUpdate *upd = (GdaSqlStatementUpdate*) uppart;
2223 		GdaSqlField *field;
2224 		field = g_slist_nth_data (upd->fields_list, g_slist_index (upd->expr_list, expr));
2225 		if (field)
2226 			plugin = browser_connection_get_table_column_attribute (data->bcnc,
2227 										GDA_META_TABLE (upd->table->validity_meta_object),
2228 										field->validity_meta_table_column,
2229 										BROWSER_CONNECTION_COLUMN_PLUGIN, NULL);
2230 	}
2231 	else if (uppart->type == GDA_SQL_ANY_STMT_INSERT) {
2232 		GdaSqlStatementInsert *ins = (GdaSqlStatementInsert*) uppart;
2233 		GdaSqlField *field;
2234 		gint expr_index = -1;
2235 		GSList *slist;
2236 		GdaMetaTableColumn *column = NULL;
2237 		for (slist = ins->values_list; slist; slist = slist->next) {
2238 			expr_index = g_slist_index ((GSList*) slist->data, expr);
2239 			if (expr_index >= 0)
2240 				break;
2241 		}
2242 		if (expr_index >= 0) {
2243 			field = g_slist_nth_data (ins->fields_list, expr_index);
2244 			if (field)
2245 				column = field->validity_meta_table_column;
2246 			else {
2247 				/* no field specified => take the table's fields */
2248 				GdaMetaTable *mtable = GDA_META_TABLE (ins->table->validity_meta_object);
2249 				column = g_slist_nth_data (mtable->columns, expr_index);
2250 			}
2251 		}
2252 		if (column)
2253 			plugin = browser_connection_get_table_column_attribute (data->bcnc,
2254 										GDA_META_TABLE (ins->table->validity_meta_object),
2255 										column,
2256 										BROWSER_CONNECTION_COLUMN_PLUGIN, NULL);
2257 	}
2258 
2259 	if (plugin) {
2260 		/*g_print ("Using plugin [%s]\n", plugin);*/
2261 		GValue *value;
2262 		g_value_take_string ((value = gda_value_new (G_TYPE_STRING)), plugin);
2263 		gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
2264 		gda_value_free (value);
2265 	}
2266 
2267 	return TRUE;
2268 }
2269 
2270 /**
2271  * browser_connection_define_ui_plugins_for_stmt
2272  * @bcnc: a #BrowserConnection object
2273  * @stmt: a #GdaStatement
2274  * @params: a #GdaSet (usually created with gda_statement_get_parameters())
2275  *
2276  * Analyses @stmt and assign plugins to each #GdaHolder in @params according to the preferences stored
2277  * for each table's field, defined at some point using browser_connection_set_table_column_attribute().
2278  */
2279 void
browser_connection_define_ui_plugins_for_stmt(BrowserConnection * bcnc,GdaStatement * stmt,GdaSet * params)2280 browser_connection_define_ui_plugins_for_stmt (BrowserConnection *bcnc, GdaStatement *stmt, GdaSet *params)
2281 {
2282 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
2283 	g_return_if_fail (GDA_IS_STATEMENT (stmt));
2284 	if (!params)
2285 		return;
2286 	g_return_if_fail (GDA_IS_SET (params));
2287 
2288 	GdaSqlStatement *sqlst;
2289 	GdaSqlAnyPart *rootpart;
2290 	g_object_get ((GObject*) stmt, "structure", &sqlst, NULL);
2291 	g_return_if_fail (sqlst);
2292 	switch (sqlst->stmt_type) {
2293 	case GDA_SQL_STATEMENT_INSERT:
2294 	case GDA_SQL_STATEMENT_UPDATE:
2295 	case GDA_SQL_STATEMENT_DELETE:
2296 	case GDA_SQL_STATEMENT_SELECT:
2297 	case GDA_SQL_STATEMENT_COMPOUND:
2298 		rootpart = (GdaSqlAnyPart*) sqlst->contents;
2299 		break;
2300 	default:
2301 		rootpart = NULL;
2302 		break;
2303 	}
2304 	GError *lerror = NULL;
2305 	if (!rootpart || !browser_connection_check_sql_statement_validify (bcnc, sqlst, &lerror)) {
2306 		/*g_print ("ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");*/
2307 		g_clear_error (&lerror);
2308 		gda_sql_statement_free (sqlst);
2309 		return;
2310 	}
2311 
2312 	ParamsData data;
2313 	data.params = params;
2314 	data.bcnc = bcnc;
2315 	gda_sql_any_part_foreach (rootpart, (GdaSqlForeachFunc) foreach_ui_plugins_for_params,
2316 				  &data, NULL);
2317 
2318 	gda_sql_statement_free (sqlst);
2319 
2320 	/* REM: we also need to handle FK tables to propose a drop down list of possible values */
2321 }
2322 
2323 /**
2324  * browser_connection_keep_variables
2325  * @bcnc: a #BrowserConnection object
2326  * @set: a #GdaSet containing variables for which a copy has to be done
2327  *
2328  * Makes a copy of the variables in @set and keep them in @bcnc. Retreive them
2329  * using browser_connection_load_variables()
2330  */
2331 void
browser_connection_keep_variables(BrowserConnection * bcnc,GdaSet * set)2332 browser_connection_keep_variables (BrowserConnection *bcnc, GdaSet *set)
2333 {
2334 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
2335 	if (!set)
2336 		return;
2337 	g_return_if_fail (GDA_IS_SET (set));
2338 
2339 	if (! bcnc->priv->variables) {
2340 		bcnc->priv->variables = gda_set_copy (set);
2341 		return;
2342 	}
2343 
2344 	GSList *list;
2345 	for (list = set->holders; list; list = list->next) {
2346 		GdaHolder *nh, *eh;
2347 		nh = GDA_HOLDER (list->data);
2348 		eh = gda_set_get_holder (bcnc->priv->variables, gda_holder_get_id (nh));
2349 		if (eh) {
2350 			if (gda_holder_get_g_type (nh) == gda_holder_get_g_type (eh)) {
2351 				const GValue *cvalue;
2352 				cvalue = gda_holder_get_value (nh);
2353 				gda_holder_set_value (eh, cvalue, NULL);
2354 			}
2355 			else {
2356 				gda_set_remove_holder (bcnc->priv->variables, eh);
2357 				eh = gda_holder_copy (nh);
2358 				gda_set_add_holder (bcnc->priv->variables, eh);
2359 				g_object_unref (eh);
2360 			}
2361 		}
2362 		else {
2363 			eh = gda_holder_copy (nh);
2364 			gda_set_add_holder (bcnc->priv->variables, eh);
2365 			g_object_unref (eh);
2366 		}
2367 	}
2368 }
2369 
2370 /**
2371  * browser_connection_load_variables
2372  * @bcnc: a #BrowserConnection object
2373  * @set: a #GdaSet which will in the end contain (if any) variables stored in @bcnc
2374  *
2375  * For each #GdaHolder in @set, set the value if one is available in @bcnc.
2376  */
2377 void
browser_connection_load_variables(BrowserConnection * bcnc,GdaSet * set)2378 browser_connection_load_variables (BrowserConnection *bcnc, GdaSet *set)
2379 {
2380 	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
2381 	if (!set)
2382 		return;
2383 	g_return_if_fail (GDA_IS_SET (set));
2384 
2385 	if (! bcnc->priv->variables)
2386 		return;
2387 
2388 	GSList *list;
2389 	for (list = set->holders; list; list = list->next) {
2390 		GdaHolder *nh, *eh;
2391 		nh = GDA_HOLDER (list->data);
2392 		eh = gda_set_get_holder (bcnc->priv->variables, gda_holder_get_id (nh));
2393 		if (eh) {
2394 			if (gda_holder_get_g_type (nh) == gda_holder_get_g_type (eh)) {
2395 				const GValue *cvalue;
2396 				cvalue = gda_holder_get_value (eh);
2397 				gda_holder_set_value (nh, cvalue, NULL);
2398 			}
2399 			else if (g_value_type_transformable (gda_holder_get_g_type (eh),
2400 							     gda_holder_get_g_type (nh))) {
2401 				const GValue *evalue;
2402 				GValue *nvalue;
2403 				evalue = gda_holder_get_value (eh);
2404 				nvalue = gda_value_new (gda_holder_get_g_type (nh));
2405 				if (g_value_transform (evalue, nvalue))
2406 					gda_holder_take_value (nh, nvalue, NULL);
2407 				else
2408 					gda_value_free (nvalue);
2409 			}
2410 		}
2411 	}
2412 }
2413 
2414 /**
2415  * browser_connection_is_ldap:
2416  * @bcnc: a #BrowserConnection
2417  *
2418  * Returns: %TRUE if @bcnc proxies an LDAP connection
2419  */
2420 gboolean
browser_connection_is_ldap(BrowserConnection * bcnc)2421 browser_connection_is_ldap (BrowserConnection *bcnc)
2422 {
2423 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
2424 
2425 #ifdef HAVE_LDAP
2426 	return GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc) ? TRUE : FALSE;
2427 #endif
2428 	return FALSE;
2429 }
2430 
2431 #ifdef HAVE_LDAP
2432 
2433 typedef struct {
2434 	GdaConnection *cnc;
2435 	gchar *base_dn;
2436 	gchar *attributes;
2437 	gchar *filter;
2438 	GdaLdapSearchScope scope;
2439 } LdapSearchData;
2440 static void
ldap_search_data_free(LdapSearchData * data)2441 ldap_search_data_free (LdapSearchData *data)
2442 {
2443 	g_free (data->base_dn);
2444 	g_free (data->filter);
2445 	g_free (data->attributes);
2446 	g_free (data);
2447 }
2448 
2449 /* executed in sub @bcnc->priv->wrapper's thread */
2450 static gpointer
wrapper_ldap_search(LdapSearchData * data,GError ** error)2451 wrapper_ldap_search (LdapSearchData *data, GError **error)
2452 {
2453 	GdaDataModel *model;
2454 	model = gda_data_model_ldap_new (GDA_CONNECTION (data->cnc), data->base_dn,
2455 					 data->filter, data->attributes, data->scope);
2456 	if (!model) {
2457 		g_set_error (error, GDA_TOOLS_ERROR, GDA_TOOLS_INTERNAL_COMMAND_ERROR,
2458 			     "%s", _("Could not execute LDAP search"));
2459 		return (gpointer) 0x01;
2460 	}
2461 	else {
2462 		GdaDataModel *wrapped;
2463 		wrapped = gda_data_access_wrapper_new (model);
2464 		g_object_unref (model);
2465 		/* force loading all the LDAP entries in memory to avoid
2466 		 * having the GTK thread lock on LDAP searches */
2467 		gda_data_model_get_n_rows (wrapped);
2468 		return wrapped;
2469 	}
2470 }
2471 
2472 /**
2473  * browser_connection_ldap_search:
2474  *
2475  * Executes an LDAP search. Wrapper around gda_data_model_ldap_new()
2476  *
2477  * Returns: a job ID, or %0 if an error occurred
2478  */
2479 guint
browser_connection_ldap_search(BrowserConnection * bcnc,const gchar * base_dn,const gchar * filter,const gchar * attributes,GdaLdapSearchScope scope,BrowserConnectionJobCallback callback,gpointer cb_data,GError ** error)2480 browser_connection_ldap_search (BrowserConnection *bcnc,
2481 				const gchar *base_dn, const gchar *filter,
2482 				const gchar *attributes, GdaLdapSearchScope scope,
2483 				BrowserConnectionJobCallback callback,
2484 				gpointer cb_data, GError **error)
2485 {
2486 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
2487 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
2488 
2489 	LdapSearchData *data;
2490 	guint job_id;
2491 	data = g_new0 (LdapSearchData, 1);
2492 	data->cnc = bcnc->priv->cnc;
2493 	data->base_dn = g_strdup (base_dn);
2494 	data->filter = g_strdup (filter);
2495 	data->attributes = g_strdup (attributes);
2496 	data->scope = scope;
2497 
2498 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
2499 					     (GdaThreadWrapperFunc) wrapper_ldap_search,
2500 					     data, (GDestroyNotify) ldap_search_data_free, error);
2501 	if (job_id > 0)
2502 		push_wrapper_job (bcnc, job_id, JOB_TYPE_CALLBACK,
2503 				  _("Executing LDAP search"), callback, cb_data);
2504 	return job_id;
2505 }
2506 
2507 
2508 typedef struct {
2509 	GdaConnection *cnc;
2510 	gchar *dn;
2511 	gchar **attributes;
2512 } LdapData;
2513 static void
ldap_data_free(LdapData * data)2514 ldap_data_free (LdapData *data)
2515 {
2516 	g_free (data->dn);
2517 	if (data->attributes)
2518 		g_strfreev (data->attributes);
2519 	g_free (data);
2520 }
2521 
2522 /* executed in sub @bcnc->priv->wrapper's thread */
2523 static gpointer
wrapper_ldap_describe_entry(LdapData * data,GError ** error)2524 wrapper_ldap_describe_entry (LdapData *data, GError **error)
2525 {
2526 	GdaLdapEntry *lentry;
2527 	lentry = gda_ldap_describe_entry (GDA_LDAP_CONNECTION (data->cnc), data->dn, error);
2528 	return lentry ? lentry : (gpointer) 0x01;
2529 }
2530 
2531 /**
2532  * browser_connection_ldap_describe_entry:
2533  * @bcnc: a #BrowserConnection
2534  * @dn: the DN of the entry to describe
2535  * @callback: the callback to execute with the results
2536  * @cb_data: a pointer to pass to @callback
2537  * @error: a place to store errors, or %NULL
2538  *
2539  * Wrapper around gda_ldap_describe_entry().
2540  *
2541  * Returns: a job ID, or %0 if an error occurred
2542  */
2543 guint
browser_connection_ldap_describe_entry(BrowserConnection * bcnc,const gchar * dn,BrowserConnectionJobCallback callback,gpointer cb_data,GError ** error)2544 browser_connection_ldap_describe_entry (BrowserConnection *bcnc, const gchar *dn,
2545 					BrowserConnectionJobCallback callback,
2546 					gpointer cb_data, GError **error)
2547 {
2548 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
2549 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
2550 
2551 	LdapData *data;
2552 	guint job_id;
2553 
2554 	data = g_new0 (LdapData, 1);
2555 	data->cnc = bcnc->priv->cnc;
2556 	data->dn = g_strdup (dn);
2557 
2558 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
2559 					     (GdaThreadWrapperFunc) wrapper_ldap_describe_entry,
2560 					     data, (GDestroyNotify) ldap_data_free, error);
2561 	if (job_id > 0)
2562 		push_wrapper_job (bcnc, job_id, JOB_TYPE_CALLBACK,
2563 				  _("Fetching LDAP entry's attributes"), callback, cb_data);
2564 
2565 	return job_id;
2566 }
2567 
2568 /* executed in sub @bcnc->priv->wrapper's thread */
2569 static gpointer
wrapper_ldap_get_entry_children(LdapData * data,GError ** error)2570 wrapper_ldap_get_entry_children (LdapData *data, GError **error)
2571 {
2572 	GdaLdapEntry **array;
2573 	array = gda_ldap_get_entry_children (GDA_LDAP_CONNECTION (data->cnc), data->dn, data->attributes, error);
2574 	return array ? array : (gpointer) 0x01;
2575 }
2576 
2577 /**
2578  * browser_connection_ldap_get_entry_children:
2579  * @bcnc: a #BrowserConnection
2580  * @dn: the DN of the entry to get children from
2581  * @callback: the callback to execute with the results
2582  * @cb_data: a pointer to pass to @callback
2583  * @error: a place to store errors, or %NULL
2584  *
2585  * Wrapper around gda_ldap_get_entry_children().
2586  *
2587  * Returns: a job ID, or %0 if an error occurred
2588  */
2589 guint
browser_connection_ldap_get_entry_children(BrowserConnection * bcnc,const gchar * dn,gchar ** attributes,BrowserConnectionJobCallback callback,gpointer cb_data,GError ** error)2590 browser_connection_ldap_get_entry_children (BrowserConnection *bcnc, const gchar *dn,
2591 					    gchar **attributes,
2592 					    BrowserConnectionJobCallback callback,
2593 					    gpointer cb_data, GError **error)
2594 {
2595 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
2596 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
2597 
2598 	LdapData *data;
2599 	guint job_id;
2600 
2601 	data = g_new0 (LdapData, 1);
2602 	data->cnc = bcnc->priv->cnc;
2603 	data->dn = g_strdup (dn);
2604 	if (attributes) {
2605 		gint i;
2606 		GArray *array = NULL;
2607 		for (i = 0; attributes [i]; i++) {
2608 			gchar *tmp;
2609 			if (! array)
2610 				array = g_array_new (TRUE, FALSE, sizeof (gchar*));
2611 			tmp = g_strdup (attributes [i]);
2612 			g_array_append_val (array, tmp);
2613 		}
2614 		if (array)
2615 			data->attributes = (gchar**) g_array_free (array, FALSE);
2616 	}
2617 
2618 	job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
2619 					     (GdaThreadWrapperFunc) wrapper_ldap_get_entry_children,
2620 					     data, (GDestroyNotify) ldap_data_free, error);
2621 	if (job_id > 0)
2622 		push_wrapper_job (bcnc, job_id, JOB_TYPE_CALLBACK,
2623 				  _("Fetching LDAP entry's children"), callback, cb_data);
2624 
2625 	return job_id;
2626 }
2627 
2628 /**
2629  * browser_connection_ldap_icon_for_class:
2630  * @objectclass: objectClass attribute
2631  *
2632  * Returns: the correct icon, or %NULL if it could not be determined
2633  */
2634 GdkPixbuf *
browser_connection_ldap_icon_for_class(GdaLdapAttribute * objectclass)2635 browser_connection_ldap_icon_for_class (GdaLdapAttribute *objectclass)
2636 {
2637 	gint type = 0;
2638 	if (objectclass) {
2639 		guint i;
2640 		for (i = 0; i < objectclass->nb_values; i++) {
2641 			const gchar *class = g_value_get_string (objectclass->values[i]);
2642 			if (!class)
2643 				continue;
2644 			else if (!strcmp (class, "organization"))
2645 				type = MAX(type, 1);
2646 			else if (!strcmp (class, "groupOfNames") ||
2647 				 !strcmp (class, "posixGroup"))
2648 				type = MAX(type, 2);
2649 			else if (!strcmp (class, "account") ||
2650 				 !strcmp (class, "mailUser") ||
2651 				 !strcmp (class, "organizationalPerson") ||
2652 				 !strcmp (class, "person") ||
2653 				 !strcmp (class, "pilotPerson") ||
2654 				 !strcmp (class, "newPilotPerson") ||
2655 				 !strcmp (class, "pkiUser") ||
2656 				 !strcmp (class, "posixUser") ||
2657 				 !strcmp (class, "posixAccount") ||
2658 				 !strcmp (class, "residentalPerson") ||
2659 				 !strcmp (class, "shadowAccount") ||
2660 				 !strcmp (class, "strongAuthenticationUser") ||
2661 				 !strcmp (class, "inetOrgPerson"))
2662 				type = MAX(type, 3);
2663 		}
2664 	}
2665 
2666 	switch (type) {
2667 	case 0:
2668 		return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_ENTRY);
2669 	case 1:
2670 		return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_ORGANIZATION);
2671 	case 2:
2672 		return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_GROUP);
2673 	case 3:
2674 		return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_PERSON);
2675 	default:
2676 		g_assert_not_reached ();
2677 	}
2678 	return NULL;
2679 }
2680 
2681 typedef struct {
2682 	BrowserConnectionJobCallback callback;
2683 	gpointer cb_data;
2684 } IconTypeData;
2685 
2686 static void
fetch_classes_cb(G_GNUC_UNUSED BrowserConnection * bcnc,gpointer out_result,IconTypeData * data,G_GNUC_UNUSED GError * error)2687 fetch_classes_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
2688 		  gpointer out_result, IconTypeData *data, G_GNUC_UNUSED GError *error)
2689 {
2690 	GdkPixbuf *pixbuf = NULL;
2691 	if (out_result) {
2692 		GdaLdapEntry *lentry = (GdaLdapEntry*) out_result;
2693 		GdaLdapAttribute *attr;
2694 		attr = g_hash_table_lookup (lentry->attributes_hash, "objectClass");
2695 		pixbuf = browser_connection_ldap_icon_for_class (attr);
2696 		gda_ldap_entry_free (lentry);
2697 	}
2698 	if (data->callback)
2699 		data->callback (bcnc, pixbuf, data->cb_data, error);
2700 
2701 	g_free (data);
2702 }
2703 
2704 /**
2705  * browser_connection_ldap_icon_for_dn:
2706  * @bcnc: a #BrowserConnection
2707  * @dn: the DN of the entry
2708  * @callback: the callback to execute with the results
2709  * @cb_data: a pointer to pass to @callback
2710  * @error: a place to store errors, or %NULL
2711  *
2712  * Determines the correct icon type for @dn based on which class it belongs to
2713  *
2714  * Returns: a job ID, or %0 if an error occurred
2715  */
2716 guint
browser_connection_ldap_icon_for_dn(BrowserConnection * bcnc,const gchar * dn,BrowserConnectionJobCallback callback,gpointer cb_data,GError ** error)2717 browser_connection_ldap_icon_for_dn (BrowserConnection *bcnc, const gchar *dn,
2718 				     BrowserConnectionJobCallback callback,
2719 				     gpointer cb_data, GError **error)
2720 {
2721 	IconTypeData *data;
2722 	guint job_id;
2723 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
2724 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
2725 	g_return_val_if_fail (dn && *dn, 0);
2726 
2727 	data = g_new (IconTypeData, 1);
2728 	data->callback = callback;
2729 	data->cb_data = cb_data;
2730 	job_id = browser_connection_ldap_describe_entry (bcnc, dn,
2731 							 BROWSER_CONNECTION_JOB_CALLBACK (fetch_classes_cb),
2732 							 data, error);
2733 	return job_id;
2734 }
2735 
2736 /**
2737  * browser_connection_ldap_get_base_dn:
2738  * @bcnc: a #BrowserConnection
2739  *
2740  * wrapper for gda_ldap_connection_get_base_dn()
2741  *
2742  * Returns: the base DN or %NULL
2743  */
2744 const gchar *
browser_connection_ldap_get_base_dn(BrowserConnection * bcnc)2745 browser_connection_ldap_get_base_dn (BrowserConnection *bcnc)
2746 {
2747 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
2748 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), NULL);
2749 
2750 	return gda_ldap_connection_get_base_dn (GDA_LDAP_CONNECTION (bcnc->priv->cnc));
2751 }
2752 
2753 /**
2754  * browser_connection_describe_table:
2755  * @bcnc: a #BrowserConnection
2756  * @table_name: a table name, not %NULL
2757  * @out_base_dn: (allow-none) (transfer none): a place to store the LDAP search base DN, or %NULL
2758  * @out_filter: (allow-none) (transfer none): a place to store the LDAP search filter, or %NULL
2759  * @out_attributes: (allow-none) (transfer none): a place to store the LDAP search attributes, or %NULL
2760  * @out_scope: (allow-none) (transfer none): a place to store the LDAP search scope, or %NULL
2761  * @error: a place to store errors, or %NULL
2762  */
2763 gboolean
browser_connection_describe_table(BrowserConnection * bcnc,const gchar * table_name,const gchar ** out_base_dn,const gchar ** out_filter,const gchar ** out_attributes,GdaLdapSearchScope * out_scope,GError ** error)2764 browser_connection_describe_table  (BrowserConnection *bcnc, const gchar *table_name,
2765 				    const gchar **out_base_dn, const gchar **out_filter,
2766 				    const gchar **out_attributes,
2767 				    GdaLdapSearchScope *out_scope, GError **error)
2768 {
2769 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
2770 	g_return_val_if_fail (browser_connection_is_ldap (bcnc), FALSE);
2771 	g_return_val_if_fail (table_name && *table_name, FALSE);
2772 
2773 	return gda_ldap_connection_describe_table (GDA_LDAP_CONNECTION (bcnc->priv->cnc),
2774 						   table_name, out_base_dn, out_filter,
2775 						   out_attributes, out_scope, error);
2776 }
2777 
2778 /**
2779  * browser_connection_get_class_info:
2780  * @bcnc: a #BrowserConnection
2781  * @classname: a class name
2782  *
2783  * proxy for gda_ldap_get_class_info.
2784  */
2785 GdaLdapClass *
browser_connection_get_class_info(BrowserConnection * bcnc,const gchar * classname)2786 browser_connection_get_class_info (BrowserConnection *bcnc, const gchar *classname)
2787 {
2788 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
2789 	g_return_val_if_fail (browser_connection_is_ldap (bcnc), NULL);
2790 
2791 	return gda_ldap_get_class_info (GDA_LDAP_CONNECTION (bcnc->priv->cnc), classname);
2792 }
2793 
2794 /**
2795  * browser_connection_get_top_classes:
2796  * @bcnc: a #BrowserConnection
2797  *
2798  * proxy for gda_ldap_get_top_classes.
2799  */
2800 const GSList *
browser_connection_get_top_classes(BrowserConnection * bcnc)2801 browser_connection_get_top_classes (BrowserConnection *bcnc)
2802 {
2803 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
2804 	g_return_val_if_fail (browser_connection_is_ldap (bcnc), NULL);
2805 
2806 	return gda_ldap_get_top_classes (GDA_LDAP_CONNECTION (bcnc->priv->cnc));
2807 }
2808 
2809 /**
2810  * browser_connection_declare_table:
2811  * @bcnc: a #BrowserConnection
2812  *
2813  * Wrapper around gda_ldap_connection_declare_table()
2814  */
2815 gboolean
browser_connection_declare_table(BrowserConnection * bcnc,const gchar * table_name,const gchar * base_dn,const gchar * filter,const gchar * attributes,GdaLdapSearchScope scope,GError ** error)2816 browser_connection_declare_table (BrowserConnection *bcnc,
2817 				  const gchar *table_name,
2818 				  const gchar *base_dn,
2819 				  const gchar *filter,
2820 				  const gchar *attributes,
2821 				  GdaLdapSearchScope scope,
2822 				  GError **error)
2823 {
2824 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
2825 	g_return_val_if_fail (browser_connection_is_ldap (bcnc), FALSE);
2826 
2827 	return gda_ldap_connection_declare_table (GDA_LDAP_CONNECTION (bcnc->priv->cnc),
2828 						  table_name, base_dn, filter,
2829 						  attributes, scope, error);
2830 }
2831 
2832 /**
2833  * browser_connection_undeclare_table:
2834  * @bcnc: a #BrowserConnection
2835  *
2836  * Wrapper around gda_ldap_connection_undeclare_table()
2837  */
2838 gboolean
browser_connection_undeclare_table(BrowserConnection * bcnc,const gchar * table_name,GError ** error)2839 browser_connection_undeclare_table (BrowserConnection *bcnc,
2840 				    const gchar *table_name,
2841 				    GError **error)
2842 {
2843 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
2844 	g_return_val_if_fail (browser_connection_is_ldap (bcnc), FALSE);
2845 
2846 	return gda_ldap_connection_undeclare_table (GDA_LDAP_CONNECTION (bcnc->priv->cnc),
2847 						    table_name, error);
2848 }
2849 
2850 #endif /* HAVE_LDAP */
2851