1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 
20 #include <glib/gi18n-lib.h>
21 #include <libgda/libgda.h>
22 #include <sql-parser/gda-sql-parser.h>
23 #include "mgr-columns.h"
24 #include "support.h"
25 
26 struct _MgrColumnsPriv {
27 	BrowserConnection  *bcnc;
28 	gchar *schema;
29 	gchar *table_name;
30 };
31 
32 static void mgr_columns_class_init (MgrColumnsClass *klass);
33 static void mgr_columns_init       (MgrColumns *tmgr1, MgrColumnsClass *klass);
34 static void mgr_columns_dispose    (GObject *object);
35 static void mgr_columns_set_property (GObject *object,
36 				      guint param_id,
37 				      const GValue *value,
38 				      GParamSpec *pspec);
39 static void mgr_columns_get_property (GObject *object,
40 				      guint param_id,
41 				      GValue *value,
42 				      GParamSpec *pspec);
43 
44 /* virtual methods */
45 static GSList *mgr_columns_update_children (GdaTreeManager *manager, GdaTreeNode *node,
46 					    const GSList *children_nodes, gboolean *out_error, GError **error);
47 
48 static GObjectClass *parent_class = NULL;
49 
50 /* properties */
51 enum {
52         PROP_0,
53 	PROP_BROWSER_CNC
54 };
55 
56 /*
57  * MgrColumns class implementation
58  * @klass:
59  */
60 static void
mgr_columns_class_init(MgrColumnsClass * klass)61 mgr_columns_class_init (MgrColumnsClass *klass)
62 {
63 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
64 
65 	parent_class = g_type_class_peek_parent (klass);
66 
67 	/* virtual methods */
68 	((GdaTreeManagerClass*) klass)->update_children = mgr_columns_update_children;
69 
70 	/* Properties */
71         object_class->set_property = mgr_columns_set_property;
72         object_class->get_property = mgr_columns_get_property;
73 
74 	g_object_class_install_property (object_class, PROP_BROWSER_CNC,
75                                          g_param_spec_object ("browser-connection", NULL, "Connection to use",
76                                                               BROWSER_TYPE_CONNECTION,
77                                                               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
78 	object_class->dispose = mgr_columns_dispose;
79 }
80 
81 static void
mgr_columns_init(MgrColumns * mgr,G_GNUC_UNUSED MgrColumnsClass * klass)82 mgr_columns_init (MgrColumns *mgr, G_GNUC_UNUSED MgrColumnsClass *klass)
83 {
84 	mgr->priv = g_new0 (MgrColumnsPriv, 1);
85 }
86 
87 static void
mgr_columns_dispose(GObject * object)88 mgr_columns_dispose (GObject *object)
89 {
90 	MgrColumns *mgr = (MgrColumns *) object;
91 
92 	if (mgr->priv) {
93 		if (mgr->priv->bcnc)
94 			g_object_unref (mgr->priv->bcnc);
95 		g_free (mgr->priv->schema);
96 		g_free (mgr->priv->table_name);
97 
98 		g_free (mgr->priv);
99 		mgr->priv = NULL;
100 	}
101 
102 	/* chain to parent class */
103 	parent_class->dispose (object);
104 }
105 
106 /* module error */
mgr_columns_error_quark(void)107 GQuark mgr_columns_error_quark (void)
108 {
109         static GQuark quark;
110         if (!quark)
111                 quark = g_quark_from_static_string ("mgr_columns_error");
112         return quark;
113 }
114 
115 /**
116  * mgr_columns_get_type
117  *
118  * Returns: the GType
119  */
120 GType
mgr_columns_get_type(void)121 mgr_columns_get_type (void)
122 {
123         static GType type = 0;
124 
125         if (G_UNLIKELY (type == 0)) {
126                 static GMutex registering;
127                 static const GTypeInfo info = {
128                         sizeof (MgrColumnsClass),
129                         (GBaseInitFunc) NULL,
130                         (GBaseFinalizeFunc) NULL,
131                         (GClassInitFunc) mgr_columns_class_init,
132                         NULL,
133                         NULL,
134                         sizeof (MgrColumns),
135                         0,
136                         (GInstanceInitFunc) mgr_columns_init,
137 			0
138                 };
139 
140                 g_mutex_lock (&registering);
141                 if (type == 0)
142                         type = g_type_register_static (GDA_TYPE_TREE_MANAGER, "MgrColumns", &info, 0);
143                 g_mutex_unlock (&registering);
144         }
145         return type;
146 }
147 
148 static void
mgr_columns_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)149 mgr_columns_set_property (GObject *object,
150 				   guint param_id,
151 				   const GValue *value,
152 				   GParamSpec *pspec)
153 {
154         MgrColumns *mgr;
155 
156         mgr = MGR_COLUMNS (object);
157         if (mgr->priv) {
158                 switch (param_id) {
159 		case PROP_BROWSER_CNC:
160 			mgr->priv->bcnc = (BrowserConnection*) g_value_get_object (value);
161 			if (mgr->priv->bcnc)
162 				g_object_ref (mgr->priv->bcnc);
163 
164 			break;
165 		default:
166 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
167 			break;
168                 }
169         }
170 }
171 
172 static void
mgr_columns_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)173 mgr_columns_get_property (GObject *object,
174 				   guint param_id,
175 				   GValue *value,
176 				   GParamSpec *pspec)
177 {
178         MgrColumns *mgr;
179 
180         mgr = MGR_COLUMNS (object);
181         if (mgr->priv) {
182                 switch (param_id) {
183 		case PROP_BROWSER_CNC:
184 			g_value_set_object (value, mgr->priv->bcnc);
185 			break;
186 		default:
187 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
188 			break;
189                 }
190         }
191 }
192 
193 /**
194  * mgr_columns_new
195  * @bcnc: a #BrowserConnection object
196  * @schema: the schema the table is in
197  * @name: the table's name
198  *
199  * Creates a new #GdaTreeManager object which will add one tree node for column found in the
200  * table @schema.@table
201  *
202  * Returns: a new #GdaTreeManager object
203  */
204 GdaTreeManager*
mgr_columns_new(BrowserConnection * bcnc,const gchar * schema,const gchar * table)205 mgr_columns_new (BrowserConnection *bcnc, const gchar *schema, const gchar *table)
206 {
207 	MgrColumns *mgr;
208 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
209 	g_return_val_if_fail (schema, NULL);
210 	g_return_val_if_fail (table, NULL);
211 
212 	mgr = (MgrColumns*) g_object_new (MGR_COLUMNS_TYPE,
213 					  "browser-connection", bcnc, NULL);
214 	mgr->priv->schema = g_strdup (schema);
215 	mgr->priv->table_name = g_strdup (table);
216 
217 	return (GdaTreeManager*) mgr;
218 }
219 
220 
221 /*
222  * Build a hash where key = column's name as a string, and value = GdaTreeNode
223  */
224 static GHashTable *
hash_for_existing_nodes(const GSList * nodes)225 hash_for_existing_nodes (const GSList *nodes)
226 {
227 	GHashTable *hash;
228 	const GSList *list;
229 
230 	hash = g_hash_table_new (g_str_hash, g_str_equal);
231 	for (list = nodes; list; list = list->next) {
232 		const GValue *cvalue;
233 		cvalue = gda_tree_node_get_node_attribute ((GdaTreeNode*) list->data, MGR_COLUMNS_COL_NAME_ATT_NAME);
234 		if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING)) {
235 			const gchar *str = g_value_get_string (cvalue);
236 			if (str)
237 				g_hash_table_insert (hash, (gpointer) str, list->data);
238 		}
239 	}
240 	return hash;
241 }
242 
243 static gboolean
column_is_fk_part(GdaMetaDbObject * dbo,gint index)244 column_is_fk_part (GdaMetaDbObject *dbo, gint index)
245 {
246 	GSList *fklist;
247 
248 	for (fklist = GDA_META_TABLE (dbo)->fk_list; fklist; fklist = fklist->next) {
249 		GdaMetaTableForeignKey *fk;
250 		gint i;
251 		fk = (GdaMetaTableForeignKey *) fklist->data;
252 		for (i = 0; i < fk->cols_nb; i++) {
253 			if (fk->fk_cols_array [i] == index + 1)
254 				return TRUE;
255 		}
256 	}
257 	return FALSE;
258 }
259 
260 static GSList *
mgr_columns_update_children(GdaTreeManager * manager,GdaTreeNode * node,const GSList * children_nodes,gboolean * out_error,GError ** error)261 mgr_columns_update_children (GdaTreeManager *manager, GdaTreeNode *node, const GSList *children_nodes,
262 			     gboolean *out_error, GError **error)
263 {
264 	MgrColumns *mgr = MGR_COLUMNS (manager);
265 	GSList *nodes_list = NULL;
266 	GHashTable *ehash = NULL;
267 
268 	if (out_error)
269 		*out_error = FALSE;
270 
271 	if (children_nodes)
272 		ehash = hash_for_existing_nodes (children_nodes);
273 
274 	GdaMetaStruct *mstruct;
275 	mstruct = browser_connection_get_meta_struct (mgr->priv->bcnc);
276 	if (!mstruct) {
277 		g_set_error (error, MGR_COLUMNS_ERROR, MGR_COLUMNS_NO_META_STRUCT,
278                              "%s", _("Not ready"));
279                 if (out_error)
280                         *out_error = TRUE;
281 		return NULL;
282 	}
283 
284 	GdaMetaDbObject *dbo;
285 	GValue *schema_v = NULL, *name_v;
286 	if (mgr->priv->schema)
287 		g_value_set_string ((schema_v = gda_value_new (G_TYPE_STRING)), mgr->priv->schema);
288 	g_value_set_string ((name_v = gda_value_new (G_TYPE_STRING)), mgr->priv->table_name);
289 	dbo = gda_meta_struct_get_db_object (mstruct, NULL, schema_v, name_v);
290 	if (schema_v)
291 		gda_value_free (schema_v);
292 	gda_value_free (name_v);
293 
294 	if (!dbo) {
295 		g_set_error (error, MGR_COLUMNS_ERROR, MGR_COLUMNS_TABLE_NOT_FOUND,
296                              "%s", _("Table not found"));
297                 if (out_error)
298                         *out_error = TRUE;
299 		return NULL;
300 	}
301 	if ((dbo->obj_type != GDA_META_DB_TABLE) && (dbo->obj_type != GDA_META_DB_VIEW)) {
302 		g_set_error (error, MGR_COLUMNS_ERROR, MGR_COLUMNS_WRONG_OBJ_TYPE,
303                              "%s", _("Requested object is not a table or view"));
304                 if (out_error)
305                         *out_error = TRUE;
306 		return NULL;
307 	}
308 
309 	GdaMetaTable *dbotable;
310 	GSList *columns;
311 	gint index;
312 	dbotable = GDA_META_TABLE (dbo);
313 
314 	for (columns = dbotable->columns, index = 0;
315 	     columns;
316 	     columns = columns->next, index ++) {
317 		GdaMetaTableColumn *col;
318 		col = GDA_META_TABLE_COLUMN (columns->data);
319 		GdaTreeNode* snode = NULL;
320 		GValue *av;
321 
322 		if (ehash)
323 			snode = g_hash_table_lookup (ehash, col->column_name);
324 		if (snode) {
325 			/* use the same node */
326 			g_object_ref (G_OBJECT (snode));
327 		}
328 		else {
329 			/* column's name */
330 			snode = gda_tree_manager_create_node (manager, node, NULL);
331 			if (col->pkey)
332 				g_value_take_string ((av = gda_value_new (G_TYPE_STRING)),
333 						     g_strdup_printf ("<b>%s</b>", col->column_name));
334 			else
335 				g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), col->column_name);
336 			gda_tree_node_set_node_attribute (snode, MGR_COLUMNS_COL_NAME_ATT_NAME, av, NULL);
337 			gda_value_free (av);
338 		}
339 
340 		/* column's type */
341 		g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), col->column_type);
342 		gda_tree_node_set_node_attribute (snode, MGR_COLUMNS_COL_TYPE_ATT_NAME, av, NULL);
343 		gda_value_free (av);
344 
345 		/* NOT NULL */
346 		g_value_set_boolean ((av = gda_value_new (G_TYPE_BOOLEAN)), !col->nullok);
347 		gda_tree_node_set_node_attribute (snode, MGR_COLUMNS_COL_NOTNULL_ATT_NAME, av, NULL);
348 		gda_value_free (av);
349 
350 		/* default value */
351 		g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), col->default_value);
352 		gda_tree_node_set_node_attribute (snode, MGR_COLUMNS_COL_DEFAULT_ATT_NAME, av, NULL);
353 		gda_value_free (av);
354 
355 		/* icon */
356 		BrowserIconType type = BROWSER_ICON_COLUMN;
357 		GdkPixbuf *pixbuf;
358 		gboolean is_fk = column_is_fk_part (dbo, index);
359 		if (col->pkey)
360 			type = BROWSER_ICON_COLUMN_PK;
361 		else if (!col->nullok) {
362 			if (is_fk)
363 				type = BROWSER_ICON_COLUMN_FK_NN;
364 			else
365 				type = BROWSER_ICON_COLUMN_NN;
366 		}
367 		else if (is_fk)
368 			type = BROWSER_ICON_COLUMN_FK;
369 
370 		pixbuf = browser_get_pixbuf_icon (type);
371 		av = gda_value_new (G_TYPE_OBJECT);
372 		g_value_set_object (av, pixbuf);
373 		gda_tree_node_set_node_attribute (snode, "icon", av, NULL);
374 		gda_value_free (av);
375 
376 		/* details */
377 		GString *details = NULL;
378 		if (col->pkey)
379 			details = g_string_new (_("Primary key"));
380 		if (is_fk) {
381 			if (details)
382 				g_string_append (details, ", ");
383 			else
384 				details = g_string_new ("");
385 			g_string_append (details, _("Foreign key"));
386 		}
387 		const GValue *autoinc;
388 		autoinc = gda_meta_table_column_get_attribute (col, GDA_ATTRIBUTE_AUTO_INCREMENT);
389 		if (autoinc) {
390 			if (details)
391 				g_string_append (details, ", ");
392 			else
393 				details = g_string_new ("");
394 			g_string_append (details, _("Auto incremented"));
395 		}
396 		if (details) {
397 			g_value_set_string ((av = gda_value_new (G_TYPE_STRING)), details->str);
398 			gda_tree_node_set_node_attribute (snode, MGR_COLUMNS_COL_DETAILS, av, NULL);
399 			gda_value_free (av);
400 			g_string_free (details, TRUE);
401 		}
402 
403 		nodes_list = g_slist_prepend (nodes_list, snode);
404 	}
405 
406 	if (ehash)
407 		g_hash_table_destroy (ehash);
408 
409 	return g_slist_reverse (nodes_list);
410 }
411