1 /*
2  * Copyright (C) 2011 Vivien Malerba <malerba@gnome-db.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #include <glib/gi18n-lib.h>
20 #include "analyser.h"
21 #include "../browser-window.h"
22 
23 static gchar *compute_fk_dependency (GdaMetaTableForeignKey *fkey, GSList *selfields, gboolean reverse,
24 				     DataSource *source, xmlNodePtr *out_sourcespec);
25 
26 static void add_data_source_mitem_activated_cb (GtkMenuItem *mitem, DataSourceManager *mgr);
27 
28 GSList *
29 data_manager_add_proposals_to_menu (GtkWidget *menu,
30 				    DataSourceManager *mgr, DataSource *source,
31 				    GtkWidget *error_attach_widget)
32 {
33 	g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
34 	g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
35 	g_return_val_if_fail (GTK_IS_WIDGET (error_attach_widget), NULL);
36 
37 	GtkWidget *mitem;
38 	gboolean add_separator = TRUE;
39 	GSList *added_list = NULL;
40 
41 	GdaStatement *stmt;
check_stability(opus_int16 * A_Q12,int order)42 	stmt = data_source_get_statement (source);
43 	if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) {
44 		GSList *fields = NULL, *flist;
45 		GdaSqlStatement *sql_statement;
46 		BrowserConnection *bcnc;
47 		GHashTable *hash; /* key = a menu string, value= 0x1 */
48 
49 		g_object_get (G_OBJECT (stmt), "structure", &sql_statement, NULL);
50 
51 		bcnc = data_source_manager_get_browser_cnc (mgr);
52 		if (browser_connection_check_sql_statement_validify (bcnc, sql_statement, NULL))
53 			fields = ((GdaSqlStatementSelect *) sql_statement->contents)->expr_list;
54 
55 		hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
56 		for (flist = fields; flist; flist = flist->next) {
57 			GdaSqlSelectField *select_field = (GdaSqlSelectField*) flist->data;
58 			if (!select_field->validity_meta_table_column)
59 				continue;
60 			GdaMetaDbObject *dbo;
61 			dbo = select_field->validity_meta_object;
62 			if (dbo->obj_type == GDA_META_DB_TABLE) {
63 				GdaMetaTable *mtable;
64 				mtable = GDA_META_TABLE (dbo);
65 
66 				GSList *fklist;
67 				for (fklist = mtable->reverse_fk_list; fklist; fklist = fklist->next) {
68 					GdaMetaTableForeignKey *fkey;
69 					fkey = (GdaMetaTableForeignKey *) fklist->data;
70 					gchar *tmp;
71 					xmlNodePtr sourcespec = NULL;
72 					if (fkey->meta_table->obj_type != GDA_META_DB_TABLE)
73 						continue;
74 					tmp = compute_fk_dependency (fkey, fields, FALSE,
75 								     source, &sourcespec);
76 					if (!tmp)
77 						continue;
78 					if (g_hash_table_lookup (hash, tmp)) {
79 						g_free (tmp);
80 						continue;
81 					}
82 
83 					if (add_separator) {
84 						mitem = gtk_separator_menu_item_new ();
85 						gtk_widget_show (mitem);
86 						gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
87 						add_separator = FALSE;
88 					}
89 
main(void)90 					mitem = gtk_menu_item_new_with_label (tmp);
91 					added_list = g_slist_prepend (added_list, mitem);
92 					g_object_set_data_full ((GObject*) mitem, "xml", sourcespec,
93 								(GDestroyNotify) xmlFreeNode);
94 					g_object_set_data_full (G_OBJECT (mitem), "attachwidget",
95 								g_object_ref (error_attach_widget),
96 								g_object_unref);
97 					g_signal_connect (mitem, "activate",
98 							  G_CALLBACK (add_data_source_mitem_activated_cb),
99 							  mgr);
100 					gtk_widget_show (mitem);
101 					gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
102 					g_hash_table_insert (hash, tmp, (gpointer) 0x1);
103 				}
104 				for (fklist = mtable->fk_list; fklist; fklist = fklist->next) {
105 					GdaMetaTableForeignKey *fkey;
106 					fkey = (GdaMetaTableForeignKey *) fklist->data;
107 					gchar *tmp;
108 					xmlNodePtr sourcespec = NULL;
109 					if (fkey->depend_on->obj_type != GDA_META_DB_TABLE)
110 						continue;
111 					tmp = compute_fk_dependency (fkey, fields, TRUE,
112 								     source, &sourcespec);
113 					if (!tmp)
114 						continue;
115 					if (g_hash_table_lookup (hash, tmp)) {
116 						g_free (tmp);
117 						continue;
118 					}
119 
120 					if (add_separator) {
121 						mitem = gtk_separator_menu_item_new ();
122 						gtk_widget_show (mitem);
123 						gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
124 						add_separator = FALSE;
125 					}
126 
127 					mitem = gtk_menu_item_new_with_label (tmp);
128 					added_list = g_slist_prepend (added_list, mitem);
129 					g_object_set_data_full ((GObject*) mitem, "xml", sourcespec,
130 								(GDestroyNotify) xmlFreeNode);
131 					g_object_set_data_full (G_OBJECT (mitem), "attachwidget",
132 								g_object_ref (error_attach_widget),
133 								g_object_unref);
134 					g_signal_connect (mitem, "activate",
135 							  G_CALLBACK (add_data_source_mitem_activated_cb),
136 							  mgr);
137 					gtk_widget_show (mitem);
138 					gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
139 					g_hash_table_insert (hash, tmp, (gpointer) 0x1);
140 				}
141 			}
142 		}
143 		g_hash_table_destroy (hash);
144 		gda_sql_statement_free (sql_statement);
145 	}
146 
147 	return added_list;
148 }
149 
150 /*
151  * Returns: a new list of #GdaSqlSelectField (which are already listed in @selfields), or %NULL
152  */
153 static gchar *
154 compute_fk_dependency (GdaMetaTableForeignKey *fkey, GSList *selfields, gboolean reverse,
155 		       DataSource *source, xmlNodePtr *out_sourcespec)
156 {
157 	GString *string = NULL;
158 	gint i;
159 	gint *index_array;
160 	GdaMetaTable *table;
161 
162 	*out_sourcespec = NULL;
163 
164 	if (reverse) {
165 		table = GDA_META_TABLE (fkey->meta_table);
166 		index_array = fkey->fk_cols_array;
167 	}
168 	else {
169 		table = GDA_META_TABLE (fkey->depend_on);
170 		index_array = fkey->ref_pk_cols_array;
171 	}
172 	for (i = 0; i < fkey->cols_nb; i++) {
173 		gint pos;
174 		GdaMetaTableColumn *col;
175 		pos = index_array[i] - 1;
176 		col = g_slist_nth_data (table->columns, pos);
177 
178 		/* make sure @col is among @selfields */
179 		GSList *flist;
180 		gboolean found = FALSE;
181 		for (flist = selfields; flist; flist = flist->next) {
182 			GdaSqlSelectField *select_field = (GdaSqlSelectField*) flist->data;
183 			if (!select_field->validity_meta_object ||
184 			    !select_field->validity_meta_table_column)
185 				continue;
186 			GdaMetaTableColumn *field;
187 			field = select_field->validity_meta_table_column;
188 			if (field == col) {
189 				found = TRUE;
190 				if (reverse) {
191 					if (!string) {
192 						string = g_string_new (_("Obtain referenced data in table "));
193 						g_string_append_printf (string, "%s from ",
194 									fkey->depend_on->obj_short_name);
195 					}
196 					else
197 						g_string_append (string, ", ");
198 					g_string_append_printf (string, "column n.%d (",
199 								g_slist_position (selfields, flist) + 1);
200 					if (select_field->as)
201 						g_string_append (string, select_field->as);
202 					else
203 						g_string_append (string,
204 								 select_field->validity_meta_table_column->column_name);
205 					g_string_append_c (string, ')');
206 				}
207 				else {
208 					if (!string) {
209 						string = g_string_new (_("List referencing data in "));
210 						g_string_append_printf (string, "%s.",
211 									fkey->meta_table->obj_short_name);
212 					}
213 					else
214 						g_string_append (string, ", ");
215 					g_string_append (string, fkey->fk_names_array [i]);
216 				}
217 				break;
218 			}
219 		}
220 
221 		if (! found) {
222 			if (string) {
223 				g_string_free (string, TRUE);
224 				string = NULL;
225 			}
226 			break;
227 		}
228 	}
229 
230 	if (string) {
231 		xmlNodePtr node, snode;
232 		node = xmlNewNode (NULL, BAD_CAST "table");
233 		xmlSetProp (node, BAD_CAST "name",
234  			    BAD_CAST (reverse ? GDA_META_DB_OBJECT (fkey->depend_on)->obj_short_name :
235 				      GDA_META_DB_OBJECT (fkey->meta_table)->obj_short_name));
236 
237 		snode = xmlNewChild (node, NULL, BAD_CAST "depend", NULL);
238 		xmlSetProp (snode, BAD_CAST "foreign_key_table",
239 			    BAD_CAST GDA_META_DB_OBJECT (table)->obj_short_name);
240 
241 		xmlSetProp (snode, BAD_CAST "id", BAD_CAST data_source_get_id (source));
242 
243 		gint i;
244 		for (i = 0; i < fkey->cols_nb; i++)
245 			xmlNewChild (snode, NULL, BAD_CAST "column", BAD_CAST (fkey->fk_names_array[i]));
246 
247 		*out_sourcespec = node;
248 		return g_string_free (string, FALSE);
249 	}
250 	else
251 		return NULL;
252 }
253 
254 static void
255 add_data_source_mitem_activated_cb (GtkMenuItem *mitem, DataSourceManager *mgr)
256 {
257 	DataSource *source;
258 	GError *lerror = NULL;
259 	xmlNodePtr sourcespec;
260 	BrowserConnection *bcnc;
261 
262 	bcnc = data_source_manager_get_browser_cnc (mgr);
263 	sourcespec = (xmlNodePtr) g_object_get_data ((GObject*) mitem, "xml");
264 	source = data_source_new_from_xml_node (bcnc, sourcespec, &lerror);
265 	if (source) {
266 		data_source_manager_add_source (mgr, source);
267 		g_object_unref (source);
268 	}
269 	else {
270 		BrowserWindow *bwin;
271 		GtkWidget *attachwidget;
272 		attachwidget = g_object_get_data (G_OBJECT (mitem), "attachwidget");
273 		g_assert (attachwidget);
274 		bwin = BROWSER_WINDOW (gtk_widget_get_toplevel (attachwidget));
275 		browser_window_show_notice_printf (bwin, GTK_MESSAGE_ERROR, "data-widget-add-new-source",
276 						   _("Error adding new data source: %s"),
277 						   lerror && lerror->message ? lerror->message :
278 						   _("No detail"));
279 		g_clear_error (&lerror);
280 	}
281 
282 #ifdef GDA_DEBUG_NO
283 	xmlBufferPtr buffer;
284 	buffer = xmlBufferCreate ();
285 	xmlNodeDump (buffer, NULL, sourcespec, 0, 1);
286 	g_print ("Source to ADD: [%s]\n", (gchar *) xmlBufferContent (buffer));
287 	xmlBufferFree (buffer);
288 #endif
289 }
290