1 /* Widgets/DDL queries
2  *
3  * A DDL query is a query defining or modifying a database structure and properties.
4  * In Libgda, each type of DDL query corresponds to an "operation".
5  * Examples of "operation"s include creating a database, creating a table,
6  * adding a field to a table.
7  *
8  * Because the SQL corresponding to DDL queries varies widely between the
9  * DBMS, the information required to perform an operation is provided by
10  * each DBMS adapter (server provider), and specified as a set of named
11  * parameters.
12  *
13  * For each type of operation and for each type of provider, a GdaServerOperation
14  * object can be requested; this objects holds all the named parameters required
15  * or optional to perform the operation. Once values have been set to the
16  * named parameters, that object is passed back to the provider which can render
17  * it as SQL or perform the requested operation.
18  *
19  * The GdauiServerOperation widget can be used to display
20  * and assign value to the named parameters of a GdaServerOperation object.
21  */
22 
23 #include <libgda-ui/libgda-ui.h>
24 #include <string.h>
25 
26 static GtkWidget *window = NULL;
27 
28 typedef struct {
29 	GdaServerOperation      *op;
30 	GtkWidget               *op_container;
31 	GtkWidget               *op_form;
32 	GdauiProviderSelector *prov_sel;
33 	GtkWidget               *op_combo;
34 	GdaServerProvider       *prov;
35 
36 	GtkWidget               *top_window;
37 	GtkWidget               *sql_button;
38 	GtkWidget               *show_button;
39 } DemoData;
40 
41 static void tested_provider_changed_cb (GdauiProviderSelector *prov_sel, DemoData *data);
42 static void tested_operation_changed_cb (GdauiCombo *combo, DemoData *data);
43 static void update_possible_operations (DemoData *data);
44 
45 static void show_named_parameters (GtkButton *button, DemoData *data);
46 static void show_sql (GtkButton *button, DemoData *data);
47 
48 static GdaServerProvider *get_provider_obj (DemoData *data);
49 
50 GtkWidget *
do_ddl_queries(GtkWidget * do_widget)51 do_ddl_queries (GtkWidget *do_widget)
52 {
53 	if (!window) {
54 		GtkWidget *grid;
55 		GtkWidget *label;
56 		GtkWidget *wid;
57 		DemoData *data;
58 		GtkWidget *bbox;
59 		GtkWidget *sw, *vp;
60 
61                 data = g_new0 (DemoData, 1);
62 
63 		window = gtk_dialog_new_with_buttons ("DDL queries",
64 						      GTK_WINDOW (do_widget),
65 						      0,
66 						      GTK_STOCK_CLOSE,
67 						      GTK_RESPONSE_NONE,
68 						      NULL);
69 		data->top_window = window;
70 
71 		g_signal_connect (window, "response",
72 				  G_CALLBACK (gtk_widget_destroy), NULL);
73 		g_signal_connect (window, "destroy",
74 				  G_CALLBACK (gtk_widget_destroyed), &window);
75 
76 		grid = gtk_grid_new ();
77 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
78 				    grid, TRUE, TRUE, 0);
79 		gtk_container_set_border_width (GTK_CONTAINER (grid), 5);
80 
81 		label = gtk_label_new ("<b>Tested provider and operation:</b>");
82 		gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
83 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
84 		gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 2, 1);
85 
86 		/* provider selection */
87 		label = gtk_label_new ("Tested provider:");
88 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
89 		gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
90 
91 		wid = gdaui_provider_selector_new ();
92 		gdaui_provider_selector_set_provider (GDAUI_PROVIDER_SELECTOR (wid),
93 								  "SQLite");
94 		gtk_grid_attach (GTK_GRID (grid), wid, 1, 1, 1, 1);
95 		data->prov_sel = GDAUI_PROVIDER_SELECTOR (wid);
96 		g_signal_connect (G_OBJECT (data->prov_sel), "changed",
97 				  G_CALLBACK (tested_provider_changed_cb), data);
98 
99 		/* operation selection */
100 		label = gtk_label_new ("Tested operation:");
101 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
102 		gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
103 
104 		wid = gdaui_combo_new ();
105 		gtk_grid_attach (GTK_GRID (grid), wid, 1, 2, 1, 1);
106 		g_signal_connect (G_OBJECT (wid), "changed",
107 				  G_CALLBACK (tested_operation_changed_cb), data);
108 		data->op_combo = wid;
109 
110 		/* container for GdauiServerOperation */
111 		label = gtk_label_new ("<b>GdauiServerOperation widget:</b>");
112 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
113 		gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
114 		gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 2, 1);
115 
116 		sw = gtk_scrolled_window_new (FALSE, 0);
117 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
118 						GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
119 		gtk_widget_set_size_request (sw, 600, 450);
120 		gtk_grid_attach (GTK_GRID (grid), sw, 0, 4, 2, 1);
121 		vp = gtk_viewport_new (NULL, NULL);
122 		gtk_widget_set_name (vp, "gdaui-transparent-background");
123 		gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE);
124 		gtk_container_add (GTK_CONTAINER (sw), vp);
125 		data->op_container = vp;
126 
127 		/* bottom buttons */
128 		bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
129 		gtk_grid_attach (GTK_GRID (grid), bbox, 0, 5, 2, 1);
130 
131 		wid = gtk_button_new_with_label ("Show named parameters");
132 		data->show_button = wid;
133 		gtk_box_pack_start (GTK_BOX (bbox), wid, TRUE, TRUE, 0);
134 		g_signal_connect (G_OBJECT (wid), "clicked",
135 				  G_CALLBACK (show_named_parameters), data);
136 
137 		wid = gtk_button_new_with_label ("Show SQL");
138 		data->sql_button = wid;
139 		gtk_box_pack_start (GTK_BOX (bbox), wid, TRUE, TRUE, 0);
140 		g_signal_connect (G_OBJECT (wid), "clicked",
141 				  G_CALLBACK (show_sql), data);
142 
143 		tested_provider_changed_cb (data->prov_sel, data);
144 		gtk_combo_box_set_active (GTK_COMBO_BOX (data->op_combo), 1);
145 	}
146 
147 	gboolean visible;
148 	g_object_get (G_OBJECT (window), "visible", &visible, NULL);
149 	if (!visible)
150 		gtk_widget_show_all (window);
151 	else {
152 		gtk_widget_destroy (window);
153 		window = NULL;
154 	}
155 
156 	return window;
157 }
158 
159 static void
tested_provider_changed_cb(G_GNUC_UNUSED GdauiProviderSelector * prov_sel,DemoData * data)160 tested_provider_changed_cb (G_GNUC_UNUSED GdauiProviderSelector *prov_sel, DemoData *data)
161 {
162 	if (data->prov) {
163 		g_object_unref (data->prov);
164 		data->prov = NULL;
165 	}
166 	update_possible_operations (data);
167 }
168 
169 static void
update_possible_operations(DemoData * data)170 update_possible_operations (DemoData *data)
171 {
172 	GdaServerOperationType type;
173 	GdaDataModel *model;
174 
175 	model = gdaui_data_selector_get_model (GDAUI_DATA_SELECTOR (data->op_combo));
176 	if (!model) {
177 		gint columns[] = {1};
178 		model = gda_data_model_array_new_with_g_types (2, G_TYPE_INT, G_TYPE_STRING);
179 		gdaui_combo_set_model (GDAUI_COMBO (data->op_combo), model, 1, columns);
180 	}
181 	else
182 		gda_data_model_array_clear (GDA_DATA_MODEL_ARRAY (model));
183 
184 	for (type = 0; type < GDA_SERVER_OPERATION_LAST; type ++)
185 		if (gda_server_provider_supports_operation (get_provider_obj (data),
186 							    NULL, type, NULL)) {
187 			gint row;
188 
189 			row = gda_data_model_append_row (model, NULL);
190 			if (row < 0)
191 				g_error ("Cant' append data to a GdaDataModelArray");
192 			else {
193 				GValue value;
194 
195 				memset (&value, 0, sizeof (GValue));
196 				g_value_init (&value, G_TYPE_INT);
197 				g_value_set_int (&value, type);
198 				gda_data_model_set_value_at (model, 0, row, &value, NULL);
199 
200 				memset (&value, 0, sizeof (GValue));
201 				g_value_init (&value, G_TYPE_STRING);
202 				g_value_set_string (&value, gda_server_operation_op_type_to_string (type));
203 				gda_data_model_set_value_at (model, 1, row, &value, NULL);
204 			}
205 		}
206 }
207 
208 static GdaServerProvider *
get_provider_obj(DemoData * data)209 get_provider_obj (DemoData *data)
210 {
211 	GdaServerProvider *prov = NULL;
212 
213 	if (data->prov)
214 		prov = data->prov;
215 	else {
216 		/* create the GdaServerProvider object */
217 		data->prov = gdaui_provider_selector_get_provider_obj (data->prov_sel);
218 		prov = data->prov;
219 	}
220 
221 	return prov;
222 }
223 
224 static void
tested_operation_changed_cb(G_GNUC_UNUSED GdauiCombo * combo,DemoData * data)225 tested_operation_changed_cb (G_GNUC_UNUSED GdauiCombo *combo, DemoData *data)
226 {
227 	GdaServerProvider *prov = NULL;
228 	GdaServerOperationType type;
229 	GError *error = NULL;
230 	GdaDataModelIter *iter;
231 	const GValue *cvalue = NULL;
232 
233 	if (data->op) {
234 		g_object_unref (data->op);
235 		data->op = NULL;
236 	}
237 	if (data->op_form)
238 		gtk_widget_destroy (data->op_form);
239 
240 	gtk_widget_set_sensitive (data->show_button, FALSE);
241 	gtk_widget_set_sensitive (data->sql_button, FALSE);
242 
243 	iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (data->op_combo));
244 	if (iter)
245 		cvalue = gda_data_model_iter_get_value_at (iter, 0);
246 	if (!cvalue || !gda_value_isa ((GValue *) cvalue, G_TYPE_INT)) {
247 		GtkWidget *label;
248 
249 		label = gtk_label_new ("Select an operation to perform");
250 		gtk_container_add (GTK_CONTAINER (data->op_container), label);
251 		data->op_form = label;
252 		gtk_widget_show (data->op_form);
253 		return;
254 	}
255 	type = g_value_get_int ((GValue *) cvalue);
256 
257 	prov = get_provider_obj (data);
258 	if (prov)
259 		data->op = gda_server_provider_create_operation (prov, NULL, type, NULL, &error);
260 
261 	if (!data->op) {
262 		GtkWidget *label;
263 		gchar *str;
264 
265 		str = g_strdup_printf ("Can't create GdaServerOperation widget: %s",
266 				       error && error->message ? error->message : "No detail");
267 		label = gtk_label_new (str);
268 		g_free (str);
269 		gtk_container_add (GTK_CONTAINER (data->op_container), label);
270 		data->op_form = label;
271 	}
272 	else {
273 		GtkWidget *wid;
274 
275 		wid = gdaui_server_operation_new (data->op);
276 		gtk_container_add (GTK_CONTAINER (data->op_container), wid);
277 		data->op_form = wid;
278 		gtk_widget_set_sensitive (data->show_button, TRUE);
279 		gtk_widget_set_sensitive (data->sql_button, TRUE);
280 	}
281 	gtk_widget_show (data->op_form);
282 }
283 
284 static void
extract_named_parameters(GdaServerOperation * op,const gchar * root_path,GtkTextBuffer * tbuffer)285 extract_named_parameters (GdaServerOperation *op, const gchar *root_path, GtkTextBuffer *tbuffer)
286 {
287 	GdaServerOperationNode *node;
288 	GtkTextIter iter;
289 	gchar *str;
290 
291 	node = gda_server_operation_get_node_info (op, root_path);
292 	g_return_if_fail (node);
293 
294 	gtk_text_buffer_get_end_iter (tbuffer, &iter);
295 
296 	gtk_text_buffer_insert (tbuffer, &iter, "  * ", -1);
297 	if (node->status == GDA_SERVER_OPERATION_STATUS_REQUIRED)
298 		gtk_text_buffer_insert_with_tags_by_name (tbuffer, &iter, root_path, -1, "req_pathname", NULL);
299 	else
300 		gtk_text_buffer_insert_with_tags_by_name (tbuffer, &iter, root_path, -1, "opt_pathname", NULL);
301 	gtk_text_buffer_insert (tbuffer, &iter, " (", -1);
302 
303 	switch (node->type) {
304 	case GDA_SERVER_OPERATION_NODE_PARAMLIST: {
305 		GSList *params;
306 
307 		str = g_strdup_printf ("GdaSet @%p)\n", node->plist);
308 		gtk_text_buffer_insert (tbuffer, &iter, str, -1);
309 		g_free (str);
310 
311 		for (params = node->plist->holders; params; params = params->next) {
312 			gchar *npath;
313 			npath = g_strdup_printf ("%s/%s", root_path, gda_holder_get_id (GDA_HOLDER (params->data)));
314 			extract_named_parameters (op, npath, tbuffer);
315 			g_free (npath);
316 		}
317 
318 		break;
319 	}
320 	case GDA_SERVER_OPERATION_NODE_DATA_MODEL: {
321 		gint i, ncols;
322 
323 		str = g_strdup_printf ("GdaDataModel @%p)\n", node->model);
324 		gtk_text_buffer_insert (tbuffer, &iter, str, -1);
325 		g_free (str);
326 
327 		ncols = gda_data_model_get_n_columns (node->model);
328 		for (i = 0; i < ncols; i++) {
329 			GdaColumn *col = gda_data_model_describe_column (node->model, i);
330 			gchar *npath, *str;
331 
332 			g_object_get (G_OBJECT (col), "id", &str, NULL);
333 			npath = g_strdup_printf ("%s/@%s", root_path, str);
334 			g_free (str);
335 			extract_named_parameters (op, npath, tbuffer);
336 			g_free (npath);
337 		}
338 		break;
339 	}
340 	case GDA_SERVER_OPERATION_NODE_PARAM: {
341 		gchar *str;
342 		const GValue *value;
343 
344 		gtk_text_buffer_insert (tbuffer, &iter, "GdaHolder) = ", -1);
345 
346 		value = gda_holder_get_value (node->param);
347 		str = gda_value_stringify (value);
348 		gtk_text_buffer_insert (tbuffer, &iter, str, -1);
349 		gtk_text_buffer_insert (tbuffer, &iter, "\n", -1);
350 		g_free (str);
351 		break;
352 	}
353 	case GDA_SERVER_OPERATION_NODE_SEQUENCE: {
354 		gtk_text_buffer_insert (tbuffer, &iter, "Sequence)\n", -1);
355 		guint i, size = gda_server_operation_get_sequence_size (op, root_path);
356 		for (i = 0; i < size; i++) {
357 			gchar **names;
358 			names = gda_server_operation_get_sequence_item_names (op, root_path);
359 			guint n;
360 			for (n = 0; names [n]; n++) {
361 				gchar *npath;
362 				npath = g_strdup_printf ("%s/%u%s", root_path, i, names [n]);
363 				extract_named_parameters (op, npath, tbuffer);
364 				g_free (npath);
365 			}
366 			g_strfreev (names);
367 		}
368 		break;
369 	}
370 
371 	case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM:
372 		gtk_text_buffer_insert (tbuffer, &iter, "Sequence item)\n", -1);
373 		break;
374 
375 	case GDA_SERVER_OPERATION_NODE_DATA_MODEL_COLUMN: {
376 		gint j, nrows;
377 
378 		gtk_text_buffer_insert (tbuffer, &iter, "Model column)\n", -1);
379 
380 		nrows = gda_data_model_get_n_rows (node->model);
381 		for (j = 0; j < nrows; j++) {
382 			gchar *npath, *str;
383 			const GValue *value;
384 
385 			npath = g_strdup_printf ("%s/%d", root_path, j);
386 			value = gda_data_model_get_value_at (node->model, gda_column_get_position  (node->column), j, NULL);
387 			if (value)
388 				str = gda_value_stringify (value);
389 			else
390 				str = g_strdup ("Error: could not read data model's value");
391 			gtk_text_buffer_insert (tbuffer, &iter, "  * ", -1);
392 			gtk_text_buffer_insert_with_tags_by_name (tbuffer, &iter, npath, -1, "opt_pathname", NULL);
393 			g_free (npath);
394 			gtk_text_buffer_insert (tbuffer, &iter, " (GValue) = ", -1);
395 			gtk_text_buffer_insert (tbuffer, &iter, str, -1);
396 			gtk_text_buffer_insert (tbuffer, &iter, "\n", -1);
397 			g_free (str);
398 		}
399 
400 		break;
401 	}
402 	default:
403 		gtk_text_buffer_insert (tbuffer, &iter, "???", -1);
404 		break;
405 	}
406 }
407 
408 static void
show_named_parameters(G_GNUC_UNUSED GtkButton * button,DemoData * data)409 show_named_parameters (G_GNUC_UNUSED GtkButton *button, DemoData *data)
410 {
411 	GtkWidget *dlg, *label;
412 	gchar **root_nodes;
413 	gint i;
414 	GtkWidget *view;
415 	GtkWidget *sw;
416 	GtkTextBuffer *buffer;
417 	GtkTextIter iter;
418 
419 	if (!data->op || !data->op_form || ! GDAUI_IS_SERVER_OPERATION (data->op_form))
420 		return;
421 
422 	/* dialog box */
423 	dlg = gtk_dialog_new_with_buttons ("Named parameters",
424 					   GTK_WINDOW (data->top_window),
425 					   GTK_DIALOG_MODAL,
426 					   GTK_STOCK_CLOSE, GTK_RESPONSE_REJECT, NULL);
427 
428 	label = gtk_label_new ("<b>Named parameters:</b>\n");
429 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
430 	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
431 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
432 			    label, FALSE, FALSE, 0);
433 	gtk_widget_show (label);
434 
435 	/* text area */
436 	view = gtk_text_view_new ();
437 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
438 	gtk_text_buffer_get_start_iter (buffer, &iter);
439 	gtk_text_buffer_create_tag (buffer, "opt_pathname",
440                                     "weight", PANGO_WEIGHT_BOLD,
441                                     "foreground", "grey", NULL);
442 	gtk_text_buffer_create_tag (buffer, "req_pathname",
443                                     "weight", PANGO_WEIGHT_BOLD,
444                                     "foreground", "blue", NULL);
445 
446 	xmlNodePtr xml;
447 	xml = gda_server_operation_save_data_to_xml (data->op, NULL);
448 	if (xml) {
449 		g_print ("XML rendering of the GdaServerOperation is:\n");
450                 xmlBufferPtr buffer;
451                 buffer = xmlBufferCreate ();
452                 xmlNodeDump (buffer, NULL, xml, 0, 1);
453                 xmlFreeNode (xml);
454                 xmlBufferDump (stdout, buffer);
455                 xmlBufferFree (buffer);
456 		g_print ("\n");
457 	}
458 	else {
459 		g_print ("XML rendering ERROR\n");
460 	}
461 
462 	root_nodes = gda_server_operation_get_root_nodes (data->op);
463 	for (i = 0; root_nodes && root_nodes[i]; i++)
464 		extract_named_parameters (data->op, root_nodes[i], buffer);
465 	g_strfreev (root_nodes);
466 
467 	sw = gtk_scrolled_window_new (NULL, NULL);
468 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
469 					GTK_POLICY_AUTOMATIC,
470 					GTK_POLICY_AUTOMATIC);
471 	gtk_container_add (GTK_CONTAINER (sw), view);
472 	gtk_widget_show_all (sw);
473 
474 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
475 			    sw, TRUE, TRUE, 0);
476 	gtk_widget_set_size_request (dlg, 530, 350);
477 
478 	gtk_dialog_run (GTK_DIALOG (dlg));
479 	gtk_widget_destroy (dlg);
480 }
481 
482 static void
show_sql(G_GNUC_UNUSED GtkButton * button,DemoData * data)483 show_sql (G_GNUC_UNUSED GtkButton *button, DemoData *data)
484 {
485 	GdaServerProvider *prov;
486 
487 	if (!data->op)
488 		return;
489 
490 	prov = get_provider_obj (data);
491 	if (prov) {
492 		gchar *sql, *msg;
493 		GtkMessageType msg_type = GTK_MESSAGE_INFO;
494 		GtkWidget *dlg;
495 		GError *error = NULL;
496 
497 		sql = gda_server_provider_render_operation (prov, NULL, data->op, &error);
498 		if (!sql) {
499 			msg_type = GTK_MESSAGE_ERROR;
500 			msg = g_strdup_printf ("<b>Can't render operation as SQL:</b>\n%s\n",
501 					       error && error->message ? error->message :
502 					       "No detail (This operation may not be accessible using SQL)");
503 			if (error)
504 				g_error_free (error);
505 		}
506 		else
507 			msg = g_strdup_printf ("<b>SQL:</b>\n%s", sql);
508 
509 		dlg = gtk_message_dialog_new (GTK_WINDOW (data->top_window),
510 					      GTK_DIALOG_MODAL, msg_type, GTK_BUTTONS_CLOSE, NULL);
511 		gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dlg), msg);
512 		g_free (sql);
513 		g_free (msg);
514 
515 		gtk_dialog_run (GTK_DIALOG (dlg));
516 		gtk_widget_destroy (dlg);
517 	}
518 	else
519 		g_warning ("Could not get provider object");
520 }
521