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
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
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 *
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 *
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 *
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
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
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
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
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
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, &params, 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