1 /*
2 * Copyright (C) 2008 - 2011 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20
21 #include <libgda/libgda.h>
22 #include <sql-parser/gda-sql-parser.h>
23 #include "html.h"
24 #include <libgda/gda-enum-types.h>
25 #include <libgda/sqlite/virtual/gda-virtual-provider.h>
26
27 /* options */
28 gboolean ask_pass = FALSE;
29 gchar *outfile = NULL;
30
31 static GOptionEntry entries[] = {
32 { "no-password-ask", 'p', 0, G_OPTION_ARG_NONE, &ask_pass, "Don't ast for a password when it is empty", NULL },
33 { "output-file", 'o', 0, G_OPTION_ARG_STRING, &outfile, "Output file", "output file"},
34 { NULL, 0, 0, 0, NULL, NULL, NULL }
35 };
36
37 HtmlConfig *config;
38
39 static GdaConnection *open_connection (const gchar *cnc_string, GError **error);
40 static gboolean report_provider_status (GdaServerProvider *prov, GdaConnection *cnc);
41
42 int
main(int argc,char * argv[])43 main (int argc, char *argv[])
44 {
45 GOptionContext *context;
46 GError *error = NULL;
47 int exit_status = EXIT_SUCCESS;
48 GSList *list, *cnc_list = NULL;
49
50 context = g_option_context_new ("[DSN|connection string]...");
51 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
52 if (!g_option_context_parse (context, &argc, &argv, &error)) {
53 g_print ("Can't parse arguments: %s\n", error->message);
54 exit_status = EXIT_FAILURE;
55 goto cleanup;
56 }
57 g_option_context_free (context);
58 gda_init ();
59 ask_pass = !ask_pass;
60
61 config = g_new0 (HtmlConfig, 1);
62 html_init_config (HTML_CONFIG (config));
63 config->index = html_file_new (HTML_CONFIG (config),
64 "index.html", "Providers status");
65 config->dir = g_strdup (".");
66
67 /* parse command line arguments for connections */
68 if (argc > 1) {
69 gint i;
70 for (i = 1; i < argc; i++) {
71 /* open connection */
72 GdaConnection *cnc;
73 cnc = open_connection (argv[i], &error);
74 if (!cnc) {
75 g_print ("Can't open connection to '%s': %s\n", argv[i],
76 error && error->message ? error->message : "No detail");
77 exit_status = EXIT_FAILURE;
78 goto cleanup;
79 }
80 cnc_list = g_slist_append (cnc_list, cnc);
81 }
82 }
83 else {
84 if (getenv ("GDA_SQL_CNC")) {
85 GdaConnection *cnc;
86 cnc = open_connection (getenv ("GDA_SQL_CNC"), &error);
87 if (!cnc) {
88 g_print ("Can't open connection defined by GDA_SQL_CNC: %s\n",
89 error && error->message ? error->message : "No detail");
90 exit_status = EXIT_FAILURE;
91 goto cleanup;
92 }
93 cnc_list = g_slist_append (cnc_list, cnc);
94 }
95 else {
96 /* report status for all providers */
97 GdaDataModel *providers;
98 gint i, nb;
99
100 providers = gda_config_list_providers ();
101 nb = gda_data_model_get_n_rows (providers);
102 for (i = 0; i < nb; i++) {
103 GdaServerProvider *prov = NULL;
104 const gchar *pname;
105 const GValue *cvalue;
106
107 cvalue = gda_data_model_get_value_at (providers, 0, i, &error);
108 if (!cvalue)
109 g_error ("Can't load next provider: %s\n",
110 error && error->message ? error->message : "No detail");
111 pname = g_value_get_string (cvalue);
112 prov = gda_config_get_provider (pname, &error);
113 if (!prov)
114 g_error ("Can't load the '%s' provider: %s\n", pname,
115 error && error->message ? error->message : "No detail");
116 if (!report_provider_status (prov, NULL)) {
117 exit_status = EXIT_FAILURE;
118 goto cleanup;
119 }
120 }
121 g_object_unref (providers);
122 }
123 }
124
125 /* report provider's status for all the connections */
126 for (list = cnc_list; list; list = list->next) {
127 if (!report_provider_status (NULL, GDA_CONNECTION (list->data))) {
128 exit_status = EXIT_FAILURE;
129 goto cleanup;
130
131 }
132 }
133
134 g_slist_foreach (HTML_CONFIG (config)->all_files, (GFunc) html_file_write, config);
135
136 /* cleanups */
137 cleanup:
138 g_slist_foreach (cnc_list, (GFunc) g_object_unref, NULL);
139 g_slist_free (cnc_list);
140
141 return exit_status;
142 }
143
144
145 /*
146 * Open a connection
147 */
148 static GdaConnection*
open_connection(const gchar * cnc_string,GError ** error)149 open_connection (const gchar *cnc_string, GError **error)
150 {
151 GdaConnection *cnc = NULL;
152
153 GdaDsnInfo *info;
154 gchar *user, *pass, *real_cnc, *real_provider, *real_auth_string = NULL;
155 gda_connection_string_split (cnc_string, &real_cnc, &real_provider, &user, &pass);
156 if (!real_cnc) {
157 g_free (user);
158 g_free (pass);
159 g_free (real_provider);
160 g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR,
161 "Malformed connection string '%s'", cnc_string);
162 return NULL;
163 }
164
165 if (ask_pass) {
166 if (user && !*user) {
167 gchar buf[80];
168 g_print ("\tUsername for '%s': ", cnc_string);
169 if (scanf ("%80s", buf) == -1) {
170 g_free (real_cnc);
171 g_free (user);
172 g_free (pass);
173 g_free (real_provider);
174 g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR,
175 "No username for '%s'", cnc_string);
176 return NULL;
177 }
178 g_free (user);
179 user = g_strdup (buf);
180 }
181 if (pass && !*pass) {
182 gchar buf[80];
183 g_print ("\tPassword for '%s': ", cnc_string);
184 if (scanf ("%80s", buf) == -1) {
185 g_free (real_cnc);
186 g_free (user);
187 g_free (pass);
188 g_free (real_provider);
189 g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR,
190 "No password for '%s'", cnc_string);
191 return NULL;
192 }
193 g_free (pass);
194 pass = g_strdup (buf);
195 }
196 if (user || pass) {
197 gchar *s1;
198 s1 = gda_rfc1738_encode (user);
199 if (pass) {
200 gchar *s2;
201 s2 = gda_rfc1738_encode (pass);
202 real_auth_string = g_strdup_printf ("USERNAME=%s;PASSWORD=%s", s1, s2);
203 g_free (s2);
204 }
205 else
206 real_auth_string = g_strdup_printf ("USERNAME=%s", s1);
207 g_free (s1);
208 }
209 }
210
211 info = gda_config_get_dsn_info (real_cnc);
212 if (info && !real_provider)
213 cnc = gda_connection_open_from_dsn (cnc_string, real_auth_string, 0, error);
214 else
215 cnc = gda_connection_open_from_string (NULL, cnc_string, real_auth_string, 0, error);
216
217 g_free (real_cnc);
218 g_free (user);
219 g_free (pass);
220 g_free (real_provider);
221 g_free (real_auth_string);
222
223 return cnc;
224 }
225
226 static gboolean
report_provider_status(GdaServerProvider * prov,GdaConnection * cnc)227 report_provider_status (GdaServerProvider *prov, GdaConnection *cnc)
228 {
229 gchar *header_str;
230 HtmlFile *file = config->index;
231 gboolean is_virt;
232
233 typedef void (*AFunc) (void);
234 typedef struct {
235 const gchar *name;
236 gboolean should_be;
237 void (*func) (void);
238 } ProvFunc;
239 GdaServerProviderClass *pclass;
240
241 if (prov && cnc && (prov != gda_connection_get_provider (cnc)))
242 /* ignoring connection as it has a different provider */
243 return TRUE;
244 g_assert (prov || cnc);
245
246 /* section */
247 if (cnc)
248 header_str = g_strdup_printf ("Report for connection '%s'", gda_connection_get_cnc_string (cnc));
249 else
250 header_str = g_strdup_printf ("Report for '%s' provider", gda_server_provider_get_name (prov));
251
252 /* provider info */
253 if (!prov)
254 prov = gda_connection_get_provider (cnc);
255 is_virt = GDA_IS_VIRTUAL_PROVIDER (prov);
256 pclass = (GdaServerProviderClass*) G_OBJECT_GET_CLASS (prov);
257 ProvFunc fa[] = {
258 {"get_name", TRUE, (AFunc) pclass->get_name},
259 {"get_version", TRUE, (AFunc) pclass->get_version},
260 {"get_server_version", TRUE, (AFunc) pclass->get_server_version},
261 {"supports_feature", TRUE, (AFunc) pclass->supports_feature},
262 {"get_data_handler", TRUE, (AFunc) pclass->get_data_handler},
263 {"get_def_dbms_type", TRUE, (AFunc) pclass->get_def_dbms_type},
264 {"escape_string", TRUE, (AFunc) pclass->escape_string},
265 {"unescape_string", TRUE, (AFunc) pclass->unescape_string},
266 {"open_connection", TRUE, (AFunc) pclass->open_connection},
267 {"close_connection", TRUE, (AFunc) pclass->close_connection},
268 {"get_database", TRUE, (AFunc) pclass->get_database},
269 {"supports_operation", is_virt ? FALSE : TRUE, (AFunc) pclass->supports_operation},
270 {"create_operation", FALSE, (AFunc) pclass->create_operation},
271 {"render_operation", FALSE, (AFunc) pclass->render_operation},
272 {"perform_operation", FALSE, (AFunc) pclass->perform_operation},
273 {"begin_transaction", FALSE, (AFunc) pclass->begin_transaction},
274 {"commit_transaction", FALSE, (AFunc) pclass->commit_transaction},
275 {"rollback_transaction", FALSE, (AFunc) pclass->rollback_transaction},
276 {"add_savepoint", FALSE, (AFunc) pclass->add_savepoint},
277 {"rollback_savepoint", FALSE, (AFunc) pclass->rollback_savepoint},
278 {"delete_savepoint", FALSE, (AFunc) pclass->delete_savepoint},
279 {"create_parser", FALSE, (AFunc) pclass->create_parser},
280 {"statement_to_sql", TRUE, (AFunc) pclass->statement_to_sql},
281 {"statement_prepare", TRUE, (AFunc) pclass->statement_prepare},
282 {"statement_execute", TRUE, (AFunc) pclass->statement_execute},
283 {"identifier_quote", TRUE, (AFunc) pclass->identifier_quote}
284 };
285
286 ProvFunc md[] = {
287 {"_info", TRUE, (AFunc) pclass->meta_funcs._info},
288 {"_btypes", TRUE, (AFunc) pclass->meta_funcs._btypes},
289 {"_udt", TRUE, (AFunc) pclass->meta_funcs._udt},
290 {"udt", TRUE, (AFunc) pclass->meta_funcs.udt},
291 {"_udt_cols", TRUE, (AFunc) pclass->meta_funcs._udt_cols},
292 {"udt_cols", TRUE, (AFunc) pclass->meta_funcs.udt_cols},
293 {"_enums", TRUE, (AFunc) pclass->meta_funcs._enums},
294 {"enums", TRUE, (AFunc) pclass->meta_funcs.enums},
295 {"_domains", TRUE, (AFunc) pclass->meta_funcs._domains},
296 {"domains", TRUE, (AFunc) pclass->meta_funcs.domains},
297 {"_constraints_dom", TRUE, (AFunc) pclass->meta_funcs._constraints_dom},
298 {"constraints_dom", TRUE, (AFunc) pclass->meta_funcs.constraints_dom},
299 {"_el_types", TRUE, (AFunc) pclass->meta_funcs._el_types},
300 {"el_types", TRUE, (AFunc) pclass->meta_funcs.el_types},
301 {"_collations", TRUE, (AFunc) pclass->meta_funcs._collations},
302 {"collations", TRUE, (AFunc) pclass->meta_funcs.collations},
303 {"_character_sets", TRUE, (AFunc) pclass->meta_funcs._character_sets},
304 {"character_sets", TRUE, (AFunc) pclass->meta_funcs.character_sets},
305 {"_schemata", TRUE, (AFunc) pclass->meta_funcs._schemata},
306 {"schemata", TRUE, (AFunc) pclass->meta_funcs.schemata},
307 {"_tables_views", TRUE, (AFunc) pclass->meta_funcs._tables_views},
308 {"tables_views", TRUE, (AFunc) pclass->meta_funcs.tables_views},
309 {"_columns", TRUE, (AFunc) pclass->meta_funcs._columns},
310 {"columns", TRUE, (AFunc) pclass->meta_funcs.columns},
311 {"_view_cols", TRUE, (AFunc) pclass->meta_funcs._view_cols},
312 {"view_cols", TRUE, (AFunc) pclass->meta_funcs.view_cols},
313 {"_constraints_tab", TRUE, (AFunc) pclass->meta_funcs._constraints_tab},
314 {"constraints_tab", TRUE, (AFunc) pclass->meta_funcs.constraints_tab},
315 {"_constraints_ref", TRUE, (AFunc) pclass->meta_funcs._constraints_ref},
316 {"constraints_ref", TRUE, (AFunc) pclass->meta_funcs.constraints_ref},
317 {"_key_columns", TRUE, (AFunc) pclass->meta_funcs._key_columns},
318 {"key_columns", TRUE, (AFunc) pclass->meta_funcs.key_columns},
319 {"_check_columns", TRUE, (AFunc) pclass->meta_funcs._check_columns},
320 {"check_columns", TRUE, (AFunc) pclass->meta_funcs.check_columns},
321 {"_triggers", TRUE, (AFunc) pclass->meta_funcs._triggers},
322 {"triggers", TRUE, (AFunc) pclass->meta_funcs.triggers},
323 {"_routines", TRUE, (AFunc) pclass->meta_funcs._routines},
324 {"routines", TRUE, (AFunc) pclass->meta_funcs.routines},
325 {"_routine_col", TRUE, (AFunc) pclass->meta_funcs._routine_col},
326 {"routine_col", TRUE, (AFunc) pclass->meta_funcs.routine_col},
327 {"_routine_par", TRUE, (AFunc) pclass->meta_funcs._routine_par},
328 {"routine_par", TRUE, (AFunc) pclass->meta_funcs.routine_par},
329 };
330 gboolean has_xa = gda_server_provider_supports_feature (prov, cnc,
331 GDA_CONNECTION_FEATURE_XA_TRANSACTIONS);
332
333
334 xmlNodePtr table, tr, td, span;
335 GdaSqlParser *parser;
336 GString *string;
337 gsize i;
338 GdaProviderInfo *pinfo;
339
340 pinfo = gda_config_get_provider_info (gda_server_provider_get_name (prov));
341 g_assert (pinfo);
342
343 table = xmlNewChild (file->body, NULL, BAD_CAST "table", NULL);
344 xmlSetProp (table, BAD_CAST "width", BAD_CAST "100%");
345 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
346 td = xmlNewTextChild (tr, NULL, BAD_CAST "th", BAD_CAST header_str);
347 xmlSetProp (td, BAD_CAST "colspan", BAD_CAST "4");
348
349 /* line 1 */
350 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
351 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Provider's name:");
352 td = xmlNewTextChild (tr, NULL, BAD_CAST "td", BAD_CAST gda_server_provider_get_name (prov));
353 xmlSetProp (td, BAD_CAST "width", (xmlChar*) "35%");
354 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Provider is virtual:");
355 td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST (is_virt ? "Yes (uses the SQLite engine)" : "No"));
356 xmlSetProp (td, BAD_CAST "width", (xmlChar*) "35%");
357
358 /* line 2 */
359 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
360 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Provider's version:");
361 xmlNewTextChild (tr, NULL, BAD_CAST "td", BAD_CAST gda_server_provider_get_version (prov));
362 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Provider's server version:");
363 xmlNewTextChild (tr, NULL, BAD_CAST "td",
364 BAD_CAST (cnc ? gda_server_provider_get_server_version (prov, cnc) : "(non connected)"));
365
366 /* line 3 */
367 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
368 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Provider's description:");
369 xmlNewTextChild (tr, NULL, BAD_CAST "td", BAD_CAST pinfo->description);
370 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Filename:");
371 xmlNewTextChild (tr, NULL, BAD_CAST "td", BAD_CAST pinfo->location);
372
373 /* line 4 */
374 parser = gda_server_provider_create_parser (prov, cnc);
375 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
376 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Creates its own SQL parser:");
377 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST (parser ? "Yes" : "No"));
378 if (parser)
379 g_object_unref (parser);
380 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Non implemented base methods:");
381 span = NULL;
382 td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
383 for (i = 0; i < sizeof (fa) / sizeof (ProvFunc); i++) {
384 gchar *str;
385 ProvFunc *pf = &(fa[i]);
386
387 if (pf->func)
388 continue;
389
390 if (span)
391 str = g_strdup_printf (", %s()", pf->name);
392 else
393 str = g_strdup_printf ("%s()", pf->name);
394 span = xmlNewTextChild (td, NULL, BAD_CAST "span", BAD_CAST str);
395 g_free (str);
396 if (pf->should_be)
397 xmlSetProp (span, BAD_CAST "class", BAD_CAST "error");
398 }
399 if (!span)
400 xmlNodeSetContent (td, BAD_CAST "---");
401
402 /* line 5 */
403 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
404 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Non implemented meta data methods:");
405 span = NULL;
406 td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
407 for (i = 0; i < sizeof (md) / sizeof (ProvFunc); i++) {
408 gchar *str;
409 ProvFunc *pf = &(md[i]);
410
411 if (pf->func)
412 continue;
413
414 if (span)
415 str = g_strdup_printf (", %s()", pf->name);
416 else
417 str = g_strdup_printf ("%s()", pf->name);
418 span = xmlNewTextChild (td, NULL, BAD_CAST "span", BAD_CAST str);
419 g_free (str);
420 if (pf->should_be)
421 xmlSetProp (span, BAD_CAST "class", BAD_CAST "error");
422 }
423 if (!span)
424 xmlNodeSetContent (td, BAD_CAST "---");
425
426 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Non implemented XA transactions:");
427 if (pclass->xa_funcs) {
428 if (!has_xa) {
429 td = xmlNewChild (tr, NULL, BAD_CAST "td",
430 BAD_CAST "The provider has the 'xa_funcs' part but "
431 "reports that distributed transactions are "
432 "not supported.");
433 xmlSetProp (td, BAD_CAST "class", BAD_CAST "warning");
434 }
435 else {
436 ProvFunc dt[] = {
437 {"xa_start", TRUE, (AFunc) pclass->xa_funcs->xa_start},
438 {"xa_end", FALSE, (AFunc) pclass->xa_funcs->xa_end},
439 {"xa_prepare", TRUE, (AFunc) pclass->xa_funcs->xa_prepare},
440 {"xa_commit", TRUE, (AFunc) pclass->xa_funcs->xa_commit},
441 {"xa_rollback", TRUE, (AFunc) pclass->xa_funcs->xa_rollback},
442 {"xa_recover", TRUE, (AFunc) pclass->xa_funcs->xa_recover},
443 };
444 span = NULL;
445 td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
446 for (i = 0; i < sizeof (dt) / sizeof (ProvFunc); i++) {
447 gchar *str;
448 ProvFunc *pf = &(dt[i]);
449
450 if (pf->func)
451 continue;
452
453 if (span)
454 str = g_strdup_printf (", %s()", pf->name);
455 else
456 str = g_strdup_printf ("%s()", pf->name);
457 span = xmlNewTextChild (td, NULL, BAD_CAST "span", BAD_CAST str);
458 g_free (str);
459 if (pf->should_be)
460 xmlSetProp (span, BAD_CAST "class", BAD_CAST "error");
461 }
462 if (!span)
463 xmlNodeSetContent (td, BAD_CAST "---");
464 }
465 }
466 else {
467 if (has_xa) {
468 td = xmlNewTextChild (tr, NULL, BAD_CAST "td",
469 BAD_CAST "The provider does not have the 'xa_funcs' part but "
470 "reports that distributed transactions are "
471 "supported.");
472 xmlSetProp (td, BAD_CAST "class", BAD_CAST "warning");
473 }
474 else
475 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "---");
476 }
477
478 /* line 6 */
479 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
480 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Connection's parameters:");
481 if (pinfo->dsn_params && pinfo->dsn_params->holders) {
482 GSList *list;
483 td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
484 for (list = pinfo->dsn_params->holders; list; list = list->next) {
485 gchar *str, *descr;
486 GdaHolder *holder = GDA_HOLDER (list->data);
487 g_object_get (G_OBJECT (holder), "description", &descr, NULL);
488 if (descr)
489 str = g_strdup_printf ("%s: %s", gda_holder_get_id (holder), descr);
490 else
491 str = g_strdup (gda_holder_get_id (holder));
492 g_free (descr);
493 xmlNewTextChild (td, NULL, BAD_CAST "div", BAD_CAST str);
494 g_free (str);
495 }
496 }
497 else {
498 td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "None provided");
499 xmlSetProp (td, BAD_CAST "class", BAD_CAST "error");
500 }
501 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Authentication's parameters:");
502 if (pinfo->auth_params) {
503 GSList *list;
504 if (pinfo->auth_params->holders) {
505 td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
506 for (list = pinfo->auth_params->holders; list; list = list->next) {
507 gchar *str, *descr;
508 GdaHolder *holder = GDA_HOLDER (list->data);
509 g_object_get (G_OBJECT (holder), "description", &descr, NULL);
510 if (descr)
511 str = g_strdup_printf ("%s: %s", gda_holder_get_id (holder), descr);
512 else
513 str = g_strdup (gda_holder_get_id (holder));
514 g_free (descr);
515 xmlNewTextChild (td, NULL, BAD_CAST "div", BAD_CAST str);
516 g_free (str);
517 }
518 }
519 else
520 td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "None required");
521 }
522 else {
523 td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "None provided");
524 xmlSetProp (td, BAD_CAST "class", BAD_CAST "error");
525 }
526
527 /* line 7 */
528 GdaConnectionFeature f;
529 string = NULL;
530 tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
531 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Supported features:");
532 for (f = 0; f < GDA_CONNECTION_FEATURE_LAST; f++) {
533 if (gda_server_provider_supports_feature (prov, cnc, f)) {
534 GEnumValue *ev;
535
536 ev = g_enum_get_value ((GEnumClass *) g_type_class_ref (GDA_TYPE_CONNECTION_FEATURE), f);
537 if (!string)
538 string = g_string_new (ev->value_name);
539 else
540 g_string_append_printf (string, ", %s", ev->value_name);
541 }
542 }
543 if (string) {
544 xmlNewTextChild (tr, NULL, BAD_CAST "td", BAD_CAST string->str);
545 g_string_free (string, TRUE);
546 }
547 else
548 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "---");
549
550 string = NULL;
551 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "Unsupported features:");
552 for (f = 0; f < GDA_CONNECTION_FEATURE_LAST; f++) {
553 if (!gda_server_provider_supports_feature (prov, cnc, f)) {
554 GEnumValue *ev;
555
556 ev = g_enum_get_value ((GEnumClass *) g_type_class_ref (GDA_TYPE_CONNECTION_FEATURE), f);
557 if (!string)
558 string = g_string_new (ev->value_name);
559 else
560 g_string_append_printf (string, ", %s", ev->value_name);
561 }
562 }
563 if (string) {
564 xmlNewTextChild (tr, NULL, BAD_CAST "td", BAD_CAST string->str);
565 g_string_free (string, TRUE);
566 }
567 else
568 xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST "---");
569
570 g_free (header_str);
571
572 return TRUE;
573 }
574