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 (®istering);
254 if (type == 0)
255 type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaWebProvider", &info, 0);
256 g_mutex_unlock (®istering);
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, ¶ms, 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