1 /*
2 * Copyright (C) 2008 Murray Cumming <murrayc@murrayc.com>
3 * Copyright (C) 2008 - 2011 Vivien Malerba <malerba@gnome-db.org>
4 * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
5 * Copyright (C) 2010 David King <davidk@openismus.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 #include <stdlib.h>
22 #include <string.h>
23 #include "test-cnc-utils.h"
24 #include "test-errors.h"
25 #include "gda-ddl-creator.h"
26 #include <libgda/gda-server-provider-extra.h>
27 #include <libgda/sql-parser/gda-sql-parser.h>
28 #include <libgda/sql-parser/gda-sql-statement.h>
29 #include <unistd.h>
30 #include <libgda/gda-debug-macros.h>
31
32 #define CHECK_EXTRA_INFO
33 /*#undef CHECK_EXTRA_INFO*/
34
35 #define CREATE_FILES 1
36
37 /*
38 *
39 * Connection SETUP
40 *
41 */
42 typedef struct {
43 GdaQuarkList *ql;
44 GString *string;
45 gchar *requested_db_name;
46 } Data1;
47
48 static void
db_create_quark_foreach_func(gchar * name,gchar * value,GdaServerOperation * op)49 db_create_quark_foreach_func (gchar *name, gchar *value, GdaServerOperation *op)
50 {
51 gda_server_operation_set_value_at (op, value, NULL, "/SERVER_CNX_P/%s", name);
52 }
53
54 static void
cnc_quark_foreach_func(gchar * name,gchar * value,Data1 * data)55 cnc_quark_foreach_func (gchar *name, gchar *value, Data1 *data)
56 {
57 if (!strcmp (name, "DB_NAME")) {
58 data->requested_db_name = g_strdup (value);
59 return;
60 }
61
62 if (data->ql) {
63 if (!gda_quark_list_find (data->ql, name)) {
64 if (*(data->string->str) != 0)
65 g_string_append_c (data->string, ';');
66 g_string_append_printf (data->string, "%s=%s", name, value);
67 }
68 }
69 else {
70 if (*(data->string->str) != 0)
71 g_string_append_c (data->string, ';');
72 g_string_append_printf (data->string, "%s=%s", name, value);
73 }
74 }
75
76 static gchar *
prov_name_upcase(const gchar * prov_name)77 prov_name_upcase (const gchar *prov_name)
78 {
79 gchar *str, *ptr;
80
81 str = g_ascii_strup (prov_name, -1);
82 for (ptr = str; *ptr; ptr++) {
83 if (! g_ascii_isalnum (*ptr))
84 *ptr = '_';
85 }
86
87 return str;
88 }
89
90 GdaConnection *
test_cnc_open_connection(const gchar * provider,const gchar * dbname,GError ** error)91 test_cnc_open_connection (const gchar *provider, const gchar *dbname, GError **error)
92 {
93 GdaConnection *cnc = NULL;
94 gchar *str, *upname;
95 const gchar *cnc_params;
96 GdaProviderInfo *prov_info;
97 GdaQuarkList *db_quark_list = NULL, *cnc_quark_list = NULL;
98 gboolean db_created = FALSE;
99
100 g_return_val_if_fail (dbname && *dbname, NULL);
101
102 prov_info = gda_config_get_provider_info (provider);
103 if (!prov_info) {
104 g_set_error (error, TEST_ERROR, TEST_ERROR_GENERIC,
105 "Provider '%s' not found", provider);
106 return NULL;
107 }
108
109 /* open connection to database */
110 upname = prov_name_upcase (prov_info->id);
111 str = g_strdup_printf ("%s_CNC_PARAMS", upname);
112 cnc_params = getenv (str);
113 g_free (str);
114 if (cnc_params)
115 cnc_quark_list = gda_quark_list_new_from_string (cnc_params);
116
117 if (db_quark_list || cnc_quark_list) {
118 Data1 data;
119
120 data.string = g_string_new ("");
121 data.ql = NULL;
122 data.requested_db_name = NULL;
123
124 if (db_quark_list)
125 gda_quark_list_foreach (db_quark_list, (GHFunc) cnc_quark_foreach_func, &data);
126 data.ql = db_quark_list;
127 if (cnc_quark_list)
128 gda_quark_list_foreach (cnc_quark_list, (GHFunc) cnc_quark_foreach_func, &data);
129
130 if (*(data.string->str) != 0)
131 g_string_append_c (data.string, ';');
132 g_string_append_printf (data.string, "DB_NAME=%s",
133 data.requested_db_name ? data.requested_db_name : dbname);
134 g_print ("Open connection string: %s\n", data.string->str);
135
136 gchar *auth_string = NULL;
137 GSList *current = prov_info->auth_params->holders;
138 while (current) {
139 GdaHolder *holder = (GdaHolder *) current->data;
140
141 const gchar *id = gda_holder_get_id (holder);
142 const gchar *env = NULL;
143 if (g_strrstr (id, "USER") != NULL) {
144 str = g_strdup_printf ("%s_USER", upname);
145 env = getenv (str);
146 g_free (str);
147 } else if (g_strrstr (id, "PASS") != NULL) {
148 str = g_strdup_printf ("%s_PASS", upname);
149 env = getenv (str);
150 g_free (str);
151 }
152
153 if (env) {
154 str = g_strdup_printf ("%s=%s;", id, env);
155
156 gchar *tmp = auth_string;
157 auth_string = g_strconcat (auth_string, str, NULL);
158 g_free (str);
159 g_free (tmp);
160 }
161
162 current = g_slist_next (current);
163 }
164
165 cnc = gda_connection_open_from_string (prov_info->id, data.string->str, auth_string,
166 GDA_CONNECTION_OPTIONS_NONE, error);
167 g_free (auth_string);
168 g_string_free (data.string, TRUE);
169 }
170
171 if (db_quark_list)
172 gda_quark_list_free (db_quark_list);
173 if (cnc_quark_list)
174 gda_quark_list_free (cnc_quark_list);
175
176 if (!cnc_params)
177 g_set_error (error, TEST_ERROR, TEST_ERROR_GENERIC,
178 "Connection parameters not specified, test not executed (define %s_CNC_PARAMS or %s_DBCREATE_PARAMS to create a test DB)\n", upname, upname);
179 g_free (upname);
180
181 return cnc;
182 }
183
184 /*
185 * Set up a connection.
186 *
187 * Optionnally the database can be created if the <upper_case_provider_name>_DBCREATE_PARAMS
188 * environment variable exists. Examples are:
189 * MYSQL_DBCREATE_PARAMS "HOST=localhost"
190 * POSTGRESQL_DBCREATE_PARAMS "HOST=localhost;PORT=5432"
191 * SQLITE_DBCREATE_PARAMS "DB_DIR=."
192 * BERKELEY_DB_CNC_PARAMS "DB_NAME=gda_check_bdb.db"
193 *
194 * The connection is opened if the <upper_case_provider_name>_CNC_PARAMS environment variable exists.
195 * For example:
196 * MSACCESS_CNC_PARAMS "DB_DIR=/home/me/libgda/tests/providers;DB_NAME=gda_check_db"
197 * ORACLE_CNC_PARAMS TNSNAME=//127.0.0.1
198 *
199 *
200 * If the <upper_case_provider_name>_DBCREATE_PARAMS is supplied, then its contents can be used
201 * to complement the <upper_case_provider_name>_CNC_PARAMS.
202 *
203 * Returns: a GdaConnection if no error occurred
204 */
205 GdaConnection *
test_cnc_setup_connection(const gchar * provider,const gchar * dbname,GError ** error)206 test_cnc_setup_connection (const gchar *provider, const gchar *dbname, GError **error)
207 {
208 GdaConnection *cnc = NULL;
209 gchar *str, *upname;
210 const gchar *db_params, *cnc_params;
211 GdaProviderInfo *prov_info;
212 GdaQuarkList *db_quark_list = NULL, *cnc_quark_list = NULL;
213 gboolean db_created = FALSE;
214
215 g_return_val_if_fail (dbname && *dbname, NULL);
216
217 prov_info = gda_config_get_provider_info (provider);
218 if (!prov_info) {
219 g_set_error (error, TEST_ERROR, TEST_ERROR_GENERIC,
220 "Provider '%s' not found", provider);
221 return NULL;
222 }
223
224 /* create database if requested */
225 upname = prov_name_upcase (prov_info->id);
226 str = g_strdup_printf ("%s_DBCREATE_PARAMS", upname);
227 db_params = getenv (str);
228 g_free (str);
229 if (db_params) {
230 GdaServerOperation *op;
231
232 db_quark_list = gda_quark_list_new_from_string (db_params);
233 op = gda_server_operation_prepare_drop_database (prov_info->id, dbname, NULL);
234 gda_quark_list_foreach (db_quark_list, (GHFunc) db_create_quark_foreach_func, op);
235 gda_server_operation_perform_drop_database (op, NULL, NULL);
236 g_object_unref (op);
237
238 op = gda_server_operation_prepare_create_database (prov_info->id, dbname, NULL);
239 gda_quark_list_foreach (db_quark_list, (GHFunc) db_create_quark_foreach_func, op);
240 if (!gda_server_operation_perform_create_database (op, NULL, error))
241 goto out;
242 db_created = TRUE;
243 }
244
245 /* open connection to database */
246 cnc = test_cnc_open_connection (provider, dbname, error);
247
248 out:
249 if (cnc) {
250 g_object_set_data_full (G_OBJECT (cnc), "dbname", g_strdup (dbname), g_free);
251 g_object_set_data (G_OBJECT (cnc), "db_created", GINT_TO_POINTER (db_created));
252 g_print ("Connection now set up (%s)\n", db_created ? "database created" : "reusing database");
253 }
254
255 return cnc;
256 }
257
258 /*
259 * Creates the structure of the database pointed by @cnc, as specified in @schema_file
260 *
261 */
262 gboolean
test_cnc_setup_db_structure(GdaConnection * cnc,const gchar * schema_file,GError ** error)263 test_cnc_setup_db_structure (GdaConnection *cnc, const gchar *schema_file, GError **error)
264 {
265 GdaDDLCreator *ddl;
266 g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
267
268 ddl = gda_ddl_creator_new ();
269 if (!gda_ddl_creator_set_dest_from_file (ddl, schema_file, error)) {
270 g_object_unref (ddl);
271 return FALSE;
272 }
273
274 gda_ddl_creator_set_connection (ddl, cnc);
275 if (!gda_ddl_creator_execute (ddl, error)) {
276 g_object_unref (ddl);
277 return FALSE;
278 }
279
280 g_object_unref (ddl);
281 return TRUE;
282 }
283
284 /*
285 *
286 * Connection CLEAN
287 *
288 */
289 static void
db_drop_quark_foreach_func(gchar * name,gchar * value,GdaServerOperation * op)290 db_drop_quark_foreach_func (gchar *name, gchar *value, GdaServerOperation *op)
291 {
292 gda_server_operation_set_value_at (op, value, NULL, "/SERVER_CNX_P/%s", name);
293 gda_server_operation_set_value_at (op, value, NULL, "/DB_DESC_P/%s", name);
294 }
295
296 /*
297 * Cleans up a connection.
298 *
299 * If @destroy_db is TRUE, then the database is destroyed, except if <upper_case_provider_name>_DONT_REMOVE_DB
300 * is set.
301 *
302 * WARNING: the @cnc connection destroyed closed by this function
303 */
304 gboolean
test_cnc_clean_connection(GdaConnection * cnc,GError ** error)305 test_cnc_clean_connection (GdaConnection *cnc, GError **error)
306 {
307 gchar *prov_id;
308 gboolean retval = TRUE;
309 gchar *str, *upname;
310 gboolean destroy_db;
311
312 prov_id = g_strdup (gda_connection_get_provider_name (cnc));
313
314 upname = prov_name_upcase (prov_id);
315 str = g_strdup_printf ("%s_DONT_REMOVE_DB", upname);
316 if (getenv (str))
317 destroy_db = FALSE;
318 else
319 destroy_db = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cnc), "db_created"));
320 g_free (str);
321
322 if (destroy_db) {
323 GdaServerOperation *op;
324 gchar *dbname;
325
326 const gchar *db_params;
327 GdaQuarkList *db_quark_list = NULL;
328
329 dbname = (gchar *) g_object_get_data (G_OBJECT (cnc), "dbname");
330 g_assert (dbname);
331 dbname = g_strdup (dbname);
332
333 gda_connection_close (cnc);
334 g_object_unref (cnc);
335
336 #ifdef CHECK_EXTRA_INFO
337 g_print ("Waiting a bit for the server to register the disconnection...\n");
338 #endif
339 sleep (1);
340 str = g_strdup_printf ("%s_DBCREATE_PARAMS", upname);
341 db_params = getenv (str);
342 g_free (str);
343 g_assert (db_params);
344
345 op = gda_server_operation_prepare_drop_database (prov_id, dbname, NULL);
346 g_free (dbname);
347 db_quark_list = gda_quark_list_new_from_string (db_params);
348 gda_quark_list_foreach (db_quark_list, (GHFunc) db_drop_quark_foreach_func, op);
349 gda_quark_list_free (db_quark_list);
350
351 if (!gda_server_operation_perform_drop_database (op, NULL, error))
352 retval = FALSE;
353 g_object_unref (op);
354 }
355 else {
356 TO_IMPLEMENT;
357 gda_connection_close (cnc);
358 g_object_unref (cnc);
359 }
360 g_free (upname);
361 g_free (prov_id);
362
363 return retval;
364 }
365
366 gboolean
test_cnc_setup_db_contents(G_GNUC_UNUSED GdaConnection * cnc,G_GNUC_UNUSED const gchar * data_file,G_GNUC_UNUSED GError ** error)367 test_cnc_setup_db_contents (G_GNUC_UNUSED GdaConnection *cnc, G_GNUC_UNUSED const gchar *data_file,
368 G_GNUC_UNUSED GError **error)
369 {
370 TO_IMPLEMENT;
371 return FALSE;
372 }
373
374 /*
375 * Load data from file @file into table @table
376 */
377 gboolean
test_cnc_load_data_from_file(GdaConnection * cnc,const gchar * table,const gchar * full_file,GError ** error)378 test_cnc_load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *full_file, GError **error)
379 {
380 GdaStatement *stmt = NULL;
381 GdaSet *params = NULL;
382 GdaDataModel *import;
383 gint nrows, ncols, i;
384 GdaMetaStruct *mstruct = NULL;
385 GSList *list;
386 gboolean retval = TRUE;
387
388 /* loading XML file */
389 import = gda_data_model_import_new_file (full_file, TRUE, NULL);
390 if (gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (import))) {
391 g_set_error (error, TEST_ERROR, TEST_ERROR_GENERIC, "Error loading '%s' file", full_file);
392 return FALSE;
393 }
394
395 /* retrieving meta data info */
396 GdaMetaDbObject *table_dbo;
397 GValue *name_value;
398 g_value_set_string ((name_value = gda_value_new (G_TYPE_STRING)), table);
399 mstruct = gda_meta_struct_new (gda_connection_get_meta_store (cnc), GDA_META_STRUCT_FEATURE_NONE);
400 table_dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE,
401 NULL, NULL, name_value, error);
402 gda_value_free (name_value);
403 if (! table_dbo) {
404 retval = FALSE;
405 goto out;
406 }
407
408 /* creating INSERT statement */
409 GdaSqlStatement *st;
410 GdaSqlStatementInsert *ist;
411 GSList *insert_values_list = NULL;
412
413 ist = g_new0 (GdaSqlStatementInsert, 1);
414 GDA_SQL_ANY_PART (ist)->type = GDA_SQL_ANY_STMT_INSERT;
415 ist->table = gda_sql_table_new (GDA_SQL_ANY_PART (ist));
416 ist->table->table_name = g_strdup (table);
417
418 GdaMetaTable *mtable = GDA_META_TABLE (table_dbo);
419 for (list = mtable->columns; list; list = list->next) {
420 GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
421 GdaSqlField *field;
422
423 /* field */
424 field = gda_sql_field_new (GDA_SQL_ANY_PART (ist));
425 field->field_name = g_strdup (tcol->column_name);
426 ist->fields_list = g_slist_append (ist->fields_list, field);
427
428 /* value */
429 GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
430 GdaSqlExpr *expr;
431 pspec->name = g_strdup (tcol->column_name);
432 pspec->g_type = tcol->gtype;
433 pspec->nullok = tcol->nullok;
434 expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ist));
435 expr->param_spec = pspec;
436 insert_values_list = g_slist_append (insert_values_list, expr);
437 }
438
439 ist->values_list = g_slist_append (NULL, insert_values_list);
440 st = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
441 st->contents = ist;
442 stmt = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
443 gda_sql_statement_free (st);
444 g_object_unref (mstruct);
445
446 if (! gda_statement_get_parameters (stmt, ¶ms, error)) {
447 retval = FALSE;
448 goto out;
449 }
450
451 /* executing inserts */
452 nrows = gda_data_model_get_n_rows (import);
453 ncols = gda_data_model_get_n_columns (import);
454 if (!gda_connection_begin_transaction (cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, error)) {
455 retval = FALSE;
456 goto out;
457 }
458 for (i = 0; i < nrows; i++) {
459 gint j;
460 GSList *list;
461 for (list = params->holders, j = 0; list && (j < ncols); list = list->next, j++) {
462 const GValue *cvalue = gda_data_model_get_value_at (import, j, i, error);
463 if (!cvalue) {
464 gda_connection_rollback_transaction (cnc, NULL, NULL);
465 retval = FALSE;
466 goto out;
467 }
468 if (! gda_holder_set_value (GDA_HOLDER (list->data), cvalue, error)) {
469 gda_connection_rollback_transaction (cnc, NULL, NULL);
470 retval = FALSE;
471 goto out;
472 }
473 }
474
475 if (list || (j < ncols)) {
476 g_set_error (error, TEST_ERROR, TEST_ERROR_GENERIC, "%s",
477 "Incoherent number of columns in table and imported data");
478 gda_connection_rollback_transaction (cnc, NULL, NULL);
479 retval = FALSE;
480 goto out;
481 }
482
483 if (gda_connection_statement_execute_non_select (cnc, stmt, params, NULL, error) == -1) {
484 gda_connection_rollback_transaction (cnc, NULL, NULL);
485 retval = FALSE;
486 goto out;
487 }
488 }
489
490 if (! gda_connection_commit_transaction (cnc, NULL, error))
491 retval = FALSE;
492
493 out:
494 if (import)
495 g_object_unref (import);
496 if (stmt)
497 g_object_unref (stmt);
498 if (params)
499 g_object_unref (params);
500
501 return retval;
502 }
503