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, ¶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