1 /*
2  * Copyright (C) 2006 - 2008 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2006 - 2011 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2007 Leonardo Boshell <lb@kmc.com.co>
5  * Copyright (C) 2008 Armin Burgmeier <armin@openismus.com>
6  * Copyright (C) 2008 Phil Longstaff <plongstaff@rogers.com>
7  * Copyright (C) 2008 Przemysław Grzegorczyk <pgrzegorczyk@gmail.com>
8  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
9  * Copyright (C) 2010 David King <davidk@openismus.com>
10  * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
11  * Copyright (C) 2011 - 2012 Daniel Espinosa <despinosa@src.gnome.org>
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the
25  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
26  * Boston, MA  02110-1301, USA.
27  */
28 
29 #include <stdlib.h>
30 #include <glib.h>
31 #include <libgda/gda-marshal.h>
32 #include <libgda/gda-server-provider.h>
33 #include <libgda/gda-server-operation.h>
34 #include <libgda/gda-set.h>
35 #include <libgda/gda-holder.h>
36 #include <libgda/gda-connection.h>
37 #include <libgda/gda-config.h>
38 #include <libgda/gda-data-model-private.h>
39 #include <libgda/gda-data-model-import.h>
40 #include <libgda/gda-data-model-array.h>
41 #include "gda-util.h"
42 #include <string.h>
43 #ifdef HAVE_LOCALE_H
44 #include <locale.h>
45 #endif
46 #include <glib/gi18n-lib.h>
47 
48 extern gchar *gda_lang_locale;
49 
50 #define CLASS(operation) (GDA_SERVER_OPERATION_CLASS (G_OBJECT_GET_CLASS (operation)))
51 
52 static void gda_server_operation_class_init (GdaServerOperationClass *klass);
53 static void gda_server_operation_init       (GdaServerOperation *operation,
54 					    GdaServerOperationClass *klass);
55 static void gda_server_operation_dispose   (GObject *object);
56 
57 static void gda_server_operation_set_property (GObject *object,
58 					       guint param_id,
59 					       const GValue *value,
60 					       GParamSpec *pspec);
61 static void gda_server_operation_get_property (GObject *object,
62 					       guint param_id,
63 					       GValue *value,
64 					       GParamSpec *pspec);
65 
66 /* signals */
67 enum
68 {
69 	SEQUENCE_ITEM_ADDED,
70 	SEQUENCE_ITEM_REMOVE,
71 	LAST_SIGNAL
72 };
73 
74 static gint gda_server_operation_signals[LAST_SIGNAL] = { 0, 0 };
75 
76 /* properties */
77 enum
78 {
79 	PROP_0,
80 	PROP_CNC,
81 	PROP_PROV,
82 	PROP_OP_TYPE,
83 	PROP_SPEC_FILE
84 };
85 
86 extern xmlDtdPtr _gda_server_op_dtd;
87 static GObjectClass *parent_class = NULL;
88 
89 typedef struct _Node {
90 	struct _Node                 *parent;
91 	GdaServerOperationNodeType    type;
92 	GdaServerOperationNodeStatus  status;
93 	gchar                        *path_name; /* NULL for GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM nodes */
94 	union {
95 		GdaSet               *plist;
96 		GdaDataModel         *model;
97 		GdaHolder            *param;
98 		struct {
99 			GSList       *seq_tmpl; /* list of Node templates */
100 			guint         min_items;
101 			guint         max_items;
102 			GSList       *seq_items; /* list of Node of type GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM */
103 			gchar        *name;
104 			gchar        *descr;
105 			xmlNodePtr    xml_spec; /* references a op->priv->xml_spec_doc node,
106 						   for future instantiation of nodes */
107 		}                     seq;
108 		GSList               *seq_item_nodes; /* list of Node structures composing the item */
109 	}                             d;
110 } Node;
111 #define NODE(x) ((Node*)(x))
112 
113 static void   node_destroy (GdaServerOperation *op, Node *node);
114 static Node  *node_new (Node *parent, GdaServerOperationNodeType type, const gchar *path);
115 static void   sequence_add_item (GdaServerOperation *op, Node *node);
116 static Node  *node_find (GdaServerOperation *op, const gchar *path);
117 static Node  *node_find_or_create (GdaServerOperation *op, const gchar *path);
118 static gchar *node_get_complete_path (GdaServerOperation *op, Node *node);
119 static void   clean_nodes_info_cache (GdaServerOperation *operation);
120 struct _GdaServerOperationPrivate {
121 	GdaServerOperationType  op_type;
122 	gboolean                cnc_set;
123 	GdaConnection          *cnc;
124 	gboolean                prov_set;
125 	GdaServerProvider      *prov;
126 
127 	xmlDocPtr               xml_spec_doc;
128 	GSList                 *sources; /* list of GdaDataModel as sources for the parameters */
129 
130 	GSList                 *allnodes; /* list of all the Node structures, referenced here only */
131 	GSList                 *topnodes; /* list of the "/(*)" named nodes, not referenced here  */
132 	GHashTable             *info_hash; /* key = path, value = a GdaServerOperationNode */
133 };
134 
135 
136 
137 /*
138  * GdaServerOperation class implementation
139  */
140 static void
141 gda_server_operation_class_init (GdaServerOperationClass *klass)
142 {
143 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
144 
145 	parent_class = g_type_class_peek_parent (klass);
146 
147 	/* signals */
148 	/**
149 	 * GdaServerOperation::sequence-item-added:
150 	 * @op: the #GdaServerOperation
151 	 * @seq_path: the path to the new sequence item
152 	 * @item_index: the index (starting from 0) of the new sequence item in the sequence
153 	 *
154 	 * Gets emitted whenever a new sequence item (from a sequence template) has been added
155 	 */
156 	gda_server_operation_signals[SEQUENCE_ITEM_ADDED] =
157 		g_signal_new ("sequence-item-added",
158 			      G_TYPE_FROM_CLASS (object_class),
159 			      G_SIGNAL_RUN_FIRST,
160 			      G_STRUCT_OFFSET (GdaServerOperationClass, seq_item_added),
161 			      NULL, NULL,
162 			      _gda_marshal_VOID__STRING_INT, G_TYPE_NONE,
163 			      2, G_TYPE_STRING, G_TYPE_INT);
164 	/**
165 	 * GdaServerOperation::sequence-item-remove:
166 	 * @op: the #GdaServerOperation
167 	 * @seq_path: the path to the sequence item to be removed
168 	 * @item_index: the index (starting from 0) of the sequence item in the sequence
169 	 *
170 	 * Gets emitted whenever a sequence item is about to be removed
171 	 */
172 	gda_server_operation_signals[SEQUENCE_ITEM_REMOVE] =
173 		g_signal_new ("sequence-item-remove",
174 			      G_TYPE_FROM_CLASS (object_class),
175 			      G_SIGNAL_RUN_FIRST,
176 			      G_STRUCT_OFFSET (GdaServerOperationClass, seq_item_remove),
177 			      NULL, NULL,
178 			      _gda_marshal_VOID__STRING_INT, G_TYPE_NONE,
179 			      2, G_TYPE_STRING, G_TYPE_INT);
180 
181 	klass->seq_item_added = NULL;
182 	klass->seq_item_remove = NULL;
183 
184 	object_class->dispose = gda_server_operation_dispose;
185 
186 	/* Properties */
187 	object_class->set_property = gda_server_operation_set_property;
188 	object_class->get_property = gda_server_operation_get_property;
189 
190 	g_object_class_install_property (object_class, PROP_CNC,
191 					 g_param_spec_object ("connection", NULL, "Connection to use",
192 							      GDA_TYPE_CONNECTION,
193 							      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
194 	g_object_class_install_property (object_class, PROP_PROV,
195 					 g_param_spec_object ("provider", NULL,
196 							      "Database provider which created the object",
197 							      GDA_TYPE_SERVER_PROVIDER,
198 							      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
199 	g_object_class_install_property (object_class, PROP_SPEC_FILE,
200 					 g_param_spec_string ("spec-filename", NULL,
201 							      "XML file which contains the object's data structure",
202 							      NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
203 	g_object_class_install_property (object_class, PROP_OP_TYPE,
204 					 g_param_spec_int ("op-type", NULL, "Type of operation to be done",
205 							   0, GDA_SERVER_OPERATION_LAST - 1,
206 							   0, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
207 }
208 
209 static void
210 gda_server_operation_init (GdaServerOperation *operation,
211 			  G_GNUC_UNUSED GdaServerOperationClass *klass)
212 {
213 	g_return_if_fail (GDA_IS_SERVER_OPERATION (operation));
214 
215 	operation->priv = g_new0 (GdaServerOperationPrivate, 1);
216 	operation->priv->allnodes = NULL;
217 	operation->priv->info_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
218 }
219 
220 static void
221 clean_nodes_info_cache (GdaServerOperation *operation)
222 {
223 	if (operation->priv->info_hash)
224 		g_hash_table_destroy (operation->priv->info_hash);
225 
226 	operation->priv->info_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
227 }
228 
229 
230 static void
231 gda_server_operation_dispose (GObject *object)
232 {
233 	GdaServerOperation *operation = (GdaServerOperation *) object;
234 
235 	g_return_if_fail (GDA_IS_SERVER_OPERATION (operation));
236 
237 	/* free memory */
238 	if (operation->priv) {
239 		if (operation->priv->info_hash)
240 			g_hash_table_destroy (operation->priv->info_hash);
241 
242 		if (operation->priv->cnc)
243 			g_object_unref (operation->priv->cnc);
244 		if (operation->priv->prov)
245 			g_object_unref (operation->priv->prov);
246 
247 		while (operation->priv->topnodes)
248 			node_destroy (operation, NODE (operation->priv->topnodes->data));
249 		g_assert (!operation->priv->allnodes);
250 
251 		/* don't free operation->priv->xml_spec_doc */
252 
253 		if (operation->priv->sources) {
254 			g_slist_foreach (operation->priv->sources, (GFunc) g_object_unref, NULL);
255 			g_slist_free (operation->priv->sources);
256 		}
257 
258 		g_free (operation->priv);
259 		operation->priv = NULL;
260 	}
261 
262 	/* chain to parent class */
263 	parent_class->dispose (object);
264 }
265 
266 /* module error */
267 GQuark
268 gda_server_operation_error_quark (void)
269 {
270         static GQuark quark;
271         if (!quark)
272                 quark = g_quark_from_static_string ("gda_server_operation_error");
273         return quark;
274 }
275 
276 GType
277 gda_server_operation_get_type (void)
278 {
279 	static GType type = 0;
280 
281 	if (G_UNLIKELY (type == 0)) {
282 		static GMutex registering;
283 
284 		static const GTypeInfo info = {
285 			sizeof (GdaServerOperationClass),
286 			(GBaseInitFunc) NULL,
287 			(GBaseFinalizeFunc) NULL,
288 			(GClassInitFunc) gda_server_operation_class_init,
289 			NULL,
290 			NULL,
291 			sizeof (GdaServerOperation),
292 			0,
293 			(GInstanceInitFunc) gda_server_operation_init,
294 			0
295 		};
296 
297 		g_mutex_lock (&registering);
298 		if (!type)
299 			type = g_type_register_static (G_TYPE_OBJECT, "GdaServerOperation", &info, 0);
300 		g_mutex_unlock (&registering);
301 	}
302 	return type;
303 }
304 
305 /* create a new Node structure */
306 static Node *
307 node_new (Node *parent, GdaServerOperationNodeType type, const gchar *path)
308 {
309 	Node *node;
310 
311 	node = g_new0 (Node, 1);
312 	node->parent = parent;
313 	node->type = type;
314 	node->status = GDA_SERVER_OPERATION_STATUS_REQUIRED;
315 	node->path_name = g_strdup (path);
316 
317 	return node;
318 }
319 
320 /* destroy a Node structure */
321 static void
322 node_destroy (GdaServerOperation *op, Node *node)
323 {
324 	switch (node->type) {
325 	case GDA_SERVER_OPERATION_NODE_PARAMLIST:
326 		g_object_unref (G_OBJECT (node->d.plist));
327 		break;
328 	case GDA_SERVER_OPERATION_NODE_DATA_MODEL:
329 		g_object_unref (G_OBJECT (node->d.model));
330 		break;
331 	case GDA_SERVER_OPERATION_NODE_PARAM:
332 		g_object_unref (G_OBJECT (node->d.param));
333 		break;
334 	case GDA_SERVER_OPERATION_NODE_SEQUENCE: {
335 		GSList *list;
336 
337 		for (list = node->d.seq.seq_tmpl; list; list = list->next)
338 			node_destroy (op, NODE (list->data));
339 		g_slist_free (node->d.seq.seq_tmpl);
340 
341 		for (list = node->d.seq.seq_items; list; list = list->next)
342 			node_destroy (op, NODE (list->data));
343 		g_slist_free (node->d.seq.seq_items);
344 
345 		g_free (node->d.seq.name);
346 		g_free (node->d.seq.descr);
347 		break;
348 	}
349 	case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM: {
350 		GSList *list;
351 
352 		for (list = node->d.seq_item_nodes; list; list = list->next)
353 			node_destroy (op, NODE (list->data));
354 		g_slist_free (node->d.seq_item_nodes);
355 		break;
356 	}
357 	default:
358 		g_assert_not_reached ();
359 		break;
360 	}
361 
362 	g_free (node->path_name);
363 	if (op) {
364 		op->priv->topnodes = g_slist_remove (op->priv->topnodes, node);
365 		op->priv->allnodes = g_slist_remove (op->priv->allnodes, node);
366 	}
367 
368 	g_free (node);
369 }
370 
371 /*
372  * Find a Node from its full path
373  */
374 static Node *
375 node_find (GdaServerOperation *op, const gchar *path)
376 {
377 	Node *node = NULL;
378 	GSList *list;
379 
380 	if (!path || !*path || (*path != '/'))
381 		return NULL;
382 
383 	for (list = op->priv->allnodes; list; list = list->next) {
384 		gchar *str;
385 		str = node_get_complete_path (op, NODE (list->data));
386 		if (!strcmp (str, path)) {
387 			node = NODE (list->data);
388 			g_free (str);
389 			break;
390 		}
391 		g_free (str);
392 	}
393 	/*g_print ("%s(%s) => %p\n", __FUNCTION__, path, node);*/
394 	return node;
395 }
396 
397 /*
398  * Find a node from its full path and if it does not exist, tries to
399  * create it (for sequences' items)
400  */
401 static Node *
402 node_find_or_create (GdaServerOperation *op, const gchar *path)
403 {
404 	Node *node;
405 
406 	if (!path || !*path || (*path != '/'))
407 		return NULL;
408 
409 	node = node_find (op, path);
410 	if (!node) {
411 		gchar *cpath = g_strdup (path);
412 		gchar *ptr;
413 		gchar *root, *ext;
414 
415 		/* separate @path to <root>/<ext> */
416 		ptr = cpath + strlen (cpath) - 1;
417 		while (*ptr && (*ptr != '/')) ptr--;
418 		*ptr = 0;
419 
420 		root = cpath;
421 		ext = ptr+1;
422 
423 		/* treatment */
424 		node = node_find_or_create (op, root);
425 		if (node)
426 			switch (node->type) {
427 			case GDA_SERVER_OPERATION_NODE_SEQUENCE: {
428 				gint index;
429 
430 				index = strtol (ext, &ptr, 10);
431 				if (ptr && *ptr)
432 					index = -1; /* could not convert array[i] to an int */
433 				if (index >= 0) {
434 					gint i;
435 
436 					for (i = g_slist_length (node->d.seq.seq_items); i <= index; i++)
437 						sequence_add_item (op, node);
438 					node = node_find (op, path);
439 					g_assert (node);
440 				}
441 				break;
442 			}
443 			case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM: {
444 				node = node_find (op, path);
445 				g_assert (node);
446 				break;
447 			}
448 			default:
449 				node = NULL;
450 				break;
451 		}
452 		g_free(cpath);
453 	}
454 
455 	/*g_print ("# %s (%s) => %p\n", __FUNCTION__, path, node);*/
456 
457 	return node;
458 }
459 
460 /*
461  * Computes the complete path of a node
462  */
463 static gchar *
464 node_get_complete_path (G_GNUC_UNUSED GdaServerOperation *op, Node *node)
465 {
466 	GString *string;
467 	gchar *retval;
468 	Node *lnode;
469 
470 	if (!node)
471 		return NULL;
472 
473 	string = g_string_new ("");
474 	for (lnode = node; lnode; lnode = lnode->parent) {
475 		if (lnode->type == GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM) {
476 			gchar *str;
477 
478 			g_assert (lnode->parent);
479 			g_assert (lnode->parent->type == GDA_SERVER_OPERATION_NODE_SEQUENCE);
480 			str = g_strdup_printf ("%d", g_slist_index (lnode->parent->d.seq.seq_items, lnode));
481 			g_string_prepend (string, str);
482 			g_free (str);
483 		}
484 		else
485 			g_string_prepend (string, lnode->path_name);
486 		g_string_prepend_c (string, '/');
487 	}
488 
489 	retval = string->str;
490 	g_string_free (string, FALSE);
491 
492 	/*g_print ("%s(%p) => %s\n", __FUNCTION__, node, retval);*/
493 	return retval;
494 }
495 
496 static GSList *load_xml_spec (GdaServerOperation *op, xmlNodePtr specnode, const gchar *root, GError **error);
497 
498 /* add a new item to @node and inserts it into @op's private structures */
499 static void
500 sequence_add_item (GdaServerOperation *op, Node *node)
501 {
502 	gchar *path, *seq_path;
503 	Node *new_node;
504 	GSList *seq_item_nodes, *list;
505 
506 	g_assert (node);
507 	g_assert (node->type == GDA_SERVER_OPERATION_NODE_SEQUENCE);
508 
509 	seq_path = node_get_complete_path (op, node);
510 	path = g_strdup_printf ("%s/%d", seq_path, g_slist_length (node->d.seq.seq_items));
511 
512 	seq_item_nodes = load_xml_spec (op, node->d.seq.xml_spec, path, NULL);
513 	g_assert (seq_item_nodes);
514 
515 	new_node = node_new (node, GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM, NULL);
516 	op->priv->allnodes = g_slist_append (op->priv->allnodes, new_node);
517 	new_node->d.seq_item_nodes = NULL;
518 	new_node->status = node->status;
519 	node->d.seq.seq_items = g_slist_append (node->d.seq.seq_items, new_node);
520 
521 	new_node->d.seq_item_nodes = seq_item_nodes;
522 	for (list = seq_item_nodes; list; list = list->next)
523 		((Node*) list->data)->parent = new_node;
524 
525 	clean_nodes_info_cache (op);
526 #ifdef GDA_DEBUG_signal
527 	g_print (">> 'SEQUENCE_ITEM_ADDED' from %s\n", __FUNCTION__);
528 #endif
529 	g_signal_emit (G_OBJECT (op), gda_server_operation_signals [SEQUENCE_ITEM_ADDED], 0,
530 		       seq_path, g_slist_length (node->d.seq.seq_items) - 1);
531 #ifdef GDA_DEBUG_signal
532 	g_print ("<< 'SEQUENCE_ITEM_ADDED' from %s\n", __FUNCTION__);
533 #endif
534 
535 	g_free (seq_path);
536 	g_free (path);
537 }
538 
539 static void xml_validity_error_func (void *ctx, const char *msg, ...);
540 static gboolean use_xml_spec (GdaServerOperation *op, xmlDocPtr doc, const gchar *xmlfile);
541 
542 static void
543 gda_server_operation_set_property (GObject *object,
544 				   guint param_id,
545 				   const GValue *value,
546 				   GParamSpec *pspec)
547 {
548 	GdaServerOperation *op;
549 
550 	op = GDA_SERVER_OPERATION (object);
551 	if (op->priv) {
552 		switch (param_id) {
553 		case PROP_CNC:
554 			if (op->priv->cnc)
555 				g_object_unref (op->priv->cnc);
556 
557 			op->priv->cnc = GDA_CONNECTION (g_value_get_object (value));
558 			op->priv->cnc_set = TRUE;
559 
560 			if (op->priv->cnc) {
561 				g_object_ref (op->priv->cnc);
562 
563 				if (gda_connection_get_provider (op->priv->cnc)) {
564 					if (op->priv->prov)
565 						g_object_unref (op->priv->prov);
566 					op->priv->prov = gda_connection_get_provider (op->priv->cnc);
567 					g_object_ref (op->priv->prov);
568 					op->priv->prov_set = TRUE;
569 				}
570 			}
571 			break;
572 		case PROP_PROV:
573 			if (g_value_get_object (value)) {
574 				if (op->priv->prov)
575 					g_object_unref (op->priv->prov);
576 				op->priv->prov = g_value_get_object(value);
577 				g_object_ref (op->priv->prov);
578 			}
579 			op->priv->prov_set = TRUE;
580 			break;
581 		case PROP_OP_TYPE:
582 			op->priv->op_type = g_value_get_int (value);
583 			break;
584 		case PROP_SPEC_FILE: {
585 			xmlDocPtr doc;
586 			const gchar *xmlfile;
587 			static GHashTable *doc_hash = NULL; /* key = file name, value = xmlDocPtr */
588 
589 			xmlfile = g_value_get_string (value);
590 			if (!xmlfile)
591 				return;
592 
593 			if (!doc_hash)
594 				doc_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
595 								  g_free, (GDestroyNotify) xmlFreeDoc);
596 			else {
597 				doc = g_hash_table_lookup (doc_hash, xmlfile);
598 				if (doc) {
599 					op->priv->xml_spec_doc = doc;
600 					break;
601 				}
602 			}
603 
604 			if (! g_file_test (xmlfile, G_FILE_TEST_EXISTS)) {
605 				g_warning (_("GdaServerOperation: could not find file '%s'"), xmlfile);
606 				return;
607 			}
608 
609 			doc = xmlParseFile (xmlfile);
610 			if (doc) {
611 				if (!use_xml_spec (op, doc, xmlfile))
612 					return;
613 				g_hash_table_insert (doc_hash, g_strdup (xmlfile), doc);
614 			}
615 			else {
616 				g_warning (_("GdaServerOperation: could not load file '%s'"), xmlfile);
617 				return;
618 			}
619 			break;
620 		}
621 		default:
622 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
623 			break;
624 		}
625 	}
626 
627 	if (!op->priv->topnodes && op->priv->xml_spec_doc && op->priv->cnc_set && op->priv->prov_set) {
628 		/* load XML file */
629 		GError *lerror = NULL;
630 		op->priv->topnodes = load_xml_spec (op, xmlDocGetRootElement (op->priv->xml_spec_doc), NULL, &lerror);
631 		if (!op->priv->topnodes) {
632 			g_warning (_("Could not load XML specifications: %s"),
633 				   lerror && lerror->message ? lerror->message : _("No detail"));
634 			if (lerror)
635 				g_error_free (lerror);
636 		}
637 	}
638 }
639 
640 static void
641 gda_server_operation_get_property (GObject *object,
642 				   guint param_id,
643 				   GValue *value,
644 				   GParamSpec *pspec)
645 {
646 	GdaServerOperation *op;
647 
648 	op = GDA_SERVER_OPERATION (object);
649 	if (op->priv) {
650 		switch (param_id) {
651 		case PROP_CNC:
652 			g_value_set_object (value, op->priv->cnc);
653 			break;
654 		case PROP_PROV:
655 			g_value_set_object (value, op->priv->prov);
656 			break;
657 		case PROP_OP_TYPE:
658 			g_value_set_int (value, op->priv->op_type);
659 			break;
660 		default:
661 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
662 			break;
663 		}
664 	}
665 }
666 
667 /*
668  * Steals @doc (it is freed if necessary)
669  */
670 static gboolean
671 use_xml_spec (GdaServerOperation *op, xmlDocPtr doc, const gchar *xmlfile)
672 {
673 	/* doc validation */
674 	xmlValidCtxtPtr validc;
675 	int xmlcheck;
676 	xmlDtdPtr old_dtd = NULL;
677 
678 	validc = g_new0 (xmlValidCtxt, 1);
679 	validc->userData = op;
680 	validc->error = xml_validity_error_func;
681 	validc->warning = NULL;
682 
683 	xmlcheck = xmlDoValidityCheckingDefaultValue;
684 	xmlDoValidityCheckingDefaultValue = 1;
685 
686 	/* replace the DTD with ours */
687 	if (_gda_server_op_dtd) {
688 		old_dtd = doc->intSubset;
689 		doc->intSubset = _gda_server_op_dtd;
690 	}
691 #ifndef G_OS_WIN32
692 	if (doc->intSubset && !xmlValidateDocument (validc, doc)) {
693 		gchar *str;
694 
695 		if (_gda_server_op_dtd)
696 			doc->intSubset = old_dtd;
697 		xmlFreeDoc (doc);
698 		g_free (validc);
699 		str = g_object_get_data (G_OBJECT (op), "xmlerror");
700 		if (str) {
701 			if (xmlfile)
702 				g_warning (_("GdaServerOperation: file '%s' does not conform to DTD:\n%s"),
703 					   xmlfile, str);
704 			else
705 				g_warning (_("GdaServerOperation specification does not conform to DTD:\n%s"),
706 					   str);
707 			g_free (str);
708 			g_object_set_data (G_OBJECT (op), "xmlerror", NULL);
709 		}
710 		else {
711 			if (xmlfile)
712 				g_warning (_("GdaServerOperation: file '%s' does not conform to DTD"),
713 					   xmlfile);
714 			else
715 				g_warning (_("GdaServerOperation specification does not conform to DTD\n"));
716 		}
717 
718 		xmlDoValidityCheckingDefaultValue = xmlcheck;
719 		xmlFreeDoc (doc);
720 		return FALSE;
721 	}
722 #endif
723 
724 	xmlDoValidityCheckingDefaultValue = xmlcheck;
725 	g_free (validc);
726 	if (_gda_server_op_dtd)
727 		doc->intSubset = old_dtd;
728 	op->priv->xml_spec_doc = doc;
729 
730 	return TRUE;
731 }
732 
733 
734 /*
735  * function called when an error occurred during the document validation
736  */
737 static void
738 xml_validity_error_func (void *ctx, const char *msg, ...)
739 {
740         va_list args;
741         gchar *str, *str2, *newerr;
742         GdaServerOperation *op;
743 
744         op = GDA_SERVER_OPERATION (ctx);
745         str2 = g_object_get_data (G_OBJECT (op), "xmlerror");
746 
747         va_start (args, msg);
748         str = g_strdup_vprintf (msg, args);
749         va_end (args);
750 
751         if (str2) {
752                 newerr = g_strdup_printf ("%s\n%s", str2, str);
753                 g_free (str2);
754         }
755         else
756                 newerr = g_strdup (str);
757         g_free (str);
758         g_object_set_data (G_OBJECT (op), "xmlerror", newerr);
759 }
760 
761 /*
762  * Warning: the new nodes' parent is not set!
763  */
764 static GSList *
765 load_xml_spec (GdaServerOperation *op, xmlNodePtr specnode, const gchar *root, GError **error)
766 {
767 	xmlNodePtr node;
768 	const gchar *lang = gda_lang_locale;
769 	GSList *retlist = NULL;
770 	Node *parent = NULL;
771 
772 	if (root)
773 		parent = node_find (op, root);
774 
775 	g_assert (specnode);
776 
777 	/* Parameters' sources, not mandatory: makes the op->priv->sources list */
778 	if (!op->priv->sources) {
779 		GSList *sources = NULL;
780 
781 		node = specnode->children;
782 		while (node && (xmlNodeIsText (node) || strcmp ((gchar*)node->name, "sources")))
783 		node = node->next;
784 		if (node && !strcmp ((gchar*)node->name, "sources")) {
785 			for (node = node->xmlChildrenNode; (node != NULL); node = node->next) {
786 				if (xmlNodeIsText (node))
787 					continue;
788 
789 				if (!strcmp ((gchar*)node->name, "gda_array")) {
790 					GdaDataModel *model;
791 					GSList *errors;
792 
793 					model = gda_data_model_import_new_xml_node (node);
794 					errors = gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (model));
795 					if (errors) {
796 						g_object_unref (model);
797 						if (sources) {
798 							g_slist_foreach (sources, (GFunc) g_object_unref, NULL);
799 							g_slist_free (sources);
800 							return NULL;
801 						}
802 					}
803 					else  {
804 						xmlChar *str;
805 						sources = g_slist_prepend (sources, model);
806 						str = xmlGetProp (node, (xmlChar*)"name");
807 						if (str)
808 							g_object_set_data_full (G_OBJECT (model), "name", (gchar*)str, xmlFree);
809 					}
810 				}
811 			}
812 		}
813 		op->priv->sources = sources;
814 	}
815 
816 	/* actual objects loading */
817 	node = specnode->children;
818 	while (node) {
819 		xmlChar *id, *this_lang;
820 		gchar *complete_path = NULL, *path_name = NULL;
821 		Node *opnode = NULL;
822 		Node *old_opnode;
823 
824 		if (xmlNodeIsText (node)) {
825 			xmlNodePtr nextnode;
826 			nextnode = node->next;
827 			xmlUnlinkNode (node);
828 			xmlFreeNode (node);
829 			node = nextnode;
830 			continue;
831 		}
832 
833 		/* don't care about entries for the wrong locale */
834 		this_lang = xmlGetProp(node, (xmlChar*)"lang");
835 		if (this_lang) {
836 			if (strncmp ((gchar*)this_lang, lang, strlen ((gchar*)this_lang))) {
837 				xmlNodePtr nextnode;
838 				xmlFree (this_lang);
839 				nextnode = node->next;
840 				xmlUnlinkNode (node);
841 				xmlFreeNode (node);
842 				node = nextnode;
843 				continue;
844 			}
845 
846 			xmlFree (this_lang);
847 		}
848 
849 		id = xmlGetProp (node, BAD_CAST "id");
850 		if (!id) {
851 			node = node->next;
852 			continue;
853 		}
854 
855 		complete_path = g_strdup_printf ("%s/%s", root ? root : "", id);
856 		path_name = g_strdup ((gchar*)id);
857 		xmlFree (id);
858 
859 		old_opnode = node_find (op, complete_path);
860 		if (old_opnode) {
861 			node_destroy (op, old_opnode);
862 			retlist = g_slist_remove (retlist, old_opnode);
863 		}
864 
865 		/* GDA_SERVER_OPERATION_NODE_PARAMLIST */
866 		if (!strcmp ((gchar*)node->name, "parameters")) {
867 			GdaSet *plist;
868 
869 			plist = gda_set_new_from_spec_node (node, error);
870 			if (plist) {
871 				opnode = node_new (parent, GDA_SERVER_OPERATION_NODE_PARAMLIST, path_name);
872 				opnode->d.plist = plist;
873 			}
874 		}
875 		/* GDA_SERVER_OPERATION_NODE_DATA_MODEL */
876 		else if (!strcmp ((gchar*)node->name, "gda_array")) {
877 			GdaDataModel *import;
878 
879 			import = gda_data_model_import_new_xml_node (node);
880 			if (import) {
881 				GdaDataModel *model;
882 				model = (GdaDataModel*) gda_data_model_array_copy_model (import, NULL);
883 				opnode = node_new (parent, GDA_SERVER_OPERATION_NODE_DATA_MODEL, path_name);
884 				opnode->d.model = model;
885 				g_object_unref (import);
886 			}
887 		}
888 
889 		/* GDA_SERVER_OPERATION_NODE_SEQUENCE */
890 		else if (!strcmp ((gchar*)node->name, "sequence")) {
891 			GSList *seq_tmpl = NULL;
892 			xmlChar *prop;
893 
894 			opnode = node_new (parent, GDA_SERVER_OPERATION_NODE_SEQUENCE, path_name);
895 			opnode->d.seq.seq_tmpl = NULL;
896 			opnode->d.seq.min_items = 0;
897 			opnode->d.seq.max_items = G_MAXUINT;
898 			opnode->d.seq.seq_items = NULL;
899 			opnode->d.seq.xml_spec = node;
900 
901 			prop = xmlGetProp(node, (xmlChar*)"name");
902 			if (prop) {
903 				opnode->d.seq.name = g_strdup ((gchar*)prop);
904 				xmlFree (prop);
905 			}
906 
907 			prop = xmlGetProp(node, (xmlChar*)"descr");
908 			if (prop) {
909 				opnode->d.seq.descr = g_strdup ((gchar*)prop);
910 				xmlFree (prop);
911 			}
912 
913 
914 			prop = xmlGetProp(node, (xmlChar*)"minitems");
915 			if (prop) {
916 				gint tmp;
917 				tmp = atoi ((gchar*)prop); /* Flawfinder: ignore */
918 				if (tmp < 0)
919 					opnode->d.seq.min_items = 0;
920 				else
921 					opnode->d.seq.min_items = tmp;
922 				xmlFree (prop);
923 			}
924 
925 			prop = xmlGetProp(node, (xmlChar*)"maxitems");
926 			if (prop) {
927 				opnode->d.seq.max_items = atoi ((gchar*)prop); /* Flawfinder: ignore */
928 				if (opnode->d.seq.max_items < opnode->d.seq.min_items)
929 					opnode->d.seq.max_items = opnode->d.seq.min_items;
930 				xmlFree (prop);
931 			}
932 
933 			seq_tmpl = load_xml_spec (op, node, complete_path, error);
934 			if (! seq_tmpl) {
935 				node_destroy (NULL, opnode);
936 				opnode = NULL;
937 			}
938 			else
939 				opnode->d.seq.seq_tmpl = seq_tmpl;
940 		}
941 
942 		/* GDA_SERVER_OPERATION_NODE_PARAM */
943 		else if (!strcmp ((gchar*)node->name, "parameter")) {
944 			GdaHolder *param = NULL;
945 			xmlChar *gdatype;
946 			GType gt;
947 
948 			/* find data type and create GdaHolder */
949 			gdatype = xmlGetProp (node, BAD_CAST "gdatype");
950 			gt = gdatype ? gda_g_type_from_string ((gchar*) gdatype) : G_TYPE_STRING;
951 			if (gt == G_TYPE_INVALID)
952 				gt = GDA_TYPE_NULL;
953 			param = GDA_HOLDER (g_object_new (GDA_TYPE_HOLDER,
954 							  "g-type", gt,
955 							  NULL));
956 			if (gdatype)
957 				xmlFree (gdatype);
958 
959 			/* set parameter's attributes */
960 			if (gda_utility_holder_load_attributes (param, node, op->priv->sources, error)) {
961 				opnode = node_new (parent, GDA_SERVER_OPERATION_NODE_PARAM, path_name);
962 				opnode->d.param = param;
963 			}
964 		}
965 		else {
966 			node = node->next;
967 			g_free (path_name);
968 			continue;
969 		}
970 
971 		/* really insert the new Node, and set its status */
972 		if (opnode) {
973 			xmlChar *status;
974 
975 			/* insert */
976 			op->priv->allnodes = g_slist_append (op->priv->allnodes, opnode);
977 			retlist = g_slist_append (retlist, opnode);
978 			/*g_print ("+ %s (node's path = %s) %p\n", complete_path, opnode->path_name, opnode);*/
979 
980 			/* status */
981 			status = xmlGetProp (node, BAD_CAST "status");
982 			if (status) {
983 				if (!strcmp ((gchar*)status, "OPT"))
984 					opnode->status = GDA_SERVER_OPERATION_STATUS_OPTIONAL;
985 				xmlFree (status);
986 			}
987 
988 			if (opnode->type == GDA_SERVER_OPERATION_NODE_SEQUENCE) {
989 				/* add sequence items if necessary */
990 				if (opnode->d.seq.min_items > 0) {
991 					guint i;
992 
993 					for (i = 0; i < opnode->d.seq.min_items; i++)
994 						gda_server_operation_add_item_to_sequence (op, complete_path);
995 				}
996 			}
997 		}
998 		else {
999 			g_free (path_name);
1000 			g_free (complete_path);
1001 			g_slist_free (retlist);
1002 			return NULL;
1003 		}
1004 		g_free (path_name);
1005 		g_free (complete_path);
1006 		node = node->next;
1007 	}
1008 
1009 	return retlist;
1010 }
1011 
1012 /*
1013  * @xml_spec: the specifications for the GdaServerOperation object to create as a string
1014  * Internal function
1015  */
1016 GdaServerOperation *
1017 _gda_server_operation_new_from_string (GdaServerOperationType op_type,
1018 				       const gchar *xml_spec)
1019 {
1020 	GObject *obj;
1021 	xmlDocPtr doc;
1022 	GdaServerOperation *op;
1023 
1024 	doc = xmlParseMemory (xml_spec, strlen (xml_spec) + 1);
1025 	if (!doc)
1026 		return NULL;
1027 	obj = g_object_new (GDA_TYPE_SERVER_OPERATION, "op-type", op_type, NULL);
1028 	op = GDA_SERVER_OPERATION (obj);
1029 	use_xml_spec (op, doc, NULL);
1030 
1031 	if (!op->priv->topnodes && op->priv->xml_spec_doc && op->priv->cnc_set && op->priv->prov_set) {
1032 		/* load XML file */
1033 		GError *lerror = NULL;
1034 		op->priv->topnodes = load_xml_spec (op, xmlDocGetRootElement (op->priv->xml_spec_doc), NULL, &lerror);
1035 		if (!op->priv->topnodes) {
1036 			g_warning (_("Could not load XML specifications: %s"),
1037 				   lerror && lerror->message ? lerror->message : _("No detail"));
1038 			if (lerror)
1039 				g_error_free (lerror);
1040 		}
1041 	}
1042 
1043 	return op;
1044 }
1045 
1046 
1047 /**
1048  * gda_server_operation_new:
1049  * @op_type: type of operation
1050  * @xml_file: a file which has the specifications for the GdaServerOperation object to create
1051  *
1052  * IMPORTANT NOTE: Using this funtion is not the recommended way of creating a #GdaServerOperation object, the
1053  * correct way is to use gda_server_provider_create_operation(); this method is reserved for the database provider's
1054  * implementation.
1055  *
1056  * Creates a new #GdaServerOperation object from the @xml_file specifications
1057  *
1058  * The @xml_file must respect the DTD described in the "libgda-server-operation.dtd" file: its top
1059  * node must be a &lt;serv_op&gt; tag.
1060  *
1061  * Returns: a new #GdaServerOperation object
1062  */
1063 GdaServerOperation *
1064 gda_server_operation_new (GdaServerOperationType op_type, const gchar *xml_file)
1065 {
1066 	GObject *obj;
1067 
1068 	obj = g_object_new (GDA_TYPE_SERVER_OPERATION, "op-type", op_type,
1069 			    "spec-filename", xml_file, NULL);
1070 #ifdef GDA_DEBUG_NO
1071 	{
1072 		g_print ("New GdaServerOperation:\n");
1073 		xmlNodePtr node;
1074 		node = gda_server_operation_save_data_to_xml ((GdaServerOperation *) obj, NULL);
1075 		xmlDocPtr doc;
1076 		doc = xmlNewDoc ("1.0");
1077 		xmlDocSetRootElement (doc, node);
1078 		xmlDocDump (stdout, doc);
1079 		xmlFreeDoc (doc);
1080 	}
1081 #endif
1082 	return (GdaServerOperation *) obj;
1083 }
1084 
1085 /**
1086  * gda_server_operation_get_node_info:
1087  * @op: a #GdaServerOperation object
1088  * @path_format: a complete path to a node (starting with "/") as a format string, similar to g_strdup_printf()'s argument
1089  * @...: the arguments to insert into the format string
1090  *
1091  * Get information about the node identified by @path. The returned #GdaServerOperationNode structure can be
1092  * copied but not modified; it may change or cease to exist if @op changes
1093  *
1094  * Returns: (transfer none) (nullable): a #GdaServerOperationNode structure, or %NULL if the node was not found
1095  */
1096 GdaServerOperationNode *
1097 gda_server_operation_get_node_info (GdaServerOperation *op, const gchar *path_format, ...)
1098 {
1099 	GdaServerOperationNode *info_node;
1100 	Node *node;
1101 	gchar *path;
1102 	va_list args;
1103 
1104 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1105 	g_return_val_if_fail (op->priv, NULL);
1106 
1107 	/* build path */
1108 	va_start (args, path_format);
1109 	path = g_strdup_vprintf (path_format, args);
1110 	va_end (args);
1111 
1112 	/* use path */
1113 	info_node = g_hash_table_lookup (op->priv->info_hash, path);
1114 	if (info_node) {
1115 		g_free (path);
1116 		return info_node;
1117 	}
1118 
1119 	/* compute a new GdaServerOperationNode */
1120 	node = node_find (op, path);
1121 	if (node) {
1122 		info_node = g_new0 (GdaServerOperationNode, 1);
1123 		info_node->priv = node;
1124 		info_node->type = node->type;
1125 		info_node->status = node->status;
1126 		switch (node->type) {
1127 		case GDA_SERVER_OPERATION_NODE_PARAMLIST:
1128 			info_node->plist = node->d.plist;
1129 			break;
1130 		case GDA_SERVER_OPERATION_NODE_DATA_MODEL:
1131 			info_node->model = node->d.model;
1132 			break;
1133 		case GDA_SERVER_OPERATION_NODE_PARAM:
1134 			info_node->param = node->d.param;
1135 			break;
1136 		default:
1137 			break;
1138 		}
1139 	}
1140 	else {
1141 		/* try to see if the "parent" is a real node */
1142 		gchar *str;
1143 		gchar *extension;
1144 
1145 		str = gda_server_operation_get_node_parent (op, path);
1146 		if (str) {
1147 			node = node_find (op, str);
1148 			if (node && (node->type != GDA_SERVER_OPERATION_NODE_PARAMLIST) &&
1149 			    (node->type != GDA_SERVER_OPERATION_NODE_DATA_MODEL))
1150 				node = NULL; /* ignore node */
1151 			g_free (str);
1152 		}
1153 		if (node && (node->type == GDA_SERVER_OPERATION_NODE_PARAMLIST)) {
1154 			GdaHolder *param;
1155 			extension = gda_server_operation_get_node_path_portion (op, path);
1156 			param = gda_set_get_holder (node->d.plist, extension);
1157 			g_free (extension);
1158 
1159 			if (param) {
1160 				info_node = g_new0 (GdaServerOperationNode, 1);
1161 				info_node->type = GDA_SERVER_OPERATION_NODE_PARAM;
1162 				info_node->status = node->status;
1163 				info_node->param = param;
1164 			}
1165 		}
1166 		if (node && (node->type == GDA_SERVER_OPERATION_NODE_DATA_MODEL)) {
1167 			GdaColumn *column = NULL;
1168 
1169 			extension = gda_server_operation_get_node_path_portion (op, path);
1170 			if (extension && (*extension == '@')) {
1171 				gint i, nbcols;
1172 				GdaDataModel *model;
1173 
1174 				model = node->d.model;
1175 				nbcols = gda_data_model_get_n_columns (model);
1176 				for (i = 0; (i<nbcols) && !column; i++) {
1177 					gchar *colid = NULL;
1178 					column = gda_data_model_describe_column (model, i);
1179 					g_object_get (G_OBJECT (column), "id", &colid, NULL);
1180 					if (!colid || strcmp (colid, extension +1))
1181 						column = NULL;
1182 					g_free(colid);
1183 				}
1184 			}
1185 			g_free (extension);
1186 			if (column) {
1187 				info_node = g_new0 (GdaServerOperationNode, 1);
1188 				info_node->type = GDA_SERVER_OPERATION_NODE_DATA_MODEL_COLUMN;
1189 				info_node->status = node->status;
1190 				info_node->column = column;
1191 				info_node->model = node->d.model;
1192 			}
1193 		}
1194 	}
1195 
1196 	if (info_node)
1197 		g_hash_table_insert (op->priv->info_hash, g_strdup (path), info_node);
1198 
1199 	g_free (path);
1200 	return info_node;
1201 }
1202 
1203 /**
1204  * gda_server_operation_get_op_type:
1205  * @op: a #GdaServerOperation object
1206  *
1207  * Get the type of operation @op is for
1208  *
1209  * Returns: a #GdaServerOperationType enum
1210  */
1211 GdaServerOperationType
1212 gda_server_operation_get_op_type (GdaServerOperation *op)
1213 {
1214 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), 0);
1215 	g_return_val_if_fail (op->priv, 0);
1216 
1217 	return op->priv->op_type;
1218 }
1219 
1220 /**
1221  * gda_server_operation_op_type_to_string:
1222  * @type: a #GdaServerOperationType value
1223  *
1224  * Get a string version of @type
1225  *
1226  * Returns: (transfer none): a non %NULL string (do not free or modify)
1227  */
1228 const gchar *
1229 gda_server_operation_op_type_to_string (GdaServerOperationType type)
1230 {
1231 	switch (type) {
1232 	case GDA_SERVER_OPERATION_CREATE_DB:
1233 		return "CREATE_DB";
1234 	case GDA_SERVER_OPERATION_DROP_DB:
1235 		return "DROP_DB";
1236 	case GDA_SERVER_OPERATION_CREATE_TABLE:
1237 		return "CREATE_TABLE";
1238 	case GDA_SERVER_OPERATION_DROP_TABLE:
1239 		return "DROP_TABLE";
1240         case GDA_SERVER_OPERATION_CREATE_INDEX:
1241 		return "CREATE_INDEX";
1242         case GDA_SERVER_OPERATION_DROP_INDEX:
1243 		return "DROP_INDEX";
1244         case GDA_SERVER_OPERATION_RENAME_TABLE:
1245 		return "RENAME_TABLE";
1246         case GDA_SERVER_OPERATION_COMMENT_TABLE:
1247 		return "COMMENT_TABLE";
1248         case GDA_SERVER_OPERATION_ADD_COLUMN:
1249 		return "ADD_COLUMN";
1250         case GDA_SERVER_OPERATION_DROP_COLUMN:
1251 		return "DROP_COLUMN";
1252         case GDA_SERVER_OPERATION_COMMENT_COLUMN:
1253 		return "COMMENT_COLUMN";
1254 	case GDA_SERVER_OPERATION_CREATE_VIEW:
1255 		return "CREATE_VIEW";
1256 	case GDA_SERVER_OPERATION_DROP_VIEW:
1257 		return "DROP_VIEW";
1258 	case GDA_SERVER_OPERATION_CREATE_USER:
1259 		return "CREATE_USER";
1260 	case GDA_SERVER_OPERATION_DROP_USER:
1261 		return "DROP_USER";
1262 	case GDA_SERVER_OPERATION_ALTER_USER:
1263 		return "ALTER_USER";
1264 	default:
1265 		g_error (_("Non handled GdaServerOperationType, please report error to "
1266 			   "http://bugzilla.gnome.org/ for the \"libgda\" product"));
1267 		return "";
1268 	}
1269 }
1270 
1271 /**
1272  * gda_server_operation_string_to_op_type:
1273  * @str: a string
1274  *
1275  * Performs the reverse of gda_server_operation_op_type_to_string()
1276  *
1277  * Returns: the #GdaServerOperationType represented by @str, or #G_MAXINT if @str is not a valid representation
1278  * of a #GdaServerOperationType
1279  *
1280  * Since: 4.2
1281  */
1282 GdaServerOperationType
1283 gda_server_operation_string_to_op_type (const gchar *str)
1284 {
1285 	GdaServerOperationType operation_type = G_MAXINT;
1286 	g_return_val_if_fail (str && *str, G_MAXINT);
1287 
1288 	if (! g_ascii_strcasecmp (str, "CREATE_DB"))
1289 		operation_type = GDA_SERVER_OPERATION_CREATE_DB;
1290 	else if	(! g_ascii_strcasecmp (str, "DROP_DB"))
1291 		operation_type = GDA_SERVER_OPERATION_DROP_DB;
1292 	else if (! g_ascii_strcasecmp (str, "CREATE_TABLE"))
1293 		operation_type = GDA_SERVER_OPERATION_CREATE_TABLE;
1294 	else if (! g_ascii_strcasecmp (str, "DROP_TABLE"))
1295 		operation_type = GDA_SERVER_OPERATION_DROP_TABLE;
1296 	else if (! g_ascii_strcasecmp (str, "CREATE_INDEX"))
1297 		operation_type = GDA_SERVER_OPERATION_CREATE_INDEX;
1298 	else if (! g_ascii_strcasecmp (str, "DROP_INDEX"))
1299 		operation_type = GDA_SERVER_OPERATION_DROP_INDEX;
1300 	else if (! g_ascii_strcasecmp (str, "RENAME_TABLE"))
1301 		operation_type = GDA_SERVER_OPERATION_RENAME_TABLE;
1302 	else if (! g_ascii_strcasecmp (str, "COMMENT_TABLE"))
1303 		operation_type = GDA_SERVER_OPERATION_COMMENT_TABLE;
1304 	else if (! g_ascii_strcasecmp (str, "ADD_COLUMN"))
1305 		operation_type = GDA_SERVER_OPERATION_ADD_COLUMN;
1306 	else if (! g_ascii_strcasecmp (str, "DROP_COLUMN"))
1307 		operation_type = GDA_SERVER_OPERATION_DROP_COLUMN;
1308 	else if (! g_ascii_strcasecmp (str, "COMMENT_COLUMN"))
1309 		operation_type = GDA_SERVER_OPERATION_COMMENT_COLUMN;
1310 	else if (! g_ascii_strcasecmp (str, "CREATE_VIEW"))
1311 		operation_type = GDA_SERVER_OPERATION_CREATE_VIEW;
1312 	else if (! g_ascii_strcasecmp (str, "DROP_VIEW"))
1313 		operation_type = GDA_SERVER_OPERATION_DROP_VIEW;
1314 	else if (! g_ascii_strcasecmp (str, "CREATE_USER"))
1315 		operation_type = GDA_SERVER_OPERATION_CREATE_USER;
1316 	else if (! g_ascii_strcasecmp (str, "DROP_USER"))
1317 		operation_type = GDA_SERVER_OPERATION_DROP_USER;
1318 	else if (! g_ascii_strcasecmp (str, "ALTER_USER"))
1319 		operation_type = GDA_SERVER_OPERATION_ALTER_USER;
1320 
1321 	return operation_type;
1322 }
1323 
1324 static gboolean node_save (GdaServerOperation *op, Node *opnode, xmlNodePtr parent);
1325 
1326 /**
1327  * gda_server_operation_save_data_to_xml: (skip)
1328  * @op: a #GdaServerOperation object
1329  * @error: (nullable): a place to store errors or %NULL
1330  *
1331  * Creates a new #xmlNodePtr tree which can be used to save the #op object. This
1332  * XML structure can then be saved to disk if necessary. Use xmlFreeNode to free
1333  * the associated memory when not needed anymore.
1334  *
1335  * Returns: (transfer full): a new #xmlNodePtr structure, or %NULL
1336  */
1337 xmlNodePtr
1338 gda_server_operation_save_data_to_xml (GdaServerOperation *op, G_GNUC_UNUSED GError **error)
1339 {
1340 	xmlNodePtr topnode = NULL;
1341 	GSList *list;
1342 
1343 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1344 	g_return_val_if_fail (op->priv, NULL);
1345 
1346 	topnode = xmlNewNode (NULL, BAD_CAST "serv_op_data");
1347 	xmlSetProp (topnode, BAD_CAST "type",
1348 		    BAD_CAST gda_server_operation_op_type_to_string (gda_server_operation_get_op_type (op)));
1349 
1350 	for (list = op->priv->topnodes; list; list = list->next) {
1351 		if (!node_save (op, NODE (list->data), topnode)) {
1352 			xmlFreeNode (topnode);
1353 			topnode = NULL;
1354 			break;
1355 		}
1356 	}
1357 
1358 	return topnode;
1359 }
1360 
1361 static gboolean
1362 node_save (GdaServerOperation *op, Node *opnode, xmlNodePtr parent)
1363 {
1364 	gboolean retval = TRUE;
1365 	xmlNodePtr node;
1366 	GSList *list;
1367 	gchar *complete_path;
1368 	g_assert (opnode);
1369 
1370 	complete_path = node_get_complete_path (op, opnode);
1371 	switch (opnode->type) {
1372 	case GDA_SERVER_OPERATION_NODE_PARAMLIST:
1373 		for (list = opnode->d.plist->holders; list; list = list->next) {
1374 			gchar *path;
1375 			const GValue *value;
1376 			gchar *str;
1377 
1378 			value = gda_holder_get_value (GDA_HOLDER (list->data));
1379 			if (!value || gda_value_is_null ((GValue *) value))
1380 				str = NULL;
1381 			else {
1382 				if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
1383 					str = g_strdup (g_value_get_boolean (value) ? "TRUE" : "FALSE");
1384 				else
1385 					str = gda_value_stringify (value);
1386 			}
1387 			node = xmlNewTextChild (parent, NULL, BAD_CAST "op_data", (xmlChar*)str);
1388 			g_free (str);
1389 
1390 			path = g_strdup_printf ("%s/%s", complete_path, gda_holder_get_id (GDA_HOLDER (list->data)));
1391 			xmlSetProp(node, (xmlChar*)"path", (xmlChar*)path);
1392 			g_free (path);
1393 		}
1394 		break;
1395 	case GDA_SERVER_OPERATION_NODE_DATA_MODEL:
1396 		node = xmlNewChild (parent, NULL, BAD_CAST "op_data", NULL);
1397 		xmlSetProp(node, (xmlChar*)"path", (xmlChar*)complete_path);
1398 		if (!gda_utility_data_model_dump_data_to_xml (opnode->d.model, node, NULL, 0, NULL, 0, TRUE))
1399 			retval = FALSE;
1400 		break;
1401 	case GDA_SERVER_OPERATION_NODE_PARAM: {
1402 		const GValue *value;
1403 		gchar *str;
1404 
1405 		value = gda_holder_get_value (opnode->d.param);
1406 		if (!value || gda_value_is_null ((GValue *) value))
1407 			str = NULL;
1408 		else {
1409 			if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
1410 				str = g_strdup (g_value_get_boolean (value) ? "TRUE" : "FALSE");
1411 			else
1412 				str = gda_value_stringify (value);
1413 		}
1414 		node = xmlNewTextChild (parent, NULL, BAD_CAST "op_data", (xmlChar*)str);
1415 		g_free (str);
1416 		xmlSetProp(node, (xmlChar*)"path", (xmlChar*)complete_path);
1417 		break;
1418 	}
1419 	case GDA_SERVER_OPERATION_NODE_SEQUENCE: {
1420 		GSList *list;
1421 
1422 		for (list =  opnode->d.seq.seq_items; list; list = list->next)
1423 			if (!node_save (op, NODE (list->data), parent)) {
1424 				retval = FALSE;
1425 				break;
1426 			}
1427 		break;
1428 	}
1429 	case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM: {
1430 		GSList *list;
1431 
1432 		for (list =  opnode->d.seq_item_nodes; list; list = list->next)
1433 			if (! node_save (op, NODE (list->data), parent)) {
1434 				retval = FALSE;
1435 				break;
1436 			}
1437 		break;
1438 	}
1439 	default:
1440 		g_assert_not_reached ();
1441 	}
1442 
1443 	g_free (complete_path);
1444 	return retval;
1445 }
1446 
1447 /**
1448  * gda_server_operation_load_data_from_xml:
1449  * @op: a #GdaServerOperation object
1450  * @node: a #xmlNodePtr
1451  * @error: (nullable): a place to store errors or %NULL
1452  *
1453  * Loads the contents of @node into @op. The XML tree passed through the @node
1454  * argument must correspond to an XML tree saved using gda_server_operation_save_data_to_xml().
1455  *
1456  * Returns: %TRUE if no error occurred
1457  */
1458 gboolean
1459 gda_server_operation_load_data_from_xml (GdaServerOperation *op, xmlNodePtr node, GError **error)
1460 {
1461 	xmlNodePtr cur;
1462 
1463 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
1464 	g_return_val_if_fail (op->priv, FALSE);
1465 	if (!node)
1466 		return FALSE;
1467 
1468 	/* remove any sequence items */
1469 	GSList *list;
1470 	list = op->priv->allnodes;
1471 	while (list) {
1472 		Node *node = NODE (list->data);
1473 		if ((node->type == GDA_SERVER_OPERATION_NODE_SEQUENCE) && node->d.seq.seq_items) {
1474 			gchar *seq_path;
1475 
1476 			seq_path = node_get_complete_path (op, node);
1477 			while (node->d.seq.seq_items) {
1478 #ifdef GDA_DEBUG_signal
1479 				g_print (">> 'SEQUENCE_ITEM_REMOVE' from %s\n", __FUNCTION__);
1480 #endif
1481 				g_signal_emit (G_OBJECT (op), gda_server_operation_signals [SEQUENCE_ITEM_REMOVE], 0,
1482 					       seq_path, 0);
1483 #ifdef GDA_DEBUG_signal
1484 				g_print ("<< 'SEQUENCE_ITEM_REMOVE' from %s\n", __FUNCTION__);
1485 #endif
1486 				node_destroy (op, NODE (node->d.seq.seq_items->data));
1487 				node->d.seq.seq_items = g_slist_delete_link (node->d.seq.seq_items, node->d.seq.seq_items);
1488 			}
1489 			g_free (seq_path);
1490 			list = op->priv->allnodes;
1491 		}
1492 		else
1493 			list = list->next;
1494 	}
1495 
1496 	/* actual data loading */
1497 	if (strcmp ((gchar*)node->name, "serv_op_data")) {
1498 		g_set_error (error, GDA_SERVER_OPERATION_ERROR,
1499 			     GDA_SERVER_OPERATION_XML_ERROR,
1500 			     _("Expected tag <%s>, got <%s>"), "serv_op_data", node->name);
1501 		return FALSE;
1502 	}
1503 
1504 	cur = node->children;
1505 	while (cur) {
1506 		xmlChar *prop;
1507 		if (xmlNodeIsText (cur)) {
1508 			cur = cur->next;
1509 			continue;
1510 		}
1511 
1512 		if (strcmp ((gchar*)cur->name, "op_data")) {
1513 			g_set_error (error, GDA_SERVER_OPERATION_ERROR,
1514 				     GDA_SERVER_OPERATION_XML_ERROR,
1515 				     _("Expected tag <%s>, got <%s>"), "op_data", cur->name);
1516 			return FALSE;
1517 		}
1518 
1519 		prop = xmlGetProp(cur, (xmlChar*)"path");
1520 		if (prop) {
1521 			Node *opnode;
1522 			gchar *extension = NULL;
1523 			gboolean allok = TRUE;
1524 
1525 			opnode = node_find_or_create (op, (gchar*)prop);
1526 			if (!opnode) {
1527 				/* try to see if the "parent" is a real node */
1528 				gchar *str;
1529 
1530 				str = gda_server_operation_get_node_parent (op, (gchar*)prop);
1531 				if (str) {
1532 					opnode = node_find (op, str);
1533 					if (opnode && (opnode->type != GDA_SERVER_OPERATION_NODE_PARAMLIST))
1534 						opnode = NULL; /* ignore opnode */
1535 					g_free (str);
1536 				}
1537 				if (opnode)
1538 					extension = gda_server_operation_get_node_path_portion (op, (gchar*)prop);
1539 			}
1540 
1541 			if (opnode) {
1542 				switch (opnode->type) {
1543 				case GDA_SERVER_OPERATION_NODE_PARAMLIST:
1544 					if (!extension) {
1545 						g_set_error (error,
1546 							     GDA_SERVER_OPERATION_ERROR,
1547 							     GDA_SERVER_OPERATION_XML_ERROR,
1548 							     "%s",
1549 							     _("Parameterlist values can only be set for individual parameters within it"));
1550 						allok = FALSE;
1551 					}
1552 					else {
1553 						xmlNodePtr contents;
1554 
1555 						contents = cur->children;
1556 						if (contents && xmlNodeIsText (contents)) {
1557 							GdaHolder *param;
1558 							param = gda_set_get_holder (opnode->d.plist, extension);
1559 							if (param) {
1560 								GValue *v;
1561 								v = gda_value_new_from_string ((gchar*)contents->content,
1562 											       gda_holder_get_g_type (param));
1563 								if (!gda_holder_take_value (param, v, error))
1564 									allok = FALSE;
1565 							}
1566 						}
1567 					}
1568 					break;
1569 				case GDA_SERVER_OPERATION_NODE_DATA_MODEL:
1570 					gda_data_model_array_clear (GDA_DATA_MODEL_ARRAY (opnode->d.model));
1571 					if (cur->children &&
1572 						 ! gda_data_model_add_data_from_xml_node (opnode->d.model,
1573 											  cur->children, error))
1574 						allok = FALSE;
1575 					break;
1576 				case GDA_SERVER_OPERATION_NODE_PARAM: {
1577 					xmlNodePtr contents;
1578 
1579 					contents = cur->children;
1580 					if (contents && xmlNodeIsText (contents)) {
1581 						GValue *v;
1582 						v = gda_value_new_from_string ((gchar*)contents->content,
1583 									       gda_holder_get_g_type (opnode->d.param));
1584 						if (!gda_holder_take_value (opnode->d.param, v, error))
1585 							allok = FALSE;
1586 					}
1587 					break;
1588 				}
1589 				case GDA_SERVER_OPERATION_NODE_SEQUENCE:
1590 					break;
1591 				case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM:
1592 					break;
1593 				default:
1594 					g_assert_not_reached ();
1595 				}
1596 			}
1597 
1598 			g_free (extension);
1599 			xmlFree (prop);
1600 
1601 			if (!allok)
1602 				return FALSE;
1603 		}
1604 		else {
1605 			g_set_error (error, GDA_SERVER_OPERATION_ERROR,
1606 				     GDA_SERVER_OPERATION_XML_ERROR,
1607 				     "%s", _("Missing attribute named 'path'"));
1608 			return FALSE;
1609 		}
1610 
1611 		cur = cur->next;
1612 	}
1613 
1614 	return TRUE;
1615 }
1616 
1617 /**
1618  * gda_server_operation_get_root_nodes:
1619  * @op: a #GdaServerOperation object
1620  *
1621  * Get an array of strings containing the paths of nodes situated at the root of @op.
1622  *
1623  * Returns: (transfer full): a new array, which must be freed with g_strfreev().
1624  */
1625 gchar**
1626 gda_server_operation_get_root_nodes (GdaServerOperation *op)
1627 {
1628 	gchar **retval;
1629 	GSList *list;
1630 	gint i = 0;
1631 
1632 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1633 	g_return_val_if_fail (op->priv, NULL);
1634 
1635 	retval = g_new0 (gchar *, g_slist_length (op->priv->topnodes) + 1);
1636 	for (list = op->priv->topnodes; list; list = list->next)
1637 		retval [i++] = node_get_complete_path (op, NODE (list->data));
1638 
1639 	return retval;
1640 }
1641 
1642 /**
1643  * gda_server_operation_get_node_type:
1644  * @op: a #GdaServerOperation object
1645  * @path: a complete path to a node (starting with "/")
1646  * @status: (nullable): a place to store the status of the node, or %NULL
1647  *
1648  * Convenience function to get the type of a node.
1649  *
1650  * Returns: the type of node, or GDA_SERVER_OPERATION_NODE_UNKNOWN if the node was not found
1651  */
1652 GdaServerOperationNodeType
1653 gda_server_operation_get_node_type (GdaServerOperation *op, const gchar *path,
1654 				    GdaServerOperationNodeStatus *status)
1655 {
1656 	GdaServerOperationNode *node_info;
1657 
1658 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), GDA_SERVER_OPERATION_NODE_UNKNOWN);
1659 	g_return_val_if_fail (op->priv, GDA_SERVER_OPERATION_NODE_UNKNOWN);
1660 
1661 	node_info = gda_server_operation_get_node_info (op, path);
1662 	if (node_info) {
1663 		if (status)
1664 			*status = node_info->status;
1665 		return node_info->type;
1666 	}
1667 	return GDA_SERVER_OPERATION_NODE_UNKNOWN;
1668 }
1669 
1670 /**
1671  * gda_server_operation_get_node_parent:
1672  * @op: a #GdaServerOperation object
1673  * @path: a complete path to a node (starting with "/")
1674  *
1675  * Get the complete path to the parent of the node defined by @path
1676  *
1677  * Returns: (transfer full): a new string or %NULL if the node does not have any parent or does not exist.
1678  */
1679 gchar *
1680 gda_server_operation_get_node_parent (GdaServerOperation *op, const gchar *path)
1681 {
1682 	Node *node;
1683 
1684 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1685 	g_return_val_if_fail (op->priv, NULL);
1686 	g_return_val_if_fail (path && (*path == '/'), NULL);
1687 
1688 	node = node_find (op, path);
1689 
1690 	if (node) {
1691 		if (! node->parent)
1692 			return NULL;
1693 		else
1694 			return node_get_complete_path (op, node->parent);
1695 	}
1696 	else {
1697 		gchar *path2 = g_strdup (path);
1698 		gchar *ptr;
1699 
1700 		ptr = path2 + strlen (path2) - 1;
1701 		while (*ptr != '/') {
1702 			*ptr = 0;
1703 			ptr --;
1704 		}
1705 		*ptr = 0;
1706 
1707 		return path2;
1708 	}
1709 }
1710 
1711 /**
1712  * gda_server_operation_get_node_path_portion:
1713  * @op: a #GdaServerOperation object
1714  * @path: a complete path to a node (starting with "/")
1715  *
1716  * Get the last part of @path
1717  *
1718  * Returns: (transfer full): a new string, or %NULL if an error occurred
1719  */
1720 gchar *
1721 gda_server_operation_get_node_path_portion (GdaServerOperation *op, const gchar *path)
1722 {
1723 	Node *node;
1724 
1725 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1726 	g_return_val_if_fail (op->priv, NULL);
1727 	g_return_val_if_fail (path && (*path == '/'), NULL);
1728 
1729 	node = node_find (op, path);
1730 	if (!node) {
1731 		gchar *path2 = g_strdup (path);
1732 		gchar *ptr, *retval = NULL;
1733 
1734 		ptr = path2 + strlen (path2) - 1;
1735 		while (*ptr != '/')
1736 			ptr --;
1737 		retval = g_strdup (ptr + 1);
1738 		g_free (path2);
1739 		return retval;
1740 	}
1741 	else {
1742 		if (node->type == GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM) {
1743 			g_assert (node->parent);
1744 			g_assert (node->parent->type == GDA_SERVER_OPERATION_NODE_SEQUENCE);
1745 			return g_strdup_printf ("%d", g_slist_index (node->parent->d.seq.seq_items, node));
1746 		}
1747 		else
1748 			return g_strdup (node->path_name);
1749 	}
1750 }
1751 
1752 /**
1753  * gda_server_operation_get_sequence_item_names:
1754  * @op: a #GdaServerOperation object
1755  * @path: a complete path to a sequence node (starting with "/")
1756  *
1757  * Fetch the contents of a sequence. @path can describe either a sequence (for example "/SEQNAME") or an item in a sequence
1758  * (for example "/SEQNAME/3")
1759  *
1760  * Returns: (transfer full): a array of strings containing the complete paths of the nodes contained at @path (free with g_strfreev())
1761  */
1762 gchar **
1763 gda_server_operation_get_sequence_item_names (GdaServerOperation *op, const gchar *path)
1764 {
1765 	Node *node;
1766 	gchar **retval;
1767 	gint i;
1768 	GSList *list;
1769 
1770 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1771 	g_return_val_if_fail (op->priv, NULL);
1772 
1773 	node = node_find (op, path);
1774 	if (!node || ((node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE) &&
1775 		      (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM)))
1776 		return NULL;
1777 
1778 	if (node->type == GDA_SERVER_OPERATION_NODE_SEQUENCE)
1779 		list = node->d.seq.seq_tmpl;
1780 	else
1781 		list = node->d.seq_item_nodes;
1782 	i = 0;
1783 	retval = g_new0 (gchar *, g_slist_length (list) + 1);
1784 	for (; list; list = list->next, i++)
1785 		retval [i] = node_get_complete_path (op, NODE (list->data));
1786 
1787 	return retval;
1788 }
1789 
1790 /**
1791  * gda_server_operation_get_sequence_name:
1792  * @op: a #GdaServerOperation object
1793  * @path: a complete path to a sequence node (starting with "/")
1794  *
1795  * Returns: (transfer none): the name of the sequence at @path
1796  */
1797 const gchar *
1798 gda_server_operation_get_sequence_name (GdaServerOperation *op, const gchar *path)
1799 {
1800 	Node *node;
1801 
1802 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
1803 	g_return_val_if_fail (op->priv, NULL);
1804 
1805 	node = node_find (op, path);
1806 	if (!node || (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE))
1807 		return NULL;
1808 
1809 	return node->d.seq.name;
1810 }
1811 
1812 /**
1813  * gda_server_operation_get_sequence_size:
1814  * @op: a #GdaServerOperation object
1815  * @path: a complete path to a sequence node (starting with "/")
1816  *
1817  * Returns: the number of items in the sequence at @path, or 0 if @path is not a sequence node
1818  */
1819 guint
1820 gda_server_operation_get_sequence_size (GdaServerOperation *op, const gchar *path)
1821 {
1822 	Node *node;
1823 
1824 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), 0);
1825 	g_return_val_if_fail (op->priv, 0);
1826 
1827 	node = node_find (op, path);
1828 	if (!node || (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE))
1829 		return 0;
1830 
1831 	return g_slist_length (node->d.seq.seq_items);
1832 }
1833 
1834 /**
1835  * gda_server_operation_get_sequence_max_size:
1836  * @op: a #GdaServerOperation object
1837  * @path: a complete path to a sequence node (starting with "/")
1838  *
1839  * Returns: the maximum number of items in the sequence at @path, or 0 if @path is not a sequence node
1840  */
1841 guint
1842 gda_server_operation_get_sequence_max_size (GdaServerOperation *op, const gchar *path)
1843 {
1844 	Node *node;
1845 
1846 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), 0);
1847 	g_return_val_if_fail (op->priv, 0);
1848 
1849 	node = node_find (op, path);
1850 	if (!node || (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE))
1851 		return 0;
1852 
1853 	return node->d.seq.max_items;
1854 }
1855 
1856 /**
1857  * gda_server_operation_get_sequence_min_size:
1858  * @op: a #GdaServerOperation object
1859  * @path: a complete path to a sequence node (starting with "/")
1860  *
1861  * Returns: the minimum number of items in the sequence at @path, or 0 if @path is not a sequence node
1862  */
1863 guint
1864 gda_server_operation_get_sequence_min_size (GdaServerOperation *op, const gchar *path)
1865 {
1866 	Node *node;
1867 
1868 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), 0);
1869 	g_return_val_if_fail (op->priv, 0);
1870 
1871 	node = node_find (op, path);
1872 	if (!node || (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE))
1873 		return 0;
1874 
1875 	return node->d.seq.min_items;
1876 }
1877 
1878 
1879 #ifdef GDA_DEBUG_NO
1880 static void
1881 dump (GdaServerOperation *op)
1882 {
1883 	xmlNodePtr node;
1884 	node = gda_server_operation_save_data_to_xml (op, NULL);
1885 	if (node) {
1886 		xmlDocPtr doc;
1887 		xmlChar *buffer;
1888 
1889 		doc = xmlNewDoc ("1.0");
1890 		xmlDocSetRootElement (doc, node);
1891 		xmlIndentTreeOutput = 1;
1892 		xmlKeepBlanksDefault (0);
1893 		xmlDocDumpFormatMemory (doc, &buffer, NULL, 1);
1894 		g_print ("%s\n", buffer);
1895 		xmlFree (buffer);
1896 		xmlFreeDoc (doc);
1897 	}
1898 	else
1899 		g_warning ("Saving to XML failed!");
1900 }
1901 #endif
1902 
1903 /**
1904  * gda_server_operation_add_item_to_sequence:
1905  * @op: a #GdaServerOperation object
1906  * @seq_path: the path to the sequence to which an item must be added (like "/SEQ_NAME" for instance)
1907  *
1908  * Returns: the index of the new entry in the sequence (like 5 for example if a 6th item has
1909  *          been added to the sequence.
1910  */
1911 guint
1912 gda_server_operation_add_item_to_sequence (GdaServerOperation *op, const gchar *seq_path)
1913 {
1914 	Node *node;
1915 
1916 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), 0);
1917 	g_return_val_if_fail (op->priv, 0);
1918 
1919 	node = node_find (op, seq_path);
1920 	if (!node || (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE))
1921 		return 0;
1922 
1923 	if (g_slist_length (node->d.seq.seq_items) == node->d.seq.max_items)
1924 		return 0;
1925 
1926 	sequence_add_item (op, node);
1927 
1928 #ifdef GDA_DEBUG_NO
1929 	dump (op);
1930 #endif
1931 
1932 	return g_slist_length (node->d.seq.seq_items);
1933 }
1934 
1935 /**
1936  * gda_server_operation_del_item_from_sequence:
1937  * @op: a #GdaServerOperation object
1938  * @item_path: the path to the sequence's item to remove (like "/SEQ_NAME/5" for instance)
1939  *
1940  * Returns: TRUE if the specified node has been removed from the sequence
1941  */
1942 gboolean
1943 gda_server_operation_del_item_from_sequence (GdaServerOperation *op, const gchar *item_path)
1944 {
1945 	Node *node, *item_node;
1946 	gchar *seq_path, *ptr;
1947 
1948 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
1949 	g_return_val_if_fail (op->priv, FALSE);
1950 
1951 	seq_path = g_strdup (item_path);
1952 	ptr = seq_path + strlen (seq_path) - 1;
1953 	while ((ptr >= seq_path) &&
1954 	       (((*ptr >= '0') && (*ptr <= '9')) || (*ptr == '/'))) {
1955 		*ptr = 0;
1956 		ptr--;
1957 	}
1958 
1959 	node = node_find (op, seq_path);
1960 	if (!node ||
1961 	    (node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE) ||
1962 	    (g_slist_length (node->d.seq.seq_items) == node->d.seq.min_items)) {
1963 		g_free (seq_path);
1964 		return FALSE;
1965 	}
1966 
1967 	item_node = node_find (op, item_path);
1968 	if (!item_node || (item_node->type != GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM)) {
1969 		g_free (seq_path);
1970 		return FALSE;
1971 	}
1972 
1973 	clean_nodes_info_cache (op);
1974 #ifdef GDA_DEBUG_signal
1975 	g_print (">> 'SEQUENCE_ITEM_REMOVE' from %s\n", __FUNCTION__);
1976 #endif
1977 	g_signal_emit (G_OBJECT (op), gda_server_operation_signals [SEQUENCE_ITEM_REMOVE], 0,
1978 		       seq_path, g_slist_index (node->d.seq.seq_items, item_node));
1979 #ifdef GDA_DEBUG_signal
1980 	g_print ("<< 'SEQUENCE_ITEM_REMOVE' from %s\n", __FUNCTION__);
1981 #endif
1982 
1983 	g_free (seq_path);
1984 	node_destroy (op, item_node);
1985 	node->d.seq.seq_items = g_slist_remove (node->d.seq.seq_items, item_node);
1986 
1987 #ifdef GDA_DEBUG_NO
1988 	dump (op);
1989 #endif
1990 
1991 	return FALSE;
1992 }
1993 
1994 /**
1995  * gda_server_operation_get_value_at_path: (rename-to gda_server_operation_get_value_at)
1996  * @op: a #GdaServerOperation object
1997  * @path: a complete path to a node (starting with "/")
1998  *
1999  * Get the value for the node at the @path path
2000  *
2001  * Returns: (transfer none) (nullable): a constant #GValue if a value has been defined, or %NULL if the value is undefined or if the @path is not defined or @path does not hold any value.
2002  *
2003  * Since: 4.2.6
2004  *
2005  */
2006 const GValue *
2007 gda_server_operation_get_value_at_path (GdaServerOperation *op, const gchar *path)
2008 {
2009 	const GValue *value = NULL;
2010 	GdaServerOperationNode *node_info;
2011 
2012 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
2013 	g_return_val_if_fail (op->priv, NULL);
2014 	g_return_val_if_fail (path && *path, NULL);
2015 
2016 	/* use path */
2017 	node_info = gda_server_operation_get_node_info (op, path);
2018 	if (node_info) {
2019 		switch (node_info->type) {
2020 		case GDA_SERVER_OPERATION_NODE_PARAM:
2021 			value = gda_holder_get_value (node_info->param);
2022 			break;
2023 		case GDA_SERVER_OPERATION_NODE_PARAMLIST:
2024 		case GDA_SERVER_OPERATION_NODE_DATA_MODEL:
2025 		case GDA_SERVER_OPERATION_NODE_SEQUENCE:
2026 		case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM:
2027 		case GDA_SERVER_OPERATION_NODE_DATA_MODEL_COLUMN:
2028 			break;
2029 		default:
2030 			g_assert_not_reached ();
2031 		}
2032 	}
2033 	else {
2034 		/* specific syntax which does not yield to a GdaServerOperationNode */
2035 		gchar *str;
2036 		str = gda_server_operation_get_node_parent (op, path);
2037 		if (str) {
2038 			node_info = gda_server_operation_get_node_info (op, str);
2039 			if (node_info && (node_info->type == GDA_SERVER_OPERATION_NODE_DATA_MODEL_COLUMN)) {
2040 				gchar *extension, *ptr;
2041 				gint row;
2042 				extension = gda_server_operation_get_node_path_portion (op, path);
2043 
2044 				row = strtol (extension, &ptr, 10);
2045 				if (ptr && *ptr)
2046 					row = -1;
2047 				if (row >= 0)
2048 					value = gda_data_model_get_value_at (node_info->model,
2049 									     gda_column_get_position (node_info->column),
2050 									     row, NULL);
2051 				g_free(extension);
2052 			}
2053 			g_free (str);
2054 		}
2055 	}
2056 
2057 	return value;
2058 }
2059 
2060 /**
2061  * gda_server_operation_get_value_at: (rename-to gda_server_operation_get_value_at_format)
2062  * @op: a #GdaServerOperation object
2063  * @path_format: a complete path to a node (starting with "/")
2064  * @...: arguments to use with @path_format to make a complete path
2065  *
2066  * Get the value for the node at the path formed using @path_format and ... (the rules are the same as
2067  * for g_strdup_printf())
2068  *
2069  * Returns: (transfer none): a constant #GValue if a value has been defined, or %NULL if the value is undefined or
2070  * if the @path is not defined or @path does not hold any value.
2071  */
2072 const GValue *
2073 gda_server_operation_get_value_at (GdaServerOperation *op, const gchar *path_format, ...)
2074 {
2075 	const GValue *value = NULL;
2076 	gchar *path;
2077 	va_list args;
2078 
2079 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
2080 	g_return_val_if_fail (op->priv, NULL);
2081 
2082 	/* build path */
2083 	va_start (args, path_format);
2084 	path = g_strdup_vprintf (path_format, args);
2085 	va_end (args);
2086 
2087 	value = gda_server_operation_get_value_at_path (op, path);
2088 	g_free (path);
2089 
2090 	return value;
2091 }
2092 
2093 /**
2094  * gda_server_operation_get_sql_identifier_at: (skip)
2095  * @op: a #GdaServerOperation object
2096  * @cnc: (nullable): a #GdaConnection, or %NULL
2097  * @prov: (nullable): a #GdaServerProvider, or %NULL
2098  * @path_format: a complete path to a node (starting with "/")
2099  * @...: arguments to use with @path_format to make a complete path
2100  *
2101  * This method is similar to gda_server_operation_get_value_at(), but for SQL identifiers: a new string
2102  * is returned instead of a #GValue. Also the returned string is assumed to represents an SQL identifier
2103  * and will correctly be quoted to be used with @cnc, or @prov if @cnc is %NULL (a generic quoting rule
2104  * will be applied if both are %NULL).
2105  *
2106  * Returns: (transfer full): a new string, or %NULL if the value is undefined or
2107  * if the @path is not defined or @path does not hold any value, or if the value held is not a string
2108  * (in that last case a warning is shown).
2109  *
2110  * Since: 4.0.3
2111  */
2112 gchar *
2113 gda_server_operation_get_sql_identifier_at (GdaServerOperation *op, GdaConnection *cnc, GdaServerProvider *prov,
2114 					    const gchar *path_format, ...)
2115 {
2116 	gchar *path, *ret;
2117 	va_list args;
2118 
2119 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
2120 
2121 	/* build path */
2122 	va_start (args, path_format);
2123 	path = g_strdup_vprintf (path_format, args);
2124 	va_end (args);
2125 
2126 	ret = gda_server_operation_get_sql_identifier_at_path (op, cnc, prov, path);
2127 	g_free (path);
2128 
2129 	return ret;
2130 }
2131 
2132 /**
2133  * gda_server_operation_get_sql_identifier_at_path: (rename-to gda_server_operation_get_sql_identifier_at)
2134  * @op: a #GdaServerOperation object
2135  * @cnc: (nullable): a #GdaConnection, or %NULL
2136  * @prov: (nullable): a #GdaServerProvider, or %NULL
2137  * @path: a complete path to a node (starting with "/")
2138  *
2139  * This method is similar to gda_server_operation_get_value_at(), but for SQL identifiers: a new string
2140  * is returned instead of a #GValue. Also the returned string is assumed to represents an SQL identifier
2141  * and will correctly be quoted to be used with @cnc, or @prov if @cnc is %NULL (a generic quoting rule
2142  * will be applied if both are %NULL).
2143  *
2144  * Returns: (transfer full): a new string, or %NULL if the value is undefined or
2145  * if the @path is not defined or @path does not hold any value, or if the value held is not a string
2146  * (in that last case a warning is shown).
2147  *
2148  * Since: 4.2.6
2149  *
2150  */
2151 gchar *
2152 gda_server_operation_get_sql_identifier_at_path (GdaServerOperation *op, GdaConnection *cnc, GdaServerProvider *prov,
2153 						 const gchar *path)
2154 {
2155 	const GValue *value = NULL;
2156 	GdaConnectionOptions cncoptions = 0;
2157 
2158 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), NULL);
2159 
2160 	value = gda_server_operation_get_value_at_path (op, path);
2161 
2162 	if (!value || (G_VALUE_TYPE (value) == GDA_TYPE_NULL))
2163 		return NULL;
2164 	g_return_val_if_fail (G_VALUE_TYPE (value) == G_TYPE_STRING, NULL);
2165 
2166 	if (cnc)
2167 		g_object_get (G_OBJECT (cnc), "options", &cncoptions, NULL);
2168 	return gda_sql_identifier_quote (g_value_get_string (value), cnc, prov, FALSE,
2169 					 cncoptions & GDA_CONNECTION_OPTIONS_SQL_IDENTIFIERS_CASE_SENSITIVE);
2170 }
2171 
2172 /**
2173  * gda_server_operation_set_value_at_path: (rename-to gda_server_operation_set_value_at)
2174  * @op: a #GdaServerOperation object
2175  * @value: (nullable): a string
2176  * @path: a complete path to a node (starting with "/")
2177  * @error: a place to store errors or %NULL
2178  *
2179  * Set the value for the node at the path formed using @path_format and the ... ellipse (the rules are the same as
2180  * for g_strdup_printf()).
2181  *
2182  * Note that trying to set a value for a path which is not used by the current
2183  * provider, such as "/TABLE_OPTIONS_P/TABLE_ENGINE" for a PostgreSQL connection (this option is only supported for MySQL),
2184  * will <emphasis>not</emphasis> generate
2185  * any error; this allows one to give values to a superset of the parameters and thus use the same code for several providers.
2186  *
2187  * Here are the possible formats of @path_format:
2188  * <itemizedlist>
2189  *  <listitem><para>If the path corresponds to a #GdaHolder, then the parameter is set to <![CDATA["@value"]]></para></listitem>
2190  *  <listitem><para>If the path corresponds to a sequence item like for example "/SEQUENCE_NAME/5/NAME" for
2191  *     the "NAME" value of the 6th item of the "SEQUENCE_NAME" sequence then:
2192  *     <itemizedlist>
2193  *        <listitem><para>if the sequence already has 6 or more items, then the value is just set to the corresponding
2194  *           value in the 6th item of the sequence</para></listitem>
2195  *        <listitem><para>if the sequence has less then 6 items, then items are added up to the 6th one before setting
2196  *           the value to the corresponding in the 6th item of the sequence</para></listitem>
2197  *     </itemizedlist>
2198  *  </para></listitem>
2199  *  <listitem><para>If the path corresponds to a #GdaDataModel, like for example "/ARRAY/@@COLUMN/5" for the value at the
2200  *     6th row of the "COLUMN" column of the "ARRAY" data model, then:
2201  *     <itemizedlist>
2202  *        <listitem><para>if the data model already contains 6 or more rows, then the value is just set</para></listitem>
2203  *        <listitem><para>if the data model has less than 6 rows, then rows are added up to the 6th one before setting
2204  *           the value</para></listitem>
2205  *     </itemizedlist>
2206  *  </para></listitem>
2207  * </itemizedlist>
2208  *
2209  * Returns: %TRUE if no error occurred
2210  *
2211  * Since: 4.2.6
2212  *
2213  */
2214 gboolean
2215 gda_server_operation_set_value_at_path (GdaServerOperation *op, const gchar *value,
2216 					const gchar *path, GError **error)
2217 {
2218 	Node *opnode;
2219 	gchar *extension = NULL;
2220 	gchar *colname = NULL;
2221 	gboolean allok = TRUE;
2222 
2223 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2224 	g_return_val_if_fail (op->priv, FALSE);
2225 
2226 	/* set the value */
2227 	opnode = node_find_or_create (op, path);
2228 	if (!opnode) {
2229 		/* try to see if the "parent" is a real node */
2230 		gchar *str;
2231 
2232 		str = gda_server_operation_get_node_parent (op, path);
2233 		if (str) {
2234 			opnode = node_find (op, str);
2235 			if (opnode) {
2236 				if (opnode->type != GDA_SERVER_OPERATION_NODE_PARAMLIST)
2237 					opnode = NULL; /* ignore opnode */
2238 			}
2239 			else {
2240 				gchar *str2;
2241 
2242 				str2 = gda_server_operation_get_node_parent (op, str);
2243 				opnode = node_find (op, str2);
2244 				if (opnode) {
2245 					if (opnode->type != GDA_SERVER_OPERATION_NODE_DATA_MODEL)
2246 						opnode = NULL; /* ignore opnode */
2247 					else
2248 						colname = gda_server_operation_get_node_path_portion (op, str);
2249 				}
2250 				g_free (str2);
2251 			}
2252 			g_free (str);
2253 		}
2254 		if (opnode)
2255 			extension = gda_server_operation_get_node_path_portion (op, path);
2256 	}
2257 
2258 	if (opnode) {
2259 		switch (opnode->type) {
2260 		case GDA_SERVER_OPERATION_NODE_PARAMLIST:
2261 			if (!extension) {
2262 				g_set_error (error, GDA_SERVER_OPERATION_ERROR,
2263 					     GDA_SERVER_OPERATION_XML_ERROR,
2264 					     "%s",
2265 					     _("Parameterlist values can only be set for individual parameters within it"));
2266 				allok = FALSE;
2267 			}
2268 			else {
2269 				GdaHolder *param;
2270 				param = gda_set_get_holder (opnode->d.plist, extension);
2271 				if (param) {
2272 					GValue *v;
2273 					if (value)
2274 						v = gda_value_new_from_string (value,
2275 									       gda_holder_get_g_type (param));
2276 					else
2277 						v = gda_value_new_null ();
2278 					if (!gda_holder_take_value (param, v, error))
2279 						allok = FALSE;
2280 				}
2281 			}
2282 			break;
2283 		case GDA_SERVER_OPERATION_NODE_DATA_MODEL: {
2284 			GdaColumn *column = NULL;
2285 
2286 			if (colname && (*colname == '@')) {
2287 				gint i, nbcols;
2288 
2289 				nbcols = gda_data_model_get_n_columns (opnode->d.model);
2290 				for (i = 0; (i<nbcols) && !column; i++) {
2291 					gchar *colid = NULL;
2292 					column = gda_data_model_describe_column (opnode->d.model, i);
2293 					g_object_get (G_OBJECT (column), "id", &colid, NULL);
2294 					if (!colid || strcmp (colid, colname +1))
2295 						column = NULL;
2296 					g_free(colid);
2297 				}
2298 				if (column) {
2299 					gchar *ptr;
2300 					gint row;
2301 					row = strtol (extension, &ptr, 10);
2302 					if (ptr && *ptr)
2303 						row = -1;
2304 					if (row >= 0) {
2305 						gint i = gda_data_model_get_n_rows (opnode->d.model);
2306 
2307 						if (i <= row) {
2308 							for (; allok && (i <= row); i++)
2309 								if (gda_data_model_append_row (opnode->d.model, error) < 0)
2310 									allok = FALSE;
2311 						}
2312 
2313 						if (allok) {
2314 							GValue *gvalue;
2315 							if (value)
2316 								gvalue = gda_value_new_from_string (value,
2317 												    gda_column_get_g_type (column));
2318 							else
2319 								gvalue = gda_value_new_null ();
2320 							allok = gda_data_model_set_value_at (opnode->d.model,
2321 											     gda_column_get_position (column),
2322 											     row, gvalue, error);
2323 							gda_value_free (gvalue);
2324 						}
2325 					}
2326 				}
2327 			}
2328 			break;
2329 		}
2330 		case GDA_SERVER_OPERATION_NODE_PARAM: {
2331 			GValue *v;
2332 			if (value)
2333 				v = gda_value_new_from_string (value,
2334 							       gda_holder_get_g_type (opnode->d.param));
2335 			else
2336 				v = gda_value_new_null ();
2337 			if (!gda_holder_take_value (opnode->d.param, v, error))
2338 				allok = FALSE;
2339 			break;
2340 		}
2341 		case GDA_SERVER_OPERATION_NODE_SEQUENCE:
2342 			break;
2343 		case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM:
2344 			break;
2345 		default:
2346 			g_assert_not_reached ();
2347 		}
2348 	}
2349 
2350 	g_free (extension);
2351 	g_free (colname);
2352 	return allok;
2353 }
2354 
2355 /**
2356  * gda_server_operation_set_value_at: (skip)
2357  * @op: a #GdaServerOperation object
2358  * @value: (nullable): a string
2359  * @error: a place to store errors or %NULL
2360  * @path_format: a complete path to a node (starting with "/")
2361  * @...: arguments to use with @path_format to make a complete path
2362  *
2363  * Set the value for the node at the path formed using @path_format and the ... ellipse (the rules are the same as
2364  * for g_strdup_printf()).
2365  *
2366  * Note that trying to set a value for a path which is not used by the current
2367  * provider, such as "/TABLE_OPTIONS_P/TABLE_ENGINE" for a PostgreSQL connection (this option is only supported for MySQL),
2368  * will <emphasis>not</emphasis> generate
2369  * any error; this allows one to give values to a superset of the parameters and thus use the same code for several providers.
2370  *
2371  * Here are the possible formats of @path_format:
2372  * <itemizedlist>
2373  *  <listitem><para>If the path corresponds to a #GdaHolder, then the parameter is set to <![CDATA["@value"]]></para></listitem>
2374  *  <listitem><para>If the path corresponds to a sequence item like for example "/SEQUENCE_NAME/5/NAME" for
2375  *     the "NAME" value of the 6th item of the "SEQUENCE_NAME" sequence then:
2376  *     <itemizedlist>
2377  *        <listitem><para>if the sequence already has 6 or more items, then the value is just set to the corresponding
2378  *           value in the 6th item of the sequence</para></listitem>
2379  *        <listitem><para>if the sequence has less then 6 items, then items are added up to the 6th one before setting
2380  *           the value to the corresponding in the 6th item of the sequence</para></listitem>
2381  *     </itemizedlist>
2382  *  </para></listitem>
2383  *  <listitem><para>If the path corresponds to a #GdaDataModel, like for example "/ARRAY/@@COLUMN/5" for the value at the
2384  *     6th row of the "COLUMN" column of the "ARRAY" data model, then:
2385  *     <itemizedlist>
2386  *        <listitem><para>if the data model already contains 6 or more rows, then the value is just set</para></listitem>
2387  *        <listitem><para>if the data model has less than 6 rows, then rows are added up to the 6th one before setting
2388  *           the value</para></listitem>
2389  *     </itemizedlist>
2390  *  </para></listitem>
2391  * </itemizedlist>
2392  *
2393  * Returns: %TRUE if no error occurred
2394  */
2395 gboolean
2396 gda_server_operation_set_value_at (GdaServerOperation *op, const gchar *value, GError **error,
2397 				   const gchar *path_format, ...)
2398 {
2399 	gchar *path;
2400 	va_list args;
2401 	gboolean ret;
2402 
2403 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2404 	g_return_val_if_fail (op->priv, FALSE);
2405 
2406 	/* build path */
2407 	va_start (args, path_format);
2408 	path = g_strdup_vprintf (path_format, args);
2409 	va_end (args);
2410 
2411 	ret = gda_server_operation_set_value_at_path (op, value, path, error);
2412 	g_free (path);
2413 
2414 	return ret;
2415 }
2416 
2417 /**
2418  * gda_server_operation_is_valid:
2419  * @op: a #GdaServerOperation widget
2420  * @xml_file: (nullable): an XML specification file (see gda_server_operation_new()) or %NULL
2421  * @error: a place to store an error, or %NULL
2422  *
2423  * Tells if all the required values in @op have been defined.
2424  *
2425  * if @xml_file is not %NULL, the validity of @op is tested against that specification,
2426  * and not against the current @op's specification.
2427  *
2428  * Returns: %TRUE if @op is valid
2429  */
2430 gboolean
2431 gda_server_operation_is_valid (GdaServerOperation *op, const gchar *xml_file, GError **error)
2432 {
2433 	gboolean valid = TRUE;
2434 	GSList *list;
2435 
2436 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2437 	g_return_val_if_fail (op->priv, FALSE);
2438 
2439 	if (!xml_file) {
2440 		/* basic validity test */
2441 		for (list = op->priv->allnodes; list; list = list->next) {
2442 			Node *node;
2443 
2444 			node = NODE (list->data);
2445 			if (node->status == GDA_SERVER_OPERATION_STATUS_REQUIRED) {
2446 				if (node->type == GDA_SERVER_OPERATION_NODE_PARAM) {
2447 					const GValue *value;
2448 					gchar *path;
2449 
2450 					path = node_get_complete_path (op, node);
2451 					value = gda_server_operation_get_value_at (op, path);
2452 					if (!value) {
2453 						valid = FALSE;
2454 						g_set_error (error,
2455 							     GDA_SERVER_OPERATION_ERROR,
2456 							     GDA_SERVER_OPERATION_INCORRECT_VALUE_ERROR,
2457 							     _("Missing required value for '%s'"), path);
2458 						break;
2459 					}
2460 					g_free (path);
2461 				}
2462 				else if (node->type == GDA_SERVER_OPERATION_NODE_PARAMLIST) {
2463 					valid = gda_set_is_valid (node->d.plist, error);
2464 					if (!valid)
2465 						break;
2466 				}
2467 			}
2468 		}
2469 	}
2470 	else {
2471 		/* use @xml_file */
2472 		xmlNodePtr save;
2473 
2474 		save = gda_server_operation_save_data_to_xml (op, error);
2475 		if (save) {
2476 			GdaServerOperation *op2;
2477 			op2 = gda_server_operation_new (op->priv->op_type, xml_file);
2478 			if (gda_server_operation_load_data_from_xml (op2, save, error))
2479 				valid = gda_server_operation_is_valid (op2, NULL, error);
2480 			else
2481 				valid = FALSE;
2482 			xmlFreeNode (save);
2483 			g_object_unref (op2);
2484 		}
2485 		else
2486 			valid = FALSE;
2487 	}
2488 
2489 	return valid;
2490 }
2491 
2492 /**
2493  * gda_server_operation_prepare_create_database:
2494  * @provider: the database provider to use
2495  * @db_name: (nullable): the name of the database to create, or %NULL
2496  * @error: a place to store errors, or %NULL
2497  *
2498  * Creates a new #GdaServerOperation object which contains the specifications required
2499  * to create a database. Once these specifications provided, use
2500  * gda_server_operation_perform_create_database() to perform the database creation.
2501  *
2502  * If @db_name is left %NULL, then the name of the database to create will have to be set in the
2503  * returned #GdaServerOperation using gda_server_operation_set_value_at().
2504  *
2505  * Returns: (transfer full) (nullable): new #GdaServerOperation object, or %NULL if the provider does not support database
2506  * creation
2507  *
2508  * Since: 4.2.3
2509  */
2510 GdaServerOperation *
2511 gda_server_operation_prepare_create_database (const gchar *provider, const gchar *db_name, GError **error)
2512 {
2513 	GdaServerProvider *prov;
2514 
2515 	g_return_val_if_fail (provider && *provider, NULL);
2516 
2517 	prov = gda_config_get_provider (provider, error);
2518 	if (prov) {
2519 		GdaServerOperation *op;
2520 		op = gda_server_provider_create_operation (prov, NULL, GDA_SERVER_OPERATION_CREATE_DB,
2521 							   NULL, error);
2522 		if (op) {
2523 			g_object_set_data_full (G_OBJECT (op), "_gda_provider_obj", g_object_ref (prov), g_object_unref);
2524 			if (db_name)
2525 				gda_server_operation_set_value_at (op, db_name, NULL, "/DB_DEF_P/DB_NAME");
2526 		}
2527 		return op;
2528 	}
2529 	else
2530 		return NULL;
2531 }
2532 
2533 /**
2534  * gda_server_operation_perform_create_database:
2535  * @provider: (nullable): the database provider to use, or %NULL if @op has been created using gda_server_operation_prepare_create_database()
2536  * @op: a #GdaServerOperation object obtained using gda_server_operation_prepare_create_database()
2537  * @error: a place to store en error, or %NULL
2538  *
2539  * Creates a new database using the specifications in @op. @op can be obtained using
2540  * gda_server_provider_create_operation(), or gda_server_operation_prepare_create_database().
2541  *
2542  * Returns: TRUE if no error occurred and the database has been created, FALSE otherwise
2543  *
2544  * Since: 4.2.3
2545  */
2546 gboolean
2547 gda_server_operation_perform_create_database (GdaServerOperation *op, const gchar *provider, GError **error)
2548 {
2549 	GdaServerProvider *prov;
2550 
2551 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2552 	if (provider)
2553 		prov = gda_config_get_provider (provider, error);
2554 	else
2555 		prov = g_object_get_data (G_OBJECT (op), "_gda_provider_obj");
2556 	if (prov)
2557 		return gda_server_provider_perform_operation (prov, NULL, op, error);
2558 	else {
2559 		g_warning ("Could not find operation's associated provider, "
2560 			   "did you use gda_server_operation_prepare_create_database() ?");
2561 		return FALSE;
2562 	}
2563 }
2564 
2565 /**
2566  * gda_server_operation_prepare_drop_database:
2567  * @provider: the database provider to use
2568  * @db_name: (nullable): the name of the database to drop, or %NULL
2569  * @error: a place to store errors, or %NULL
2570  *
2571  * Creates a new #GdaServerOperation object which contains the specifications required
2572  * to drop a database. Once these specifications provided, use
2573  * gda_server_operation_perform_drop_database() to perform the database creation.
2574  *
2575  * If @db_name is left %NULL, then the name of the database to drop will have to be set in the
2576  * returned #GdaServerOperation using gda_server_operation_set_value_at().
2577  *
2578  * Returns: (transfer full) (nullable): new #GdaServerOperation object, or %NULL if the provider does not support database
2579  * destruction
2580  *
2581  * Since: 4.2.3
2582  */
2583 GdaServerOperation *
2584 gda_server_operation_prepare_drop_database (const gchar *provider, const gchar *db_name, GError **error)
2585 {
2586 	GdaServerProvider *prov;
2587 
2588 	g_return_val_if_fail (provider && *provider, NULL);
2589 
2590 	prov = gda_config_get_provider (provider, error);
2591 	if (prov) {
2592 		GdaServerOperation *op;
2593 		op = gda_server_provider_create_operation (prov, NULL, GDA_SERVER_OPERATION_DROP_DB,
2594 							   NULL, error);
2595 		if (op) {
2596 			g_object_set_data_full (G_OBJECT (op), "_gda_provider_obj", g_object_ref (prov), g_object_unref);
2597 			if (db_name)
2598 				gda_server_operation_set_value_at (op, db_name, NULL, "/DB_DESC_P/DB_NAME");
2599 		}
2600 		return op;
2601 	}
2602 	else
2603 		return NULL;
2604 }
2605 
2606 /**
2607  * gda_server_operation_perform_drop_database:
2608  * @provider: (nullable): the database provider to use, or %NULL if @op has been created using gda_server_operation_prepare_drop_database()
2609  * @op: a #GdaServerOperation object obtained using gda_server_operation_prepare_drop_database()
2610  * @error: a place to store en error, or %NULL
2611  *
2612  * Destroys an existing database using the specifications in @op. @op can be obtained using
2613  * gda_server_provider_create_operation(), or gda_server_operation_prepare_drop_database().
2614  *
2615  * Returns: TRUE if no error occurred and the database has been destroyed
2616  *
2617  * Since: 4.2.3
2618  */
2619 gboolean
2620 gda_server_operation_perform_drop_database (GdaServerOperation *op, const gchar *provider, GError **error)
2621 {
2622 	GdaServerProvider *prov;
2623 
2624 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2625 	g_return_val_if_fail (gda_server_operation_get_op_type (op) == GDA_SERVER_OPERATION_DROP_DB, FALSE);
2626 	if (provider)
2627 		prov = gda_config_get_provider (provider, error);
2628 	else
2629 		prov = g_object_get_data (G_OBJECT (op), "_gda_provider_obj");
2630 	if (prov)
2631 		return gda_server_provider_perform_operation (prov, NULL, op, error);
2632 	else {
2633 		g_warning ("Could not find operation's associated provider, "
2634 			   "did you use gda_server_operation_prepare_drop_database() ?");
2635 		return FALSE;
2636 	}
2637 }
2638 
2639 /**
2640  * gda_server_operation_prepare_create_table:
2641  * @cnc: an opened connection
2642  * @table_name: name of the table to create
2643  * @error: a place to store errors, or %NULL
2644  * @...: group of three arguments for column's name, column's #GType
2645  * and a #GdaServerOperationCreateTableFlag flag, finished with %NULL
2646  *
2647  * Add more arguments if the flag needs them:
2648  *
2649  * GDA_SERVER_OPERATION_CREATE_TABLE_FKEY_FLAG:
2650  * <itemizedlist>
2651  *   <listitem><para>string with the table's name referenced</para></listitem>
2652  *   <listitem><para>an integer with the number pairs "local_field", "referenced_field"
2653  *   used in the reference</para></listitem>
2654  *   <listitem><para>Pairs of "local_field", "referenced_field" to use, must match
2655  *    the number specified above.</para></listitem>
2656  *   <listitem><para>a string with the action for ON DELETE; can be: "RESTRICT", "CASCADE",
2657  *    "NO ACTION", "SET NULL" and "SET DEFAULT". Example: "ON UPDATE CASCADE".</para></listitem>
2658  *   <listitem><para>a string with the action for ON UPDATE (see above).</para></listitem>
2659  * </itemizedlist>
2660  *
2661  * Create a #GdaServerOperation object using an opened connection, taking three
2662  * arguments, a column's name the column's GType and #GdaServerOperationCreateTableFlag
2663  * flag, you need to finish the list using %NULL.
2664  *
2665  * You'll be able to modify the #GdaServerOperation object to add custom options * to the operation. When finished call #gda_server_operation_perform_create_table
2666  * or #gda_server_provider_perform_operation
2667  * in order to execute the operation.
2668  *
2669  * Returns: (transfer full) (nullable): a #GdaServerOperation if no errors; NULL and set @error otherwise
2670  *
2671  * Since: 4.2.3
2672  */
2673 G_GNUC_NULL_TERMINATED
2674 GdaServerOperation*
2675 gda_server_operation_prepare_create_table (GdaConnection *cnc, const gchar *table_name, GError **error, ...)
2676 {
2677 	GdaServerOperation *op;
2678 	GdaServerProvider *server;
2679 
2680 	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
2681 
2682 	server = gda_connection_get_provider (cnc);
2683 
2684 	if (!table_name) {
2685 		g_set_error (error, GDA_SERVER_OPERATION_ERROR, GDA_SERVER_OPERATION_OBJECT_NAME_ERROR,
2686 			     "%s", _("Unspecified table name"));
2687 		return NULL;
2688 	}
2689 
2690 	if (gda_server_provider_supports_operation (server, cnc, GDA_SERVER_OPERATION_CREATE_TABLE, NULL)) {
2691 		va_list  args;
2692 		gchar   *arg;
2693 		GType    type;
2694 		gchar   *dbms_type;
2695 		GdaServerOperationCreateTableFlag flag;
2696 		gint i;
2697 		gint refs;
2698 
2699 		op = gda_server_provider_create_operation (server, cnc,
2700 							   GDA_SERVER_OPERATION_CREATE_TABLE, NULL, error);
2701 		if (!GDA_IS_SERVER_OPERATION(op))
2702 			return NULL;
2703 		if(!gda_server_operation_set_value_at (op, table_name, error, "/TABLE_DEF_P/TABLE_NAME")) {
2704 			g_object_unref (op);
2705 			return NULL;
2706 		}
2707 
2708 		va_start (args, error);
2709 		type = 0;
2710 		arg = NULL;
2711 		i = 0;
2712 		refs = -1;
2713 
2714 		while ((arg = va_arg (args, gchar*))) {
2715 			/* First argument for Column's name */
2716 			if(!gda_server_operation_set_value_at (op, arg, error, "/FIELDS_A/@COLUMN_NAME/%d", i)){
2717 				g_object_unref (op);
2718 				va_end (args);
2719 				return NULL;
2720 			}
2721 
2722 			/* Second to Define column's type */
2723 			type = va_arg (args, GType);
2724 			if (type == 0) {
2725 				g_set_error (error, GDA_SERVER_OPERATION_ERROR, GDA_SERVER_OPERATION_INCORRECT_VALUE_ERROR,
2726 					     "%s", _("Invalid type"));
2727 				g_object_unref (op);
2728 				va_end (args);
2729 				return NULL;
2730 			}
2731 			dbms_type = (gchar *) gda_server_provider_get_default_dbms_type (server,
2732 											 cnc, type);
2733 			if (!gda_server_operation_set_value_at (op, dbms_type, error, "/FIELDS_A/@COLUMN_TYPE/%d", i)){
2734 				g_object_unref (op);
2735 				va_end (args);
2736 				return NULL;
2737 			}
2738 
2739 			/* Third for column's flags */
2740 			flag = va_arg (args, GdaServerOperationCreateTableFlag);
2741 			if (flag & GDA_SERVER_OPERATION_CREATE_TABLE_PKEY_FLAG)
2742 				if(!gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_PKEY/%d", i)){
2743 					g_object_unref (op);
2744 					va_end (args);
2745 					return NULL;
2746 				}
2747 			if (flag & GDA_SERVER_OPERATION_CREATE_TABLE_NOT_NULL_FLAG)
2748 				if(!gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_NNUL/%d", i)){
2749 					g_object_unref (op);
2750 					va_end (args);
2751 					return NULL;
2752 				}
2753 			if (flag & GDA_SERVER_OPERATION_CREATE_TABLE_AUTOINC_FLAG)
2754 				if (!gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_AUTOINC/%d", i)){
2755 					g_object_unref (op);
2756 					va_end (args);
2757 					return NULL;
2758 				}
2759 			if (flag & GDA_SERVER_OPERATION_CREATE_TABLE_UNIQUE_FLAG)
2760 				if(!gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_UNIQUE/%d", i)){
2761 					g_object_unref (op);
2762 					va_end (args);
2763 					return NULL;
2764 				}
2765 			if (flag & GDA_SERVER_OPERATION_CREATE_TABLE_FKEY_FLAG) {
2766 				gint j;
2767 				gint fields;
2768 				gchar *fkey_table;
2769 				gchar *fkey_ondelete;
2770 				gchar *fkey_onupdate;
2771 
2772 				refs++;
2773 
2774 				fkey_table = va_arg (args, gchar*);
2775 				if (!gda_server_operation_set_value_at (op, fkey_table, error,
2776 								   "/FKEY_S/%d/FKEY_REF_TABLE", refs)){
2777 					g_object_unref (op);
2778 					va_end (args);
2779 					return NULL;
2780 				}
2781 
2782 				fields = va_arg (args, gint);
2783 
2784 				for (j = 0; j < fields; j++) {
2785 					gchar *field, *rfield;
2786 
2787 					field = va_arg (args, gchar*);
2788 					if(!gda_server_operation_set_value_at (op, field, error,
2789 									   "/FKEY_S/%d/FKEY_FIELDS_A/@FK_FIELD/%d", refs, j)){
2790 						g_object_unref (op);
2791 						va_end (args);
2792 						return NULL;
2793 					}
2794 
2795 					rfield = va_arg (args, gchar*);
2796 					if(!gda_server_operation_set_value_at (op, rfield, error,
2797 									   "/FKEY_S/%d/FKEY_FIELDS_A/@FK_REF_PK_FIELD/%d", refs, j)){
2798 						g_object_unref (op);
2799 						va_end (args);
2800 						return NULL;
2801 					}
2802 				}
2803 
2804 				fkey_ondelete = va_arg (args, gchar*);
2805 				if (!gda_server_operation_set_value_at (op, fkey_ondelete, error,
2806 								   "/FKEY_S/%d/FKEY_ONDELETE", refs)){
2807 					g_object_unref (op);
2808 					va_end (args);
2809 					return NULL;
2810 				}
2811 				fkey_onupdate = va_arg (args, gchar*);
2812 				if(!gda_server_operation_set_value_at (op, fkey_onupdate, error,
2813 								   "/FKEY_S/%d/FKEY_ONUPDATE", refs)){
2814 					g_object_unref (op);
2815 					va_end (args);
2816 					return NULL;
2817 				}
2818 			}
2819 
2820 			i++;
2821 		}
2822 
2823 		va_end (args);
2824 
2825 		g_object_set_data_full (G_OBJECT (op), "_gda_connection", g_object_ref (cnc), g_object_unref);
2826 
2827 		return op;
2828 	}
2829 	else {
2830 		g_set_error (error, GDA_SERVER_OPERATION_ERROR, GDA_SERVER_OPERATION_OBJECT_NAME_ERROR,
2831 			     "%s", _("CREATE TABLE operation is not supported by the database server"));
2832 		return NULL;
2833 	}
2834 }
2835 
2836 
2837 /**
2838  * gda_server_operation_perform_create_table:
2839  * @op: a valid #GdaServerOperation
2840  * @error: a place to store errors, or %NULL
2841  *
2842  * Performs a prepared #GdaServerOperation to create a table. This could perform
2843  * an operation created by #gda_server_operation_prepare_create_table or any other using the
2844  * the #GdaServerOperation API.
2845  *
2846  * Returns: TRUE if the table was created; FALSE and set @error otherwise
2847  *
2848  * Since: 4.2.3
2849  */
2850 gboolean
2851 gda_server_operation_perform_create_table (GdaServerOperation *op, GError **error)
2852 {
2853 	GdaConnection *cnc;
2854 
2855 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2856 	g_return_val_if_fail (gda_server_operation_get_op_type (op) == GDA_SERVER_OPERATION_CREATE_TABLE, FALSE);
2857 
2858 	cnc = g_object_get_data (G_OBJECT (op), "_gda_connection");
2859 	if (cnc)
2860 		return gda_server_provider_perform_operation (gda_connection_get_provider (cnc), cnc, op, error);
2861 	else {
2862 		g_warning ("Could not find operation's associated connection, "
2863 			   "did you use gda_connection_prepare_create_table() ?");
2864 		return FALSE;
2865 	}
2866 }
2867 
2868 /**
2869  * gda_server_operation_prepare_drop_table:
2870  * @cnc: an opened connection
2871  * @table_name: name of the table to drop
2872  * @error: a place to store errors, or %NULL
2873  *
2874  * This is just a convenient function to create a #GdaServerOperation to drop a
2875  * table in an opened connection.
2876  *
2877  * Returns: (transfer full) (nullable): a new #GdaServerOperation or %NULL if couldn't create the opereration.
2878  *
2879  * Since: 4.2.3
2880  */
2881 GdaServerOperation*
2882 gda_server_operation_prepare_drop_table (GdaConnection *cnc, const gchar *table_name, GError **error)
2883 {
2884 	GdaServerOperation *op;
2885 	GdaServerProvider *server;
2886 
2887 	server = gda_connection_get_provider (cnc);
2888 
2889 	op = gda_server_provider_create_operation (server, cnc,
2890 						   GDA_SERVER_OPERATION_DROP_TABLE, NULL, error);
2891 
2892 	if (GDA_IS_SERVER_OPERATION (op)) {
2893 		g_return_val_if_fail (table_name != NULL
2894 				      || GDA_IS_CONNECTION (cnc)
2895 				      || !gda_connection_is_opened (cnc), NULL);
2896 
2897 		if (gda_server_operation_set_value_at (op, table_name,
2898 						       error, "/TABLE_DESC_P/TABLE_NAME")) {
2899 			g_object_set_data_full (G_OBJECT (op), "_gda_connection", g_object_ref (cnc), g_object_unref);
2900 			return op;
2901 		}
2902 		else
2903 			return NULL;
2904 	}
2905 	else
2906 		return NULL;
2907 }
2908 
2909 
2910 /**
2911  * gda_server_operation_perform_drop_table:
2912  * @op: a #GdaServerOperation object
2913  * @error: a place to store errors, or %NULL
2914  *
2915  * This is just a convenient function to perform a drop a table operation.
2916  *
2917  * Returns: TRUE if the table was dropped
2918  *
2919  * Since: 4.2.3
2920  */
2921 gboolean
2922 gda_server_operation_perform_drop_table (GdaServerOperation *op, GError **error)
2923 {
2924 	GdaConnection *cnc;
2925 
2926 	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
2927 	g_return_val_if_fail (gda_server_operation_get_op_type (op) == GDA_SERVER_OPERATION_DROP_TABLE, FALSE);
2928 
2929 	cnc = g_object_get_data (G_OBJECT (op), "_gda_connection");
2930 	if (cnc)
2931 		return gda_server_provider_perform_operation (gda_connection_get_provider (cnc), cnc, op, error);
2932 	else {
2933 		g_warning ("Could not find operation's associated connection, "
2934 			   "did you use gda_server_operation_prepare_drop_table() ?");
2935 		return FALSE;
2936 	}
2937 }
2938