1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301, USA.
20  */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <glib/gi18n-lib.h>
27 #include <glib/gstdio.h>
28 #include <libgda/libgda.h>
29 #include <libgda/gda-data-model-private.h>
30 #include <libgda/gda-server-provider-extra.h>
31 #include <libgda/binreloc/gda-binreloc.h>
32 #include <libgda/gda-statement-extra.h>
33 #include <sql-parser/gda-sql-parser.h>
34 #include "gda-web.h"
35 #include "gda-web-provider.h"
36 #include "gda-web-recordset.h"
37 #include "gda-web-ddl.h"
38 #include "gda-web-meta.h"
39 #include "gda-web-util.h"
40 #include <libgda/gda-debug-macros.h>
41 
42 /* Use the RSA reference implementation included in the RFC-1321, http://www.freesoft.org/CIE/RFC/1321/ */
43 #include "global.h"
44 #include "md5.h"
45 
46 #define _GDA_PSTMT(x) ((GdaPStmt*)(x))
47 
48 /*
49  * GObject methods
50  */
51 static void gda_web_provider_class_init (GdaWebProviderClass *klass);
52 static void gda_web_provider_init       (GdaWebProvider *provider,
53 					 GdaWebProviderClass *klass);
54 static GObjectClass *parent_class = NULL;
55 
56 /*
57  * GdaServerProvider's virtual methods
58  */
59 /* connection management */
60 static gboolean            gda_web_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
61 							     GdaQuarkList *params, GdaQuarkList *auth,
62 							     guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
63 static gboolean            gda_web_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc);
64 static const gchar        *gda_web_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc);
65 
66 /* DDL operations */
67 static gboolean            gda_web_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
68 								GdaServerOperationType type, GdaSet *options);
69 static GdaServerOperation *gda_web_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
70 							      GdaServerOperationType type,
71 							      GdaSet *options, GError **error);
72 static gchar              *gda_web_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
73 							      GdaServerOperation *op, GError **error);
74 
75 static gboolean            gda_web_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
76 							       GdaServerOperation *op, guint *task_id,
77 							       GdaServerProviderAsyncCallback async_cb, gpointer cb_data,
78 							       GError **error);
79 /* transactions */
80 static gboolean            gda_web_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
81 							       const gchar *name, GdaTransactionIsolation level, GError **error);
82 static gboolean            gda_web_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
83 								const gchar *name, GError **error);
84 static gboolean            gda_web_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection * cnc,
85 								  const gchar *name, GError **error);
86 static gboolean            gda_web_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
87 							   const gchar *name, GError **error);
88 static gboolean            gda_web_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
89 								const gchar *name, GError **error);
90 static gboolean            gda_web_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
91 							      const gchar *name, GError **error);
92 
93 /* information retrieval */
94 static const gchar        *gda_web_provider_get_version (GdaServerProvider *provider);
95 static gboolean            gda_web_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc,
96 							      GdaConnectionFeature feature);
97 
98 static const gchar        *gda_web_provider_get_name (GdaServerProvider *provider);
99 
100 static GdaDataHandler     *gda_web_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
101 							      GType g_type, const gchar *dbms_type);
102 
103 static const gchar*        gda_web_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc,
104 								   GType type);
105 /* statements */
106 static GdaSqlParser        *gda_web_provider_create_parser (GdaServerProvider *provider, GdaConnection *cnc);
107 static gchar               *gda_web_provider_statement_to_sql  (GdaServerProvider *provider, GdaConnection *cnc,
108 								GdaStatement *stmt, GdaSet *params,
109 								GdaStatementSqlFlag flags,
110 								GSList **params_used, GError **error);
111 static gboolean             gda_web_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
112 								GdaStatement *stmt, GError **error);
113 static GObject             *gda_web_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
114 								GdaStatement *stmt, GdaSet *params,
115 								GdaStatementModelUsage model_usage,
116 								GType *col_types, GdaSet **last_inserted_row,
117 								guint *task_id, GdaServerProviderExecCallback async_cb,
118 								gpointer cb_data, GError **error);
119 static GdaSqlStatement     *gda_web_statement_rewrite          (GdaServerProvider *provider, GdaConnection *cnc,
120 								GdaStatement *stmt, GdaSet *params, GError **error);
121 
122 /* Quoting */
123 static gchar               *gda_web_identifier_quote    (GdaServerProvider *provider, GdaConnection *cnc,
124 							 const gchar *id,
125 							 gboolean meta_store_convention, gboolean force_quotes);
126 
127 
128 /*
129  * GdaWebProvider class implementation
130  */
131 static void
gda_web_provider_class_init(GdaWebProviderClass * klass)132 gda_web_provider_class_init (GdaWebProviderClass *klass)
133 {
134 	GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
135 
136 	parent_class = g_type_class_peek_parent (klass);
137 
138 	provider_class->get_version = gda_web_provider_get_version;
139 	provider_class->get_server_version = gda_web_provider_get_server_version;
140 	provider_class->get_name = gda_web_provider_get_name;
141 	provider_class->supports_feature = gda_web_provider_supports_feature;
142 
143 	provider_class->get_data_handler = gda_web_provider_get_data_handler;
144 	provider_class->get_def_dbms_type = gda_web_provider_get_default_dbms_type;
145 
146 	provider_class->open_connection = gda_web_provider_open_connection;
147 	provider_class->close_connection = gda_web_provider_close_connection;
148 	provider_class->get_database = NULL;
149 
150 	provider_class->supports_operation = gda_web_provider_supports_operation;
151         provider_class->create_operation = gda_web_provider_create_operation;
152         provider_class->render_operation = gda_web_provider_render_operation;
153         provider_class->perform_operation = gda_web_provider_perform_operation;
154 
155 	provider_class->begin_transaction = gda_web_provider_begin_transaction;
156 	provider_class->commit_transaction = gda_web_provider_commit_transaction;
157 	provider_class->rollback_transaction = gda_web_provider_rollback_transaction;
158 	provider_class->add_savepoint = gda_web_provider_add_savepoint;
159         provider_class->rollback_savepoint = gda_web_provider_rollback_savepoint;
160         provider_class->delete_savepoint = gda_web_provider_delete_savepoint;
161 
162 	provider_class->create_parser = gda_web_provider_create_parser;
163 	provider_class->statement_to_sql =  NULL; /* don't use gda_web_provider_statement_to_sql()
164 						   * because it only calls gda_statement_to_sql_extended() */
165 	provider_class->statement_prepare = gda_web_provider_statement_prepare;
166 	provider_class->statement_execute = gda_web_provider_statement_execute;
167 	provider_class->statement_rewrite = gda_web_statement_rewrite;
168 
169 	provider_class->is_busy = NULL;
170 	provider_class->cancel = NULL;
171 	provider_class->create_connection = NULL;
172 
173 	provider_class->identifier_quote = gda_web_identifier_quote;
174 
175 	memset (&(provider_class->meta_funcs), 0, sizeof (GdaServerProviderMeta));
176 	provider_class->meta_funcs._info = _gda_web_meta__info;
177 	provider_class->meta_funcs._btypes = _gda_web_meta__btypes;
178 	provider_class->meta_funcs._udt = _gda_web_meta__udt;
179 	provider_class->meta_funcs.udt = _gda_web_meta_udt;
180 	provider_class->meta_funcs._udt_cols = _gda_web_meta__udt_cols;
181 	provider_class->meta_funcs.udt_cols = _gda_web_meta_udt_cols;
182 	provider_class->meta_funcs._enums = _gda_web_meta__enums;
183 	provider_class->meta_funcs.enums = _gda_web_meta_enums;
184 	provider_class->meta_funcs._domains = _gda_web_meta__domains;
185 	provider_class->meta_funcs.domains = _gda_web_meta_domains;
186 	provider_class->meta_funcs._constraints_dom = _gda_web_meta__constraints_dom;
187 	provider_class->meta_funcs.constraints_dom = _gda_web_meta_constraints_dom;
188 	provider_class->meta_funcs._el_types = _gda_web_meta__el_types;
189 	provider_class->meta_funcs.el_types = _gda_web_meta_el_types;
190 	provider_class->meta_funcs._collations = _gda_web_meta__collations;
191 	provider_class->meta_funcs.collations = _gda_web_meta_collations;
192 	provider_class->meta_funcs._character_sets = _gda_web_meta__character_sets;
193 	provider_class->meta_funcs.character_sets = _gda_web_meta_character_sets;
194 	provider_class->meta_funcs._schemata = _gda_web_meta__schemata;
195 	provider_class->meta_funcs.schemata = _gda_web_meta_schemata;
196 	provider_class->meta_funcs._tables_views = _gda_web_meta__tables_views;
197 	provider_class->meta_funcs.tables_views = _gda_web_meta_tables_views;
198 	provider_class->meta_funcs._columns = _gda_web_meta__columns;
199 	provider_class->meta_funcs.columns = _gda_web_meta_columns;
200 	provider_class->meta_funcs._view_cols = _gda_web_meta__view_cols;
201 	provider_class->meta_funcs.view_cols = _gda_web_meta_view_cols;
202 	provider_class->meta_funcs._constraints_tab = _gda_web_meta__constraints_tab;
203 	provider_class->meta_funcs.constraints_tab = _gda_web_meta_constraints_tab;
204 	provider_class->meta_funcs._constraints_ref = _gda_web_meta__constraints_ref;
205 	provider_class->meta_funcs.constraints_ref = _gda_web_meta_constraints_ref;
206 	provider_class->meta_funcs._key_columns = _gda_web_meta__key_columns;
207 	provider_class->meta_funcs.key_columns = _gda_web_meta_key_columns;
208 	provider_class->meta_funcs._check_columns = _gda_web_meta__check_columns;
209 	provider_class->meta_funcs.check_columns = _gda_web_meta_check_columns;
210 	provider_class->meta_funcs._triggers = _gda_web_meta__triggers;
211 	provider_class->meta_funcs.triggers = _gda_web_meta_triggers;
212 	provider_class->meta_funcs._routines = _gda_web_meta__routines;
213 	provider_class->meta_funcs.routines = _gda_web_meta_routines;
214 	provider_class->meta_funcs._routine_col = _gda_web_meta__routine_col;
215 	provider_class->meta_funcs.routine_col = _gda_web_meta_routine_col;
216 	provider_class->meta_funcs._routine_par = _gda_web_meta__routine_par;
217 	provider_class->meta_funcs.routine_par = _gda_web_meta_routine_par;
218 	provider_class->meta_funcs._indexes_tab = _gda_web_meta__indexes_tab;
219         provider_class->meta_funcs.indexes_tab = _gda_web_meta_indexes_tab;
220         provider_class->meta_funcs._index_cols = _gda_web_meta__index_cols;
221         provider_class->meta_funcs.index_cols = _gda_web_meta_index_cols;
222 
223 	/* distributed transactions: if not supported, then provider_class->xa_funcs should be set to NULL */
224 	provider_class->xa_funcs = NULL;
225 
226 	/* provider is thread safe */
227 	provider_class->limiting_thread = NULL;
228 }
229 
230 static void
gda_web_provider_init(G_GNUC_UNUSED GdaWebProvider * web_prv,G_GNUC_UNUSED GdaWebProviderClass * klass)231 gda_web_provider_init (G_GNUC_UNUSED GdaWebProvider *web_prv, G_GNUC_UNUSED GdaWebProviderClass *klass)
232 {
233 }
234 
235 GType
gda_web_provider_get_type(void)236 gda_web_provider_get_type (void)
237 {
238 	static GType type = 0;
239 
240 	if (G_UNLIKELY (type == 0)) {
241 		static GMutex registering;
242 		static GTypeInfo info = {
243 			sizeof (GdaWebProviderClass),
244 			(GBaseInitFunc) NULL,
245 			(GBaseFinalizeFunc) NULL,
246 			(GClassInitFunc) gda_web_provider_class_init,
247 			NULL, NULL,
248 			sizeof (GdaWebProvider),
249 			0,
250 			(GInstanceInitFunc) gda_web_provider_init,
251 			0
252 		};
253 		g_mutex_lock (&registering);
254 		if (type == 0)
255 			type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaWebProvider", &info, 0);
256 		g_mutex_unlock (&registering);
257 	}
258 
259 	return type;
260 }
261 
262 
263 /*
264  * Get provider name request
265  */
266 static const gchar *
gda_web_provider_get_name(G_GNUC_UNUSED GdaServerProvider * provider)267 gda_web_provider_get_name (G_GNUC_UNUSED GdaServerProvider *provider)
268 {
269 	return WEB_PROVIDER_NAME;
270 }
271 
272 /*
273  * Get provider's version, no need to change this
274  */
275 static const gchar *
gda_web_provider_get_version(G_GNUC_UNUSED GdaServerProvider * provider)276 gda_web_provider_get_version (G_GNUC_UNUSED GdaServerProvider *provider)
277 {
278 	return PACKAGE_VERSION;
279 }
280 
281 static gboolean
do_server_setup(GdaConnection * cnc,WebConnectionData * cdata)282 do_server_setup (GdaConnection *cnc, WebConnectionData *cdata)
283 {
284 	SoupMessage *msg;
285 	guint status;
286 	gchar *real_url;
287 
288 	real_url = g_strdup_printf ("%s/gda-setup.php", cdata->server_base_url);
289 	msg = soup_message_new ("GET", real_url);
290 	if (!msg) {
291 		gda_connection_add_event_string (cnc, _("Invalid HOST/SCRIPT '%s'"), real_url);
292 		g_free (real_url);
293 		return FALSE;
294 	}
295 	g_free (real_url);
296 
297 	g_object_set (G_OBJECT (cdata->front_session), SOUP_SESSION_TIMEOUT, 5, NULL);
298 	status = soup_session_send_message (cdata->front_session, msg);
299 	if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
300 		gda_connection_add_event_string (cnc, msg->reason_phrase);
301 		g_object_unref (msg);
302 		return FALSE;
303 	}
304 
305 	xmlDocPtr doc;
306 	gchar out_status_chr;
307 	doc = _gda_web_decode_response (cnc, cdata, msg->response_body, &out_status_chr, NULL);
308 	g_object_unref (msg);
309 	if (doc) {
310 		if (out_status_chr != 'O') {
311 			_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
312 			xmlFreeDoc (doc);
313 			return FALSE;
314 		}
315 		xmlFreeDoc (doc);
316 		return TRUE;
317 	}
318 	return FALSE;
319 }
320 
321 /*
322  * Open connection request
323  *
324  * In this function, the following _must_ be done:
325  *   - check for the presence and validify of the parameters required to actually open a connection,
326  *     using @params
327  *   - open the real connection to the database using the parameters previously checked
328  *   - create a WebConnectionData structure and associate it to @cnc
329  *
330  * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
331  */
332 static gboolean
gda_web_provider_open_connection(GdaServerProvider * provider,GdaConnection * cnc,GdaQuarkList * params,GdaQuarkList * auth,G_GNUC_UNUSED guint * task_id,GdaServerProviderAsyncCallback async_cb,G_GNUC_UNUSED gpointer cb_data)333 gda_web_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
334 				  GdaQuarkList *params, GdaQuarkList *auth,
335 				  G_GNUC_UNUSED guint *task_id, GdaServerProviderAsyncCallback async_cb,
336 				  G_GNUC_UNUSED gpointer cb_data)
337 {
338 	g_return_val_if_fail (GDA_IS_WEB_PROVIDER (provider), FALSE);
339 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
340 
341 	/* If asynchronous connection opening is not supported, then exit now */
342 	if (async_cb) {
343 		gda_connection_add_event_string (cnc, _("Provider does not support asynchronous connection open"));
344                 return FALSE;
345 	}
346 
347 	/* Check for connection parameters */
348 	const gchar *db_name, *host, *path, *port, *serversecret, *pass = NULL, *use_ssl;
349 
350 	if (auth)
351 		pass = gda_quark_list_find (auth, "PASSWORD");
352 	if (!pass) {
353 		gda_connection_add_event_string (cnc, _("The connection string must contain the %s value"), "PASSWORD");
354                 return FALSE;
355 	}
356 	host = gda_quark_list_find (params, "HOST");
357 	if (!host) {
358 		gda_connection_add_event_string (cnc,
359 						 _("The connection string must contain the %s value"), "HOST");
360 		return FALSE;
361 	}
362 	serversecret = gda_quark_list_find (params, "SECRET");
363 	if (!serversecret) {
364 		gda_connection_add_event_string (cnc,
365 						 _("The connection string must contain the %s value"), "SECRET");
366 		return FALSE;
367 	}
368 	path = gda_quark_list_find (params, "PATH");
369 	port = gda_quark_list_find (params, "PORT");
370 	db_name = gda_quark_list_find (params, "DB_NAME");
371 	if (!db_name) {
372 		gda_connection_add_event_string (cnc,
373 						 _("The connection string must contain the %s value"), "DB_NAME");
374 		return FALSE;
375 	}
376 	use_ssl = gda_quark_list_find (params, "USE_SSL");
377 	if (use_ssl && (*use_ssl != 'T') && (*use_ssl != 't'))
378 		use_ssl = NULL;
379 
380 	/* open Libsoup session */
381 	WebConnectionData *cdata;
382 	GString *server_url;
383 
384 	cdata = g_new0 (WebConnectionData, 1);
385 	cdata->mutex = gda_mutex_new ();
386 	cdata->server_id = NULL;
387 	cdata->forced_closing = FALSE;
388 	cdata->worker_session = soup_session_sync_new ();
389 	cdata->front_session = soup_session_sync_new_with_options ("max-conns-per-host", 1, NULL);
390 	if (use_ssl) {
391 		server_url = g_string_new ("https://");
392 		g_print ("USING SSL\n");
393 	}
394 	else
395 		server_url = g_string_new ("http://");
396 	g_string_append (server_url, host);
397 	if (port)
398 		g_string_append_printf (server_url, ":%s", port);
399 	if (path)
400 		g_string_append_printf (server_url, "/%s", path);
401 	cdata->front_url = g_strdup_printf ("%s/gda-front.php", server_url->str);
402 	cdata->worker_url = g_strdup_printf ("%s/gda-worker.php", server_url->str);
403 	cdata->server_base_url = g_string_free (server_url, FALSE);
404 	if (serversecret)
405 		cdata->key = g_strdup (serversecret);
406 	gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) _gda_web_free_cnc_data);
407 
408 	/*
409 	 * perform setup
410 	 */
411 	if (! do_server_setup (cnc, cdata))
412 		return FALSE;
413 
414 	/*
415 	 * send HELLO
416 	 */
417 	xmlDocPtr doc;
418 	gchar status;
419 #define HELLO_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" \
420 		"<request>\n"						\
421 		"  <cmd>HELLO</cmd>\n"					\
422 		"</request>"
423 	doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_HELLO, HELLO_MSG, NULL, &status);
424 	if (!doc) {
425 		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
426 		_gda_web_do_server_cleanup (cnc, cdata);
427 		return FALSE;
428 	}
429 	if (status != 'O') {
430 		_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
431 		xmlFreeDoc (doc);
432 		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
433 		_gda_web_do_server_cleanup (cnc, cdata);
434 		return FALSE;
435 	}
436 	xmlFreeDoc (doc);
437 
438 	/*
439 	 * send CONNECT
440 	 */
441 	gchar *tmp, *token;
442 #define CONNECT_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
443 		"<request>\n"						\
444 		"  <token>%s</token>\n"					\
445 		"  <cmd>CONNECT</cmd>\n"				\
446 		"</request>"
447 	if (cdata->key)
448 		g_free (cdata->key);
449 	cdata->key = g_strdup_printf ("%s/AND/%s", db_name, pass);
450 
451 	token = _gda_web_compute_token (cdata);
452 	tmp = g_strdup_printf (CONNECT_MSG, token);
453 	g_free (token);
454 
455 	cdata->server_secret = g_strdup (serversecret);
456 	doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_CONNECT, tmp, serversecret, &status);
457 	g_free (tmp);
458 	if (!doc) {
459 		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
460 		_gda_web_do_server_cleanup (cnc, cdata);
461 		return FALSE;
462 	}
463 	if (status != 'O') {
464 		_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
465 		xmlFreeDoc (doc);
466 		gda_connection_internal_set_provider_data (cnc, NULL, NULL);
467 		_gda_web_do_server_cleanup (cnc, cdata);
468 		return FALSE;
469 	}
470 	xmlFreeDoc (doc);
471 
472 	/*
473 	 * change key: cdata->key = MD5(cdata->key)
474 	 */
475 	MD5_CTX md5c;
476 	unsigned char digest[16];
477 	GString *md5str;
478         gint i;
479 	MD5Init (&md5c);
480         MD5Update (&md5c, (unsigned char *) cdata->key, strlen (cdata->key));
481         MD5Final (digest, &md5c);
482 	md5str = g_string_new ("");
483         for (i = 0; i < 16; i++)
484                 g_string_append_printf (md5str, "%02x", digest[i]);
485 	g_free (cdata->key);
486 	cdata->key = g_string_free (md5str, FALSE);
487 
488 	return TRUE;
489 }
490 
491 /*
492  * Close connection request
493  *
494  * In this function, the following _must_ be done:
495  *   - Actually close the connection to the database using @cnc's associated WebConnectionData structure
496  *   - Free the WebConnectionData structure and its contents
497  *
498  * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
499  */
500 static gboolean
gda_web_provider_close_connection(GdaServerProvider * provider,GdaConnection * cnc)501 gda_web_provider_close_connection (GdaServerProvider *provider, GdaConnection *cnc)
502 {
503 	WebConnectionData *cdata;
504 
505 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
506 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
507 
508 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
509 	if (!cdata)
510 		return FALSE;
511 
512 	gda_mutex_lock (cdata->mutex);
513 	if (!cdata->forced_closing && cdata->worker_running) {
514 		gda_mutex_unlock (cdata->mutex);
515 		/* send BYE message */
516 		xmlDocPtr doc;
517 		gchar status;
518 		gchar *tmp, *token;
519 #define BYE_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" \
520 			"<request>\n"					\
521 			"  <token>%s</token>\n"				\
522 			"  <cmd>BYE</cmd>\n"				\
523 			"</request>"
524 		token = _gda_web_compute_token (cdata);
525 		tmp = g_strdup_printf (BYE_MSG, token);
526 		g_free (token);
527 
528 		doc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_BYE, tmp, cdata->key, &status);
529 		g_free (tmp);
530 		if (!doc)
531 			return FALSE;
532 		if (status != 'C') {
533 			_gda_web_set_connection_error_from_xmldoc (cnc, doc, NULL);
534 			xmlFreeDoc (doc);
535 			return FALSE;
536 		}
537 		xmlFreeDoc (doc);
538 	}
539 	else
540 		gda_mutex_unlock (cdata->mutex);
541 
542 	_gda_web_do_server_cleanup (cnc, cdata);
543 
544 	/* Free the WebConnectionData structure and its contents*/
545 	_gda_web_free_cnc_data (cdata);
546 	gda_connection_internal_set_provider_data (cnc, NULL, NULL);
547 
548 	return TRUE;
549 }
550 
551 /*
552  * Server version request
553  *
554  * Returns the server version as a string, which should be stored in @cnc's associated WebConnectionData structure
555  */
556 static const gchar *
gda_web_provider_get_server_version(GdaServerProvider * provider,GdaConnection * cnc)557 gda_web_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
558 {
559 	WebConnectionData *cdata;
560 
561 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
562 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
563 
564 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
565 	if (!cdata)
566 		return NULL;
567 	return cdata->server_version;
568 }
569 
570 /*
571  * Support operation request
572  *
573  * Tells what the implemented server operations are. To add support for an operation, the following steps are required:
574  *   - create a web_specs_....xml.in file describing the required and optional parameters for the operation
575  *   - add it to the Makefile.am
576  *   - make this method return TRUE for the operation type
577  *   - implement the gda_web_provider_render_operation() and gda_web_provider_perform_operation() methods
578  *
579  * For now no server operation is supported, it can be added if cdata->reuseable is not %NULL
580  */
581 static gboolean
gda_web_provider_supports_operation(GdaServerProvider * provider,GdaConnection * cnc,GdaServerOperationType type,G_GNUC_UNUSED GdaSet * options)582 gda_web_provider_supports_operation (GdaServerProvider *provider, GdaConnection *cnc,
583 				     GdaServerOperationType type, G_GNUC_UNUSED GdaSet *options)
584 {
585 	if (cnc) {
586 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
587 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
588 	}
589 
590         switch (type) {
591         case GDA_SERVER_OPERATION_CREATE_DB:
592         case GDA_SERVER_OPERATION_DROP_DB:
593         case GDA_SERVER_OPERATION_CREATE_TABLE:
594         case GDA_SERVER_OPERATION_DROP_TABLE:
595         case GDA_SERVER_OPERATION_RENAME_TABLE:
596 
597         case GDA_SERVER_OPERATION_ADD_COLUMN:
598 
599         case GDA_SERVER_OPERATION_CREATE_INDEX:
600         case GDA_SERVER_OPERATION_DROP_INDEX:
601 
602         case GDA_SERVER_OPERATION_CREATE_VIEW:
603         case GDA_SERVER_OPERATION_DROP_VIEW:
604         default:
605 		TO_IMPLEMENT;
606                 return FALSE;
607         }
608 }
609 
610 /*
611  * Create operation request
612  *
613  * Creates a #GdaServerOperation. The following code is generic and should only be changed
614  * if some further initialization is required, or if operation's contents is dependent on @cnc
615  */
616 static GdaServerOperation *
gda_web_provider_create_operation(GdaServerProvider * provider,GdaConnection * cnc,G_GNUC_UNUSED GdaServerOperationType type,G_GNUC_UNUSED GdaSet * options,GError ** error)617 gda_web_provider_create_operation (GdaServerProvider *provider, GdaConnection *cnc,
618 				   G_GNUC_UNUSED GdaServerOperationType type, G_GNUC_UNUSED GdaSet *options,
619 				   GError **error)
620 {
621 	WebConnectionData *cdata = NULL;
622 
623 	if (cnc) {
624 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
625 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
626 
627 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
628 	}
629 	if (!cdata) {
630 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
631 			     GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
632 			     "%s", _("Not supported"));
633 		return NULL;
634 	}
635 
636 	TO_IMPLEMENT;
637 	g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
638 		     GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
639 		     "%s", _("Server operations not yet implemented"));
640 	return NULL;
641 }
642 
643 /*
644  * Render operation request
645  */
646 static gchar *
gda_web_provider_render_operation(GdaServerProvider * provider,GdaConnection * cnc,G_GNUC_UNUSED GdaServerOperation * op,GError ** error)647 gda_web_provider_render_operation (GdaServerProvider *provider, GdaConnection *cnc,
648 				   G_GNUC_UNUSED GdaServerOperation *op, GError **error)
649 {
650 	WebConnectionData *cdata = NULL;
651 
652 	if (cnc) {
653 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
654 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
655 
656 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
657 	}
658 	if (!cdata) {
659 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
660 			     GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
661 			     "%s", _("Not supported"));
662 		return NULL;
663 	}
664 
665 	TO_IMPLEMENT;
666 	g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
667 			     GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
668 		     "%s", _("Server operations not yet implemented"));
669 	return NULL;
670 }
671 
672 /*
673  * Perform operation request
674  */
675 static gboolean
gda_web_provider_perform_operation(GdaServerProvider * provider,GdaConnection * cnc,GdaServerOperation * op,G_GNUC_UNUSED guint * task_id,GdaServerProviderAsyncCallback async_cb,G_GNUC_UNUSED gpointer cb_data,GError ** error)676 gda_web_provider_perform_operation (GdaServerProvider *provider, GdaConnection *cnc,
677 				    GdaServerOperation *op, G_GNUC_UNUSED guint *task_id,
678 				    GdaServerProviderAsyncCallback async_cb, G_GNUC_UNUSED gpointer cb_data,
679 				    GError **error)
680 {
681         GdaServerOperationType optype;
682 
683 	/* If asynchronous connection opening is not supported, then exit now */
684 	if (async_cb) {
685 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
686 			     "%s", _("Provider does not support asynchronous server operation"));
687                 return FALSE;
688 	}
689 
690 	if (cnc) {
691 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
692 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
693 	}
694         optype = gda_server_operation_get_op_type (op);
695 	switch (optype) {
696 	case GDA_SERVER_OPERATION_CREATE_DB:
697 	case GDA_SERVER_OPERATION_DROP_DB:
698         default:
699 		/* use the SQL from the provider to perform the action */
700 		return gda_server_provider_perform_operation_default (provider, cnc, op, error);
701 	}
702 }
703 
704 /*
705  * Begin transaction request
706  */
707 static gboolean
gda_web_provider_begin_transaction(GdaServerProvider * provider,GdaConnection * cnc,const gchar * name,GdaTransactionIsolation level,GError ** error)708 gda_web_provider_begin_transaction (GdaServerProvider *provider, GdaConnection *cnc,
709 				    const gchar *name, GdaTransactionIsolation level,
710 				    GError **error)
711 {
712 	WebConnectionData *cdata;
713 
714 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
715 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
716 
717 	if (name && *name) {
718 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
719 			     "%s", _("Named transaction is not supported"));
720 		return FALSE;
721 	}
722 	if (level != GDA_TRANSACTION_ISOLATION_UNKNOWN) {
723 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
724 			     "%s", _("Transaction level is not supported"));
725 		return FALSE;
726 	}
727 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
728 	if (!cdata)
729 		return FALSE;
730 
731 	/* prepare XML command */
732 	xmlDocPtr doc;
733 	xmlNodePtr root;
734 	gchar *token;
735 	doc = xmlNewDoc (BAD_CAST "1.0");
736 	root = xmlNewNode (NULL, BAD_CAST "request");
737 	xmlDocSetRootElement (doc, root);
738 	token = _gda_web_compute_token (cdata);
739 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
740 	g_free (token);
741 	xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "BEGIN");
742 
743 	/* send command */
744 	xmlChar *cmde;
745 	xmlDocPtr replydoc;
746 	int size;
747 	gchar status;
748 
749 	xmlDocDumpMemory (doc, &cmde, &size);
750 	xmlFreeDoc (doc);
751 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
752 	xmlFree (cmde);
753 
754 	if (!replydoc) {
755 		_gda_web_change_connection_to_closed (cnc, cdata);
756 		return FALSE;
757 	}
758 	if (status != 'O') {
759 		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
760 		xmlFreeDoc (replydoc);
761 
762 		if (status == 'C')
763 			_gda_web_change_connection_to_closed (cnc, cdata);
764 		return FALSE;
765 	}
766 
767 	return TRUE;
768 }
769 
770 /*
771  * Commit transaction request
772  */
773 static gboolean
gda_web_provider_commit_transaction(GdaServerProvider * provider,GdaConnection * cnc,const gchar * name,GError ** error)774 gda_web_provider_commit_transaction (GdaServerProvider *provider, GdaConnection *cnc,
775 				     const gchar *name, GError **error)
776 {
777 	WebConnectionData *cdata;
778 
779 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
780 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
781 
782 	if (name && *name) {
783 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
784 			     "%s", _("Named transaction is not supported"));
785 		return FALSE;
786 	}
787 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
788 	if (!cdata)
789 		return FALSE;
790 
791 	/* prepare XML command */
792 	xmlDocPtr doc;
793 	xmlNodePtr root;
794 	gchar *token;
795 	doc = xmlNewDoc (BAD_CAST "1.0");
796 	root = xmlNewNode (NULL, BAD_CAST "request");
797 	xmlDocSetRootElement (doc, root);
798 	token = _gda_web_compute_token (cdata);
799 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
800 	g_free (token);
801 	xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "COMMIT");
802 
803 	/* send command */
804 	xmlChar *cmde;
805 	xmlDocPtr replydoc;
806 	int size;
807 	gchar status;
808 
809 	xmlDocDumpMemory (doc, &cmde, &size);
810 	xmlFreeDoc (doc);
811 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
812 	xmlFree (cmde);
813 
814 	if (!replydoc) {
815 		_gda_web_change_connection_to_closed (cnc, cdata);
816 		return FALSE;
817 	}
818 	if (status != 'O') {
819 		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
820 		xmlFreeDoc (replydoc);
821 
822 		if (status == 'C')
823 			_gda_web_change_connection_to_closed (cnc, cdata);
824 		return FALSE;
825 	}
826 
827 	return TRUE;
828 }
829 
830 /*
831  * Rollback transaction request
832  */
833 static gboolean
gda_web_provider_rollback_transaction(GdaServerProvider * provider,GdaConnection * cnc,const gchar * name,GError ** error)834 gda_web_provider_rollback_transaction (GdaServerProvider *provider, GdaConnection *cnc,
835 				       const gchar *name, GError **error)
836 {
837 	WebConnectionData *cdata;
838 
839 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
840 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
841 
842 	if (name && *name) {
843 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
844 			     "%s", _("Named transaction is not supported"));
845 		return FALSE;
846 	}
847 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
848 	if (!cdata)
849 		return FALSE;
850 
851 	/* prepare XML command */
852 	xmlDocPtr doc;
853 	xmlNodePtr root;
854 	gchar *token;
855 	doc = xmlNewDoc (BAD_CAST "1.0");
856 	root = xmlNewNode (NULL, BAD_CAST "request");
857 	xmlDocSetRootElement (doc, root);
858 	token = _gda_web_compute_token (cdata);
859 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
860 	g_free (token);
861 	xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "ROLLBACK");
862 
863 	/* send command */
864 	xmlChar *cmde;
865 	xmlDocPtr replydoc;
866 	int size;
867 	gchar status;
868 
869 	xmlDocDumpMemory (doc, &cmde, &size);
870 	xmlFreeDoc (doc);
871 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
872 	xmlFree (cmde);
873 
874 	if (!replydoc) {
875 		_gda_web_change_connection_to_closed (cnc, cdata);
876 		return FALSE;
877 	}
878 	if (status != 'O') {
879 		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
880 		xmlFreeDoc (replydoc);
881 
882 		if (status == 'C')
883 			_gda_web_change_connection_to_closed (cnc, cdata);
884 		return FALSE;
885 	}
886 
887 	return TRUE;
888 }
889 
890 /*
891  * Add savepoint request
892  */
893 static gboolean
gda_web_provider_add_savepoint(GdaServerProvider * provider,GdaConnection * cnc,const gchar * name,GError ** error)894 gda_web_provider_add_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
895 				const gchar *name, GError **error)
896 {
897 	WebConnectionData *cdata;
898 
899 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
900 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
901 
902 	if (!name || !(*name)) {
903 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
904 			     "%s", _("Unnamed savepoint is not supported"));
905 		return FALSE;
906 	}
907 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
908 	if (!cdata)
909 		return FALSE;
910 
911 	/* prepare XML command */
912 	xmlDocPtr doc;
913 	xmlNodePtr root, cmdnode;
914 	gchar *token;
915 	doc = xmlNewDoc (BAD_CAST "1.0");
916 	root = xmlNewNode (NULL, BAD_CAST "request");
917 	xmlDocSetRootElement (doc, root);
918 	token = _gda_web_compute_token (cdata);
919 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
920 	g_free (token);
921 	cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "BEGIN");
922 	xmlSetProp (cmdnode, BAD_CAST "svpname", BAD_CAST name);
923 
924 	/* send command */
925 	xmlChar *cmde;
926 	xmlDocPtr replydoc;
927 	int size;
928 	gchar status;
929 
930 	xmlDocDumpMemory (doc, &cmde, &size);
931 	xmlFreeDoc (doc);
932 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
933 	xmlFree (cmde);
934 
935 	if (!replydoc) {
936 		_gda_web_change_connection_to_closed (cnc, cdata);
937 		return FALSE;
938 	}
939 	if (status != 'O') {
940 		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
941 		xmlFreeDoc (replydoc);
942 
943 		if (status == 'C')
944 			_gda_web_change_connection_to_closed (cnc, cdata);
945 		return FALSE;
946 	}
947 
948 	return TRUE;
949 }
950 
951 /*
952  * Rollback savepoint request
953  */
954 static gboolean
gda_web_provider_rollback_savepoint(GdaServerProvider * provider,GdaConnection * cnc,const gchar * name,GError ** error)955 gda_web_provider_rollback_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
956 				     const gchar *name, GError **error)
957 {
958 	WebConnectionData *cdata;
959 
960 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
961 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
962 
963 	if (!name || !(*name)) {
964 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_NON_SUPPORTED_ERROR,
965 			     "%s", _("Unnamed savepoint is not supported"));
966 		return FALSE;
967 	}
968 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
969 	if (!cdata)
970 		return FALSE;
971 
972 	/* prepare XML command */
973 	xmlDocPtr doc;
974 	xmlNodePtr root, cmdnode;
975 	gchar *token;
976 	doc = xmlNewDoc (BAD_CAST "1.0");
977 	root = xmlNewNode (NULL, BAD_CAST "request");
978 	xmlDocSetRootElement (doc, root);
979 	token = _gda_web_compute_token (cdata);
980 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
981 	g_free (token);
982 	cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "ROLLBACK");
983 	xmlSetProp (cmdnode, BAD_CAST "svpname", BAD_CAST name);
984 
985 	/* send command */
986 	xmlChar *cmde;
987 	xmlDocPtr replydoc;
988 	int size;
989 	gchar status;
990 
991 	xmlDocDumpMemory (doc, &cmde, &size);
992 	xmlFreeDoc (doc);
993 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
994 	xmlFree (cmde);
995 
996 	if (!replydoc) {
997 		_gda_web_change_connection_to_closed (cnc, cdata);
998 		return FALSE;
999 	}
1000 	if (status != 'O') {
1001 		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
1002 		xmlFreeDoc (replydoc);
1003 
1004 		if (status == 'C')
1005 			_gda_web_change_connection_to_closed (cnc, cdata);
1006 		return FALSE;
1007 	}
1008 
1009 	return TRUE;
1010 }
1011 
1012 /*
1013  * Delete savepoint request
1014  */
1015 static gboolean
gda_web_provider_delete_savepoint(GdaServerProvider * provider,GdaConnection * cnc,G_GNUC_UNUSED const gchar * name,G_GNUC_UNUSED GError ** error)1016 gda_web_provider_delete_savepoint (GdaServerProvider *provider, GdaConnection *cnc,
1017 				   G_GNUC_UNUSED const gchar *name, G_GNUC_UNUSED GError **error)
1018 {
1019 	WebConnectionData *cdata;
1020 
1021 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1022 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1023 
1024 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
1025 	if (!cdata)
1026 		return FALSE;
1027 
1028 	TO_IMPLEMENT;
1029 
1030 	return FALSE;
1031 }
1032 
1033 /*
1034  * Feature support request
1035  */
1036 static gboolean
gda_web_provider_supports_feature(GdaServerProvider * provider,GdaConnection * cnc,GdaConnectionFeature feature)1037 gda_web_provider_supports_feature (GdaServerProvider *provider, GdaConnection *cnc, GdaConnectionFeature feature)
1038 {
1039 	if (cnc) {
1040 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1041 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1042 	}
1043 
1044 	switch (feature) {
1045 	case GDA_CONNECTION_FEATURE_SQL :
1046 		return TRUE;
1047 	default:
1048 		return FALSE;
1049 	}
1050 }
1051 
1052 /*
1053  * Get data handler request
1054  *
1055  * This method allows one to obtain a pointer to a #GdaDataHandler object specific to @type or @dbms_type (@dbms_type
1056  * must be considered only if @type is not a valid GType).
1057  *
1058  * A data handler allows one to convert a value between its different representations which are a human readable string,
1059  * an SQL representation and a GValue.
1060  *
1061  * The recommended method is to create GdaDataHandler objects only when they are needed and to keep a reference to them
1062  * for further usage, using the gda_server_provider_handler_declare() method.
1063  *
1064  * The implementation shown here does not define any specific data handler, but there should be some for at least
1065  * binary and time related types.
1066  */
1067 static GdaDataHandler *
gda_web_provider_get_data_handler(GdaServerProvider * provider,GdaConnection * cnc,G_GNUC_UNUSED GType type,G_GNUC_UNUSED const gchar * dbms_type)1068 gda_web_provider_get_data_handler (GdaServerProvider *provider, GdaConnection *cnc,
1069 				   G_GNUC_UNUSED GType type, G_GNUC_UNUSED const gchar *dbms_type)
1070 {
1071 	WebConnectionData *cdata = NULL;
1072 
1073 	if (cnc) {
1074 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1075 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1076 
1077 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
1078 	}
1079 	if (!cdata)
1080 		return NULL;
1081 
1082 	TO_IMPLEMENT;
1083 	return NULL;
1084 }
1085 
1086 /*
1087  * Get default DBMS type request
1088  *
1089  * This method returns the "preferred" DBMS type for GType
1090  */
1091 static const gchar*
gda_web_provider_get_default_dbms_type(GdaServerProvider * provider,GdaConnection * cnc,G_GNUC_UNUSED GType type)1092 gda_web_provider_get_default_dbms_type (GdaServerProvider *provider, GdaConnection *cnc, G_GNUC_UNUSED GType type)
1093 {
1094 	WebConnectionData *cdata = NULL;
1095 
1096 	if (cnc) {
1097 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1098 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1099 
1100 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
1101 	}
1102 	if (!cdata)
1103 		return NULL;
1104 
1105 	TO_IMPLEMENT;
1106 	return NULL;
1107 }
1108 
1109 /*
1110  * Create parser request
1111  *
1112  * This method is responsible for creating a #GdaSqlParser object specific to the SQL dialect used
1113  * by the database. See the PostgreSQL provider implementation for an example.
1114  */
1115 static GdaSqlParser *
gda_web_provider_create_parser(G_GNUC_UNUSED GdaServerProvider * provider,GdaConnection * cnc)1116 gda_web_provider_create_parser (G_GNUC_UNUSED GdaServerProvider *provider, GdaConnection *cnc)
1117 {
1118 	WebConnectionData *cdata = NULL;
1119 
1120 	if (cnc)
1121 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
1122 	if (!cdata)
1123 		return NULL;
1124 	if (cdata->reuseable && cdata->reuseable->operations->re_create_parser)
1125 		return cdata->reuseable->operations->re_create_parser (cdata->reuseable);
1126 	else
1127 		return NULL;
1128 }
1129 
1130 /*
1131  * GdaStatement to SQL request
1132  *
1133  * This method renders a #GdaStatement into its SQL representation.
1134  *
1135  * The implementation show here simply calls gda_statement_to_sql_extended() but the rendering
1136  * can be specialized to the database's SQL dialect, see the implementation of gda_statement_to_sql_extended()
1137  * and SQLite's specialized rendering for more details
1138  *
1139  * NOTE: This implementation MUST NOT call gda_statement_to_sql_extended() if it is
1140  *       the GdaServerProvider::statement_to_sql() virtual method's implementation
1141  */
1142 static gchar *
gda_web_provider_statement_to_sql(GdaServerProvider * provider,GdaConnection * cnc,GdaStatement * stmt,GdaSet * params,GdaStatementSqlFlag flags,GSList ** params_used,GError ** error)1143 gda_web_provider_statement_to_sql (GdaServerProvider *provider, GdaConnection *cnc,
1144 				   GdaStatement *stmt, GdaSet *params, GdaStatementSqlFlag flags,
1145 				   GSList **params_used, GError **error)
1146 {
1147 	WebConnectionData *cdata = NULL;
1148 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
1149 
1150 	if (cnc) {
1151 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1152 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1153 
1154 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
1155 	}
1156 	if (!cdata)
1157 		return gda_statement_to_sql_extended (stmt, cnc, params, flags, params_used, error);
1158 
1159 	/*TO_IMPLEMENT;*/
1160 	return gda_statement_to_sql_extended (stmt, cnc, params, flags, params_used, error);
1161 }
1162 
1163 static const gchar*
gtype_to_webtype(GType type)1164 gtype_to_webtype (GType type)
1165 {
1166 	if (type == G_TYPE_INT64)
1167                 return "integer";
1168         if (type == G_TYPE_UINT64)
1169                 return "integer";
1170         if (type == GDA_TYPE_BINARY)
1171                 return "text";
1172         if (type == GDA_TYPE_BLOB)
1173                 return "blob";
1174         if (type == G_TYPE_BOOLEAN)
1175                 return "boolean";
1176         if (type == G_TYPE_DATE)
1177                 return "date";
1178         if (type == G_TYPE_DOUBLE)
1179                 return "float";
1180         if (type == GDA_TYPE_GEOMETRIC_POINT)
1181                 return "text";
1182         if (type == G_TYPE_OBJECT)
1183                 return "text";
1184         if (type == G_TYPE_INT)
1185                 return "integer";
1186         if (type == GDA_TYPE_NUMERIC)
1187                 return "decimal";
1188         if (type == G_TYPE_FLOAT)
1189                 return "float";
1190         if (type == GDA_TYPE_SHORT)
1191 		return "integer";
1192         if (type == GDA_TYPE_USHORT)
1193                 return "integer";
1194         if (type == G_TYPE_STRING)
1195                 return "text";
1196         if (type == GDA_TYPE_TIME)
1197                 return "time";
1198         if (type == GDA_TYPE_TIMESTAMP)
1199                 return "timestamp";
1200         if (type == G_TYPE_CHAR)
1201                 return "integer";
1202         if (type == G_TYPE_UCHAR)
1203                 return "integer";
1204         if (type == G_TYPE_ULONG)
1205                 return "integer";
1206         if (type == G_TYPE_GTYPE)
1207                 return "text";
1208         if (type == G_TYPE_UINT)
1209                 return "integer";
1210         if (type == GDA_TYPE_NULL)
1211                 return "text";
1212         if (type == G_TYPE_INVALID)
1213                 return "text";
1214 
1215         return "text";
1216 }
1217 
1218 /*
1219  * Statement prepare request
1220  *
1221  * This methods "converts" @stmt into a prepared statement. A prepared statement is a notion
1222  * specific in its implementation details to the C API used here. If successfull, it must create
1223  * a new #GdaWebPStmt object and declare it to @cnc.
1224  */
1225 static gboolean
gda_web_provider_statement_prepare(GdaServerProvider * provider,GdaConnection * cnc,GdaStatement * stmt,GError ** error)1226 gda_web_provider_statement_prepare (GdaServerProvider *provider, GdaConnection *cnc,
1227 				    GdaStatement *stmt, GError **error)
1228 {
1229 	GdaWebPStmt *ps;
1230 	gboolean retval = FALSE;
1231 	WebConnectionData *cdata;
1232 
1233 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1234 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1235 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
1236 
1237 
1238 	/* fetch prepares stmt if already done */
1239 	ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
1240 	if (ps)
1241 		return TRUE;
1242 
1243 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
1244 	if (!cdata)
1245 		return FALSE;
1246 
1247 	/* render as SQL understood by the provider */
1248 	GdaSet *params = NULL;
1249 	gchar *sql;
1250 	GSList *used_params = NULL;
1251 	if (! gda_statement_get_parameters (stmt, &params, error))
1252                 return FALSE;
1253         sql = gda_web_provider_statement_to_sql (provider, cnc, stmt, params, GDA_STATEMENT_SQL_PARAMS_AS_UQMARK,
1254 						 &used_params, error);
1255         if (!sql)
1256 		goto out;
1257 
1258 	/* make a list of the parameter names used in the statement */
1259 	GSList *param_ids = NULL;
1260         if (used_params) {
1261                 GSList *list;
1262                 for (list = used_params; list; list = list->next) {
1263                         const gchar *cid;
1264                         cid = gda_holder_get_id (GDA_HOLDER (list->data));
1265                         if (cid) {
1266                                 param_ids = g_slist_append (param_ids, g_strdup (cid));
1267                                 /*g_print ("PREPARATION: param ID: %s\n", cid);*/
1268                         }
1269                         else {
1270                                 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
1271                                              "%s", _("Unnamed parameter is not allowed in prepared statements"));
1272                                 g_slist_foreach (param_ids, (GFunc) g_free, NULL);
1273                                 g_slist_free (param_ids);
1274                                 goto out;
1275                         }
1276                 }
1277         }
1278 
1279 	/* prepare XML command */
1280 	xmlDocPtr doc;
1281 	xmlNodePtr root, cmdnode, node;
1282 	gchar *token;
1283 	doc = xmlNewDoc (BAD_CAST "1.0");
1284 	root = xmlNewNode (NULL, BAD_CAST "request");
1285 	xmlDocSetRootElement (doc, root);
1286 	token = _gda_web_compute_token (cdata);
1287 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
1288 	g_free (token);
1289 	cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "PREPARE");
1290 	node = xmlNewTextChild (cmdnode, NULL, BAD_CAST "sql", BAD_CAST sql);
1291 	if ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) ||
1292 	    (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))
1293 		xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
1294 	else if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_UNKNOWN) {
1295 		if (! g_ascii_strncasecmp (sql, "select", 6) ||
1296 		    ! g_ascii_strncasecmp (sql, "pragma", 6) ||
1297 		    ! g_ascii_strncasecmp (sql, "show", 4) ||
1298 		    ! g_ascii_strncasecmp (sql, "describe", 8))
1299 			xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
1300 	}
1301 	if (param_ids) {
1302 		GSList *list;
1303 		xmlNodePtr argsnode;
1304 		argsnode = xmlNewChild (cmdnode, NULL, BAD_CAST "arguments", NULL);
1305 		for (list = used_params; list; list = list->next) {
1306 			node = xmlNewChild (argsnode, NULL, BAD_CAST "arg", NULL);
1307 			xmlSetProp (node, BAD_CAST "type",
1308 				    BAD_CAST gtype_to_webtype (gda_holder_get_g_type (GDA_HOLDER (list->data))));
1309 		}
1310 	}
1311 
1312 	/* send command */
1313 	xmlChar *cmde;
1314 	xmlDocPtr replydoc;
1315 	int size;
1316 	gchar status;
1317 
1318 	xmlDocDumpMemory (doc, &cmde, &size);
1319 	xmlFreeDoc (doc);
1320 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_PREPARE, (gchar*) cmde, cdata->key, &status);
1321 	xmlFree (cmde);
1322 
1323 	if (!replydoc) {
1324 		_gda_web_change_connection_to_closed (cnc, cdata);
1325 		goto out;
1326 	}
1327 	if (status != 'O') {
1328 		_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
1329 		xmlFreeDoc (replydoc);
1330 
1331 		if (status == 'C')
1332 			_gda_web_change_connection_to_closed (cnc, cdata);
1333 		goto out;
1334 	}
1335 
1336 	/* create a prepared statement object */
1337 	ps = NULL;
1338 	root = xmlDocGetRootElement (replydoc);
1339 	for (node = root->children; node; node = node->next) {
1340 		if (!strcmp ((gchar*) node->name, "preparehash")) {
1341 			xmlChar *contents;
1342 			contents = xmlNodeGetContent (node);
1343 			ps = gda_web_pstmt_new (cnc, (gchar*) contents);
1344 			xmlFree (contents);
1345 			break;
1346 		}
1347 	}
1348 	xmlFreeDoc (replydoc);
1349 
1350 	if (!ps)
1351 		goto out;
1352 
1353 	gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
1354 	_GDA_PSTMT (ps)->param_ids = param_ids;
1355 	_GDA_PSTMT (ps)->sql = sql;
1356 
1357 	gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
1358 	g_object_unref (ps);
1359 
1360 	retval = TRUE;
1361 
1362  out:
1363 	if (used_params)
1364                 g_slist_free (used_params);
1365         if (params)
1366                 g_object_unref (params);
1367 	return retval;
1368 }
1369 
1370 /*
1371  * Execute statement request
1372  *
1373  * Executes a statement. This method should do the following:
1374  *    - try to prepare the statement if not yet done
1375  *    - optionnally reset the prepared statement
1376  *    - bind the variables's values (which are in @params)
1377  *    - add a connection event to log the execution
1378  *    - execute the prepared statement
1379  *
1380  * If @stmt is an INSERT statement and @last_inserted_row is not NULL then additional actions must be taken to return the
1381  * actual inserted row
1382  */
1383 static GObject *
gda_web_provider_statement_execute(GdaServerProvider * provider,GdaConnection * cnc,GdaStatement * stmt,GdaSet * params,GdaStatementModelUsage model_usage,GType * col_types,GdaSet ** last_inserted_row,guint * task_id,GdaServerProviderExecCallback async_cb,gpointer cb_data,GError ** error)1384 gda_web_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
1385 				    GdaStatement *stmt, GdaSet *params,
1386 				    GdaStatementModelUsage model_usage,
1387 				    GType *col_types, GdaSet **last_inserted_row,
1388 				    guint *task_id,
1389 				    GdaServerProviderExecCallback async_cb, gpointer cb_data, GError **error)
1390 {
1391 	GdaWebPStmt *ps;
1392 	WebConnectionData *cdata;
1393 	gboolean allow_noparam;
1394         gboolean empty_rs = FALSE; /* TRUE when @allow_noparam is TRUE and there is a problem with @params
1395                                       => resulting data model will be empty (0 row) */
1396 
1397 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
1398 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
1399 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), NULL);
1400 
1401 	/* If asynchronous connection opening is not supported, then exit now */
1402 	if (async_cb) {
1403 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
1404 			     "%s", _("Provider does not support asynchronous statement execution"));
1405                 return NULL;
1406 	}
1407 
1408         allow_noparam = (model_usage & GDA_STATEMENT_MODEL_ALLOW_NOPARAM) &&
1409                 (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT);
1410 
1411         if (last_inserted_row)
1412                 *last_inserted_row = NULL;
1413 
1414 	/* Get private data */
1415 	cdata = (WebConnectionData*) gda_connection_internal_get_provider_data_error (cnc, error);
1416 	if (!cdata)
1417 		return NULL;
1418 
1419 
1420 	/* get/create new prepared statement */
1421 	ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
1422 	if (!ps) {
1423 		if (!gda_web_provider_statement_prepare (provider, cnc, stmt, error)) {
1424 			/* this case can appear for example if some variables are used in places
1425 			 * where the C API cannot allow them (for example if the variable is the table name
1426 			 * in a SELECT statement). The action here is to get the actual SQL code for @stmt,
1427 			 * and use that SQL instead of @stmt to create another GdaWebPStmt object.
1428 			 *
1429 			 * Don't call gda_connection_add_prepared_statement() with this new prepared statement
1430 			 * as it will be destroyed once used.
1431 			 */
1432 			return NULL;
1433 		}
1434 		else {
1435 			ps = (GdaWebPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
1436 			g_object_ref (ps);
1437 		}
1438 	}
1439 	else
1440 		g_object_ref (ps);
1441 	g_assert (ps);
1442 
1443 	/* prepare XML command */
1444 	xmlDocPtr doc;
1445 	xmlNodePtr root, cmdnode, node;
1446 	gchar *token;
1447 	doc = xmlNewDoc (BAD_CAST "1.0");
1448 	root = xmlNewNode (NULL, BAD_CAST "request");
1449 	xmlDocSetRootElement (doc, root);
1450 	token = _gda_web_compute_token (cdata);
1451 	xmlNewChild (root, NULL, BAD_CAST "token", BAD_CAST token);
1452 	g_free (token);
1453 	cmdnode = xmlNewChild (root, NULL, BAD_CAST "cmd", BAD_CAST "EXEC");
1454 	node = xmlNewTextChild (cmdnode, NULL, BAD_CAST "sql", BAD_CAST _GDA_PSTMT (ps)->sql);
1455 	if ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) ||
1456 	    (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))
1457 		xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
1458 	else if (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_UNKNOWN) {
1459 		if (! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "select", 6) ||
1460 		    ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "pragma", 6) ||
1461 		    ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "show", 4) ||
1462 		    ! g_ascii_strncasecmp (_GDA_PSTMT (ps)->sql, "describe", 8))
1463 			xmlSetProp (node, BAD_CAST "type", BAD_CAST "SELECT");
1464 	}
1465 	xmlNewChild (cmdnode, NULL, BAD_CAST "preparehash", BAD_CAST (ps->pstmt_hash));
1466 
1467 	/* bind statement's parameters */
1468 	GSList *list;
1469 	GdaConnectionEvent *event = NULL;
1470 	int i;
1471 	xmlNodePtr argsnode;
1472 	if (_GDA_PSTMT (ps)->param_ids)
1473 		argsnode = xmlNewChild (cmdnode, NULL, BAD_CAST "arguments", NULL);
1474 
1475 	for (i = 1, list = _GDA_PSTMT (ps)->param_ids; list; list = list->next, i++) {
1476 		const gchar *pname = (gchar *) list->data;
1477 		GdaHolder *h;
1478 
1479 		/* find requested parameter */
1480 		if (!params) {
1481 			event = gda_connection_point_available_event (cnc, GDA_CONNECTION_EVENT_ERROR);
1482 			gda_connection_event_set_description (event, _("Missing parameter(s) to execute query"));
1483 			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
1484 				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR,
1485 				     "%s", _("Missing parameter(s) to execute query"));
1486 			break;
1487 		}
1488 
1489 		h = gda_set_get_holder (params, pname);
1490 		if (!h) {
1491 			gchar *tmp = gda_alphanum_to_text (g_strdup (pname + 1));
1492 			if (tmp) {
1493 				h = gda_set_get_holder (params, tmp);
1494 				g_free (tmp);
1495 			}
1496 		}
1497 		if (!h) {
1498 			if (allow_noparam) {
1499                                 /* bind param to NULL */
1500 				node = xmlNewChild (argsnode, NULL, BAD_CAST "arg", NULL);
1501 				xmlSetProp (node, BAD_CAST "type", BAD_CAST "NULL");
1502                                 empty_rs = TRUE;
1503                                 continue;
1504 			}
1505 			else {
1506 
1507 				gchar *str;
1508 				str = g_strdup_printf (_("Missing parameter '%s' to execute query"), pname);
1509 				event = gda_connection_point_available_event (cnc, GDA_CONNECTION_EVENT_ERROR);
1510 				gda_connection_event_set_description (event, str);
1511 				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
1512 					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
1513 				g_free (str);
1514 				break;
1515                         }
1516 		}
1517 		if (!gda_holder_is_valid (h)) {
1518 			if (allow_noparam) {
1519                                 /* bind param to NULL */
1520 				xmlSetProp (node, BAD_CAST "type", BAD_CAST "NULL");
1521                                 empty_rs = TRUE;
1522                                 continue;
1523 			}
1524 			else {
1525 				gchar *str;
1526 				str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
1527 				event = gda_connection_point_available_event (cnc, GDA_CONNECTION_EVENT_ERROR);
1528 				gda_connection_event_set_description (event, str);
1529 				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
1530 					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
1531 				g_free (str);
1532 				break;
1533                         }
1534 		}
1535 		else if (gda_holder_value_is_default (h) && !gda_holder_get_value (h)) {
1536 			/* create a new GdaStatement to handle all default values and execute it instead
1537 			 * needs to be adapted to take into account how the database server handles default
1538 			 * values (some accept the DEFAULT keyword), changing the 3rd argument of the
1539 			 * gda_statement_rewrite_for_default_values() call
1540 			 */
1541 			GdaSqlStatement *sqlst;
1542 			GError *lerror = NULL;
1543 			sqlst = gda_statement_rewrite_for_default_values (stmt, params, TRUE, &lerror);
1544 			if (!sqlst) {
1545 				event = gda_connection_point_available_event (cnc,
1546 									      GDA_CONNECTION_EVENT_ERROR);
1547 				gda_connection_event_set_description (event, lerror && lerror->message ?
1548 								      lerror->message :
1549 								      _("Can't rewrite statement handle default values"));
1550 				g_propagate_error (error, lerror);
1551 				break;
1552 			}
1553 
1554 			GdaStatement *rstmt;
1555 			GObject *res;
1556 			rstmt = g_object_new (GDA_TYPE_STATEMENT, "structure", sqlst, NULL);
1557 			gda_sql_statement_free (sqlst);
1558 			g_object_unref (ps);
1559 			xmlFreeDoc (doc);
1560 			res = gda_web_provider_statement_execute (provider, cnc,
1561 								  rstmt, params,
1562 								  model_usage,
1563 								  col_types, last_inserted_row,
1564 								  task_id,
1565 								  async_cb, cb_data, error);
1566 			g_object_unref (rstmt);
1567 			return res;
1568 		}
1569 
1570 		/* actual binding using the C API, for parameter at position @i */
1571 		const GValue *value = gda_holder_get_value (h);
1572 		if (!value || gda_value_is_null (value)) {
1573 			GdaStatement *rstmt;
1574 			if (! gda_rewrite_statement_for_null_parameters (stmt, params, &rstmt, error)) {
1575 				gchar *tmp;
1576 				tmp = gda_value_stringify (value);
1577 				node = xmlNewTextChild (argsnode, NULL, BAD_CAST "arg", BAD_CAST tmp);
1578 				g_free (tmp);
1579 				xmlSetProp (node, BAD_CAST "type",
1580 					    BAD_CAST gtype_to_webtype (gda_holder_get_g_type (h)));
1581 			}
1582 			else if (!rstmt)
1583 				return NULL;
1584 			else {
1585 				xmlFreeDoc (doc);
1586 
1587 				/* The strategy here is to execute @rstmt using its prepared
1588 				 * statement, but with common data from @ps. Beware that
1589 				 * the @param_ids attribute needs to be retained (i.e. it must not
1590 				 * be the one copied from @ps) */
1591 				GObject *obj;
1592 				GdaWebPStmt *tps;
1593 				GdaPStmt *gtps;
1594 				GSList *prep_param_ids, *copied_param_ids;
1595 				if (!gda_web_provider_statement_prepare (provider, cnc,
1596 									 rstmt, error))
1597 					return NULL;
1598 				tps = (GdaWebPStmt *)
1599 					gda_connection_get_prepared_statement (cnc, rstmt);
1600 				gtps = (GdaPStmt *) tps;
1601 
1602 				/* keep @param_ids to avoid being cleared by gda_pstmt_copy_contents() */
1603 				prep_param_ids = gtps->param_ids;
1604 				gtps->param_ids = NULL;
1605 
1606 				/* actual copy */
1607 				gda_pstmt_copy_contents ((GdaPStmt *) ps, (GdaPStmt *) tps);
1608 
1609 				/* restore previous @param_ids */
1610 				copied_param_ids = gtps->param_ids;
1611 				gtps->param_ids = prep_param_ids;
1612 
1613 				/* execute */
1614 				obj = gda_web_provider_statement_execute (provider, cnc,
1615 									  rstmt, params,
1616 									  model_usage,
1617 									  col_types,
1618 									  last_inserted_row,
1619 									  task_id, async_cb,
1620 									  cb_data, error);
1621 				/* clear original @param_ids and restore copied one */
1622 				g_slist_foreach (prep_param_ids, (GFunc) g_free, NULL);
1623 				g_slist_free (prep_param_ids);
1624 
1625 				gtps->param_ids = copied_param_ids;
1626 
1627 				/*if (GDA_IS_DATA_MODEL (obj))
1628 				  gda_data_model_dump ((GdaDataModel*) obj, NULL);*/
1629 
1630 				g_object_unref (rstmt);
1631 				g_object_unref (ps);
1632 				return obj;
1633 			}
1634 		}
1635 		else {
1636 			gchar *tmp;
1637 			tmp = gda_value_stringify (value);
1638 			node = xmlNewTextChild (argsnode, NULL, BAD_CAST "arg", BAD_CAST tmp);
1639 			g_free (tmp);
1640 			xmlSetProp (node, BAD_CAST "type",
1641 				    BAD_CAST gtype_to_webtype (gda_holder_get_g_type (h)));
1642 		}
1643 	}
1644 
1645 	if (event) {
1646 		gda_connection_add_event (cnc, event);
1647 		g_object_unref (ps);
1648 		xmlFreeDoc (doc);
1649 		return NULL;
1650 	}
1651 
1652 	/* add a connection event for the execution */
1653 	event = gda_connection_point_available_event (cnc, GDA_CONNECTION_EVENT_COMMAND);
1654         gda_connection_event_set_description (event, _GDA_PSTMT (ps)->sql);
1655         gda_connection_add_event (cnc, event);
1656 
1657 	if (empty_rs) {
1658 		/* There are some missing parameters, so the SQL can't be executed but we still want
1659 		 * to execute something to get the columns correctly. A possibility is to actually
1660 		 * execute another SQL which is the code shown here.
1661 		 *
1662 		 * To adapt depending on the C API and its features */
1663 		GdaStatement *estmt;
1664                 gchar *esql;
1665                 estmt = gda_select_alter_select_for_empty (stmt, error);
1666                 if (!estmt) {
1667 			g_object_unref (ps);
1668                         return NULL;
1669 		}
1670                 esql = gda_statement_to_sql (estmt, NULL, error);
1671                 g_object_unref (estmt);
1672                 if (!esql) {
1673 			g_object_unref (ps);
1674                         return NULL;
1675 		}
1676 
1677 		/* Execute the 'esql' SQL code */
1678                 g_free (esql);
1679 
1680 		/* modify @doc */
1681 		TO_IMPLEMENT;
1682 	}
1683 
1684 	/* send command */
1685 	xmlChar *cmde;
1686 	xmlDocPtr replydoc;
1687 	int size;
1688 	gchar status;
1689 
1690 	xmlDocDumpMemory (doc, &cmde, &size);
1691 	xmlFreeDoc (doc);
1692 	replydoc = _gda_web_send_message_to_frontend (cnc, cdata, MESSAGE_EXEC, (gchar*) cmde, cdata->key, &status);
1693 	xmlFree (cmde);
1694 
1695 	if (!replydoc)
1696 		status = 'E';
1697 	if (status != 'O') {
1698 		if (replydoc) {
1699 			_gda_web_set_connection_error_from_xmldoc (cnc, replydoc, error);
1700 			xmlFreeDoc (replydoc);
1701 			if (status == 'C')
1702 				_gda_web_change_connection_to_closed (cnc, cdata);
1703 		}
1704 		else
1705 			_gda_web_change_connection_to_closed (cnc, cdata);
1706 		return NULL;
1707 	}
1708 
1709 	/* required: help @cnc keep some stats */
1710 	event = gda_connection_point_available_event (cnc, GDA_CONNECTION_EVENT_NOTICE);
1711 	gda_connection_event_set_description (event, "Command OK");
1712 	gda_connection_add_event (cnc, event);
1713 	gda_connection_internal_statement_executed (cnc, stmt, params, event);
1714 
1715 	root = xmlDocGetRootElement (replydoc);
1716 	GObject *retval = NULL;
1717 	for (node = root->children; node; node = node->next) {
1718 		if (!strcmp ((gchar*) node->name, "impacted_rows")) {
1719 			xmlChar *contents;
1720 			GdaSet *set = NULL;
1721 
1722 			contents = xmlNodeGetContent (node);
1723 			set = gda_set_new_inline (1, "IMPACTED_ROWS", G_TYPE_INT, atoi ((gchar*) contents));
1724 			xmlFree (contents);
1725 			retval = (GObject*) set;
1726 		}
1727 		else if (!strcmp ((gchar*) node->name, "gda_array")) {
1728 			GdaDataModel *data_model;
1729 			gda_mutex_lock (cdata->mutex);
1730 			data_model = gda_web_recordset_new (cnc, ps, params, model_usage,
1731 							    col_types, cdata->session_id, node, error);
1732 			gda_mutex_unlock (cdata->mutex);
1733 			retval = (GObject*) data_model;
1734 
1735 			if (! gda_web_recordset_store (GDA_WEB_RECORDSET (data_model), node, error)) {
1736 				g_object_unref (G_OBJECT (data_model));
1737 				retval = NULL;
1738 			}
1739 		}
1740 		else if (!strcmp ((gchar*) node->name, "preparehash")) {
1741 			xmlChar *contents;
1742 			contents = xmlNodeGetContent (node);
1743 			g_free (ps->pstmt_hash);
1744 			ps->pstmt_hash = g_strdup ((gchar*) contents);
1745 			xmlFree (contents);
1746 		}
1747 	}
1748 
1749 	xmlFreeDoc (replydoc);
1750 	g_object_unref (ps);
1751 	return retval;
1752 }
1753 
1754 /*
1755  * Rewrites a statement in case some parameters in @params are set to DEFAULT, for INSERT or UPDATE statements
1756  *
1757  * Removes any default value inserted or updated
1758  */
1759 static GdaSqlStatement *
gda_web_statement_rewrite(GdaServerProvider * provider,GdaConnection * cnc,GdaStatement * stmt,GdaSet * params,GError ** error)1760 gda_web_statement_rewrite (GdaServerProvider *provider, GdaConnection *cnc,
1761 			   GdaStatement *stmt, GdaSet *params, GError **error)
1762 {
1763 	if (cnc) {
1764 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
1765 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
1766 	}
1767 	return gda_statement_rewrite_for_default_values (stmt, params, TRUE, error);
1768 }
1769 
1770 static gchar *
gda_web_identifier_quote(GdaServerProvider * provider,GdaConnection * cnc,const gchar * id,gboolean for_meta_store,gboolean force_quotes)1771 gda_web_identifier_quote (GdaServerProvider *provider, GdaConnection *cnc,
1772 			  const gchar *id,
1773 			  gboolean for_meta_store, gboolean force_quotes)
1774 {
1775 	WebConnectionData *cdata = NULL;
1776 
1777 	if (cnc) {
1778 		g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
1779 		g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
1780 
1781 		cdata = (WebConnectionData*) gda_connection_internal_get_provider_data (cnc);
1782 	}
1783 	if (!cdata)
1784 		return gda_sql_identifier_quote (id, NULL, NULL, for_meta_store, force_quotes);
1785 
1786 	/*TO_IMPLEMENT;*/
1787 	return gda_sql_identifier_quote (id, NULL, NULL, for_meta_store, force_quotes);
1788 }
1789