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 (®istering);
141 if (type == 0)
142 type = g_type_register_static (GDA_TYPE_TREE_MANAGER, "MgrColumns", &info, 0);
143 g_mutex_unlock (®istering);
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