QOF 0.7.5
|
00001 /******************************************************************** 00002 * qof-gda.c 00003 * 00004 * Sat Sep 9 13:11:17 2006 00005 * Copyright 2006-2008 Neil Williams 00006 * linux@codehelp.co.uk 00007 ********************************************************************/ 00008 /* 00009 * This program is free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include "config.h" 00025 #include <glib.h> 00026 #include <glib/gstdio.h> 00027 #include <libintl.h> 00028 #include <libgda/libgda.h> 00029 #include "qof.h" 00030 #include "qof-gda.h" 00031 00032 #define _(String) dgettext (GETTEXT_PACKAGE, String) 00033 #define ACCESS_METHOD "gda" 00034 #define LIBGDA_DIR ".qofgda" 00035 #define GDA_DBNAME "gda-database-name" 00036 #define GDA_USERNAME "gda-username" 00037 #define GDA_PASSWORD "gda-password" 00038 #define GDA_DATASOURCE "qof-gda-source" 00039 00045 static QofLogModule log_module = QOF_MOD_GDA; 00046 00047 typedef struct 00048 { 00049 QofBackend be; 00050 GdaClient * client_pool; 00051 GdaConnection * connection; 00052 GdaCommand * command; 00053 GdaDataModel * dm; 00054 GValue * gda_value; 00055 /* GdaTransaction is now just a string label */ 00056 gchar * undo_trans, * commit_trans; 00057 GError * gda_err; 00058 const GdaColumn *gda_param; 00059 GList * entities; 00060 gint dbversion; 00061 gint create_handler; 00062 gint delete_handler; 00063 const gchar *fullpath; 00064 const gchar * table_name; /* revised each iteration. */ 00065 GSList * field_list; 00066 /* QofBackendOption settings: */ 00067 gchar * data_source_name; 00068 gchar * provider_name; 00069 gchar * database_name; 00070 gchar * source_description; 00071 gchar * username; 00072 gchar * password; 00073 /* end QofBackendOption */ 00074 gchar *err; 00075 gchar *sql_str; 00076 gboolean error; 00077 QofIdType e_type; 00078 QofBook * book; 00079 } QGdaBackend; 00080 00081 static gboolean 00082 qgda_determine_file_type (const gchar * path) 00083 { 00084 if (!path) 00085 return FALSE; 00086 /* accept all requests for the gda: access_method */ 00087 return TRUE; 00088 } 00089 00090 static void 00091 qgda_modify (QofBackend *be, QofInstance *inst) 00092 { 00093 00094 } 00095 00096 static GdaColumn * 00097 qoftype_to_gdafield (QofIdTypeConst qoftype) 00098 { 00099 GdaColumn *p; 00100 00101 p = gda_column_new(); 00102 gda_column_set_allow_null (p, TRUE); 00103 gda_column_set_g_type (p, G_TYPE_NONE); 00104 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_STRING)) 00105 gda_column_set_g_type (p, G_TYPE_STRING); 00106 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_GUID)) 00107 { 00108 gda_column_set_g_type (p, G_TYPE_STRING); 00109 gda_column_set_allow_null (p, FALSE); 00110 gda_column_set_primary_key (p, TRUE); 00111 } 00112 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_CHAR)) 00113 gda_column_set_g_type (p, G_TYPE_STRING); 00114 if ((0 == safe_strcasecmp (qoftype, QOF_TYPE_DOUBLE)) || 00115 (0 == safe_strcasecmp (qoftype, QOF_TYPE_NUMERIC)) || 00116 (0 == safe_strcasecmp (qoftype, QOF_TYPE_DEBCRED))) 00117 gda_column_set_g_type (p, G_TYPE_DOUBLE); 00118 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_TIME)) 00119 gda_column_set_g_type (p, G_TYPE_DATE); 00120 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_BOOLEAN)) 00121 gda_column_set_g_type (p, G_TYPE_BOOLEAN); 00122 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_INT32)) 00123 gda_column_set_g_type (p, G_TYPE_INT); 00124 if (0 == safe_strcasecmp (qoftype, QOF_TYPE_INT64)) 00125 gda_column_set_g_type (p, G_TYPE_INT64); 00126 /* if (0 == safe_strcasecmp (qoftype, QOF_TYPE_KVP)) 00127 gda_column_set_g_type (p, G_TYPE_); ?? 00128 */ 00129 if (gda_column_get_g_type (p) == G_TYPE_NONE) 00130 { 00131 g_free (p); 00132 return NULL; 00133 } 00134 return p; 00135 } 00136 00137 static void 00138 convert_params (QofParam * param, gpointer user_data) 00139 { 00140 GdaColumn * p; 00141 QGdaBackend * qgda_be; 00142 00143 qgda_be = (QGdaBackend*)user_data; 00144 if (!param) 00145 return; 00146 if (0 == safe_strcasecmp (param->param_type, QOF_ID_BOOK)) 00147 return; 00148 p = qoftype_to_gdafield (param->param_type); 00149 if (!p) 00150 { 00151 DEBUG (" unsupported QofParam: %s %s", 00152 param->param_name, param->param_type); 00153 return; 00154 } 00155 gda_column_set_name (p, param->param_name); 00156 gda_column_set_table (p, qgda_be->table_name); 00157 qgda_be->field_list = g_slist_append (qgda_be->field_list, p); 00158 PINFO (" name=%s table=%s type=%s", param->param_name, 00159 qgda_be->table_name, param->param_type); 00160 } 00161 00162 static void 00163 build_table (gpointer value, gpointer user_data) 00164 { 00165 QGdaBackend * qgda_be; 00166 GdaParameterList * plist; 00167 GError * qgda_err; 00168 gint c; 00169 00170 qgda_err = NULL; 00171 qgda_be = (QGdaBackend*)user_data; 00172 if (!gda_connection_is_opened (qgda_be->connection)) 00173 { 00174 /* this probably needs to be a user error/ */ 00175 PERR (" no connection to gda available"); 00176 return; 00177 } 00178 PINFO (" length=%d", g_slist_length(qgda_be->field_list)); 00179 c = g_slist_length(qgda_be->field_list); 00180 if (c > 0) 00181 { 00182 gchar * text; 00183 00184 /* need a plain text SQL statement for the table. 00185 (meaning that this will end up looking a lot like 00186 QSQLiteBackend). 00187 */ 00188 text = g_strdup ("create table ... "); 00189 plist = NULL; 00190 qgda_be->command = gda_command_new (text, GDA_COMMAND_TYPE_SQL, 00191 GDA_COMMAND_OPTION_STOP_ON_ERRORS); 00192 gda_connection_execute_non_select_command (qgda_be->connection, 00193 qgda_be->command, plist, &qgda_err); 00194 /* plist contains a GdaParameterList of results - probably ignore. */ 00195 if (qgda_err) 00196 { 00197 /* handle the error here */ 00198 g_clear_error (&qgda_err); 00199 } 00200 gda_command_free (qgda_be->command); 00201 } 00202 } 00203 00204 static void 00205 create_tables (QofObject * obj, gpointer user_data) 00206 { 00207 QGdaBackend * qgda_be; 00208 00209 qgda_be = (QGdaBackend*)user_data; 00210 if (qgda_be->field_list) 00211 g_slist_free (qgda_be->field_list); 00212 qgda_be->field_list = NULL; 00213 qgda_be->table_name = obj->e_type; 00214 qof_class_param_foreach (obj->e_type, convert_params, 00215 qgda_be); 00216 g_slist_foreach (qgda_be->field_list, build_table, qgda_be); 00217 } 00218 00219 static gboolean 00220 create_data_source (QGdaBackend * qgda_be) 00221 { 00222 gchar * cnc_string; 00223 QofBackend * be; 00224 GdaProviderInfo * prov; 00225 00226 ENTER (" "); 00227 be = (QofBackend*)qgda_be; 00228 if (!qgda_be->data_source_name) 00229 { 00230 qof_error_set_be (be, qof_error_register 00231 (_("GDA: Missing data source name."), FALSE)); 00232 LEAVE (" empty data source name"); 00233 return FALSE; 00234 } 00235 prov = gda_config_get_provider_by_name (qgda_be->provider_name); 00236 if (!prov) 00237 { 00238 gchar * msg; 00239 00240 msg = g_strdup_printf (_("GDA Provider '%s' could not be found"), 00241 qgda_be->provider_name); 00242 qof_error_set_be (be, qof_error_register(msg, FALSE)); 00243 g_free (msg); 00244 LEAVE (" provider '%s' not found", qgda_be->provider_name); 00245 return FALSE; 00246 } 00247 /* cnc_string = g_strconcat ("DATABASE=", qgda_be->database_name, 00248 NULL);*/ 00249 cnc_string = g_strdup ("URI=/home/neil/gda-test.db"); 00250 /* creates db within source if db does not exist */ 00251 gda_config_save_data_source (qgda_be->data_source_name, 00252 qgda_be->provider_name, cnc_string, 00253 qgda_be->source_description, qgda_be->username, 00254 qgda_be->password, TRUE); 00255 /* create tables per QofObject */ 00256 qof_object_foreach_type (create_tables, qgda_be); 00257 /* gda_connection_create_table (don't log password) */ 00258 LEAVE (" created data source for %s, %s, %s, %s", 00259 qgda_be->data_source_name, 00260 qgda_be->provider_name, cnc_string, 00261 qgda_be->username); 00262 return TRUE; 00263 } 00264 00265 static void 00266 qgda_session_begin(QofBackend *be, QofSession *session, const 00267 gchar *book_path, gboolean ignore_lock, 00268 gboolean create_if_nonexistent) 00269 { 00270 QGdaBackend *qgda_be; 00271 GError * qgda_err; 00272 // GList * connection_errors, *node; 00273 00274 qgda_err = NULL; 00275 /* cannot use ignore_lock */ 00276 PINFO (" gda session start"); 00277 qgda_be = (QGdaBackend*)be; 00278 be->fullpath = g_strdup (book_path); 00279 if(book_path == NULL) 00280 { 00281 qof_error_set_be (be, qof_error_register 00282 (_("GDA: No data source path specified."), FALSE)); 00283 qgda_be->error = TRUE; 00284 LEAVE (" bad URL"); 00285 return; 00286 } 00287 /* check/create the ~/.libgda location. */ 00288 { 00289 gchar * gdahome; 00290 struct stat lg; 00291 gint ret; 00292 00293 ret = g_stat (g_get_home_dir(), &lg); 00294 if (ret) 00295 { 00296 qof_error_set_be (be, qof_error_register 00297 (_("GDA: Unable to locate your home directory."), 00298 FALSE)); 00299 qgda_be->error = TRUE; 00300 LEAVE (" unable to use stat on home_dir."); 00301 return; 00302 } 00303 gdahome = g_strconcat (g_get_home_dir(), 00304 "/", LIBGDA_DIR, NULL); 00305 if (!S_ISDIR (lg.st_mode) || lg.st_size == 0) 00306 ret = g_mkdir_with_parents (gdahome, 0700); 00307 if (ret) 00308 { 00309 qof_error_set_be (be, qof_error_register 00310 (_("GDA: Unable to create a .libgda directory " 00311 "within your home directory."), FALSE)); 00312 qgda_be->error = TRUE; 00313 LEAVE (" unable to create '%s' 0700", gdahome); 00314 return; 00315 } 00316 g_free (gdahome); 00317 } 00318 { 00319 /* check data source */ 00320 GdaDataSourceInfo * source; 00321 gboolean created; 00322 00323 created = FALSE; 00324 source = gda_config_find_data_source 00325 (qgda_be->data_source_name); 00326 if (!source && create_if_nonexistent) 00327 { 00328 DEBUG (" no source, creating . . ."); 00329 created = create_data_source (qgda_be); 00330 } 00331 if (!source && !created) 00332 { 00333 qof_error_set_be (be, qof_error_register 00334 (_("GDA: No data source found at '%s' - " 00335 "Try loading data from another file " 00336 "and write to gda: again to create the " 00337 "GDA data source."), TRUE)); 00338 DEBUG (" no source but set not to create."); 00339 qgda_be->error = TRUE; 00340 return; 00341 } 00342 } 00343 PINFO (" trying for a connection"); 00344 /* use the username and password that created the source */ 00345 qgda_be->connection = gda_client_open_connection 00346 (qgda_be->client_pool, qgda_be->data_source_name, 00347 NULL, NULL, GDA_CONNECTION_OPTIONS_DONT_SHARE, &qgda_err); 00348 if (qgda_be->connection) 00349 { 00350 PINFO (" appear to be connected."); 00351 /* create tables per QofObject */ 00352 qof_object_foreach_type (create_tables, qgda_be); 00353 } 00354 else 00355 { 00356 gchar * msg; 00357 00358 msg = g_strdup_printf ( 00359 _("GDA encountered an error '%s' using data source '%s'."), 00360 qgda_err->message, qgda_be->data_source_name); 00361 qof_error_set_be (be, qof_error_register (msg, FALSE)); 00362 PERR (" failed to connect to GDA: '%s'", msg); 00363 qgda_be->error = TRUE; 00364 g_free (msg); 00365 } 00366 } 00367 00368 static void 00369 load_entities (gpointer value, gpointer user_data) 00370 { 00371 gint column_id, row_id; 00372 GdaDataModel * dm; 00373 QGdaBackend * qgda_be; 00374 00375 qgda_be = (QGdaBackend*)user_data; 00376 dm = (GdaDataModel*)value; 00377 if (!dm) 00378 { 00379 qgda_be->error = TRUE; 00380 DEBUG (" empty data model on load"); 00381 return; 00382 } 00383 for (column_id = 0; column_id < gda_data_model_get_n_columns (dm); 00384 column_id++) 00385 g_print("%s\t", gda_data_model_get_column_title (dm, column_id)); 00386 g_print("\n"); 00387 for (row_id = 0; row_id < gda_data_model_get_n_rows (dm); row_id++) { 00388 for (column_id = 0; column_id < gda_data_model_get_n_columns (dm); 00389 column_id++) 00390 { 00391 gchar *str; 00392 00393 qgda_be->gda_value = (GValue*)gda_data_model_get_value_at 00394 (dm, column_id, row_id); 00395 str = gda_value_stringify (qgda_be->gda_value); 00396 g_print ("%s\t", str); 00397 g_free (str); 00398 } 00399 g_print("\n"); 00400 } 00401 g_object_unref(dm); 00402 } 00403 00404 static void 00405 qgda_class_foreach (QofObject * obj, gpointer data) 00406 { 00407 QGdaBackend *qgda_be; 00408 GError * qgda_err; 00409 00410 qgda_err = NULL; 00411 qgda_be = (QGdaBackend*)data; 00412 qgda_be->sql_str = g_strdup_printf( 00413 "SELECT * FROM %s;", obj->e_type); 00414 PINFO (" sql=%s", qgda_be->sql_str); 00415 qgda_be->command = gda_command_new (qgda_be->sql_str, 00416 GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); 00417 qgda_be->entities = gda_connection_execute_command (qgda_be->connection, 00418 qgda_be->command, NULL, &qgda_err); 00419 g_list_foreach (qgda_be->entities, load_entities, qgda_be); 00420 gda_command_free (qgda_be->command); 00421 } 00422 00423 static void 00424 qgda_db_load (QofBackend *be, QofBook *book) 00425 { 00426 QGdaBackend *qgda_be; 00427 00428 qgda_be = (QGdaBackend*)be; 00429 if (qgda_be->error) 00430 return; 00431 /* select all */ 00432 qgda_be->book = book; 00433 qof_object_foreach_type(qgda_class_foreach, qgda_be); 00434 } 00435 00436 static void 00437 qgda_write_db (QofBackend *be, QofBook *book) 00438 { 00439 00440 } 00441 00442 static void 00443 qgda_session_end (QofBackend *be) 00444 { 00445 QGdaBackend *qgda_be; 00446 00447 qgda_be = (QGdaBackend*)be; 00448 if (qgda_be->dm) 00449 g_object_unref(G_OBJECT(qgda_be->dm)); 00450 gda_client_close_all_connections (qgda_be->client_pool); 00451 } 00452 00453 static void 00454 qgda_destroy_backend (QofBackend *be) 00455 { 00456 QGdaBackend *qgda_be; 00457 00458 qgda_be = (QGdaBackend*)be; 00459 if (qgda_be) 00460 g_object_unref(G_OBJECT(qgda_be->client_pool)); 00461 qof_event_unregister_handler (qgda_be->create_handler); 00462 qof_event_unregister_handler (qgda_be->delete_handler); 00463 g_free (be); 00464 g_free (qgda_be); 00465 } 00466 00467 static void 00468 option_cb (QofBackendOption * option, gpointer data) 00469 { 00470 QGdaBackend * qgda_be; 00471 00472 qgda_be = (QGdaBackend *) data; 00473 g_return_if_fail (qgda_be); 00474 if (0 == safe_strcmp (GDA_DBNAME, option->option_name)) 00475 { 00476 qgda_be->database_name = g_strdup (option->value); 00477 PINFO (" database name = %s", qgda_be->database_name); 00478 } 00479 if (0 == safe_strcmp (GDA_USERNAME, option->option_name)) 00480 { 00481 qgda_be->username = g_strdup (option->value); 00482 PINFO (" username=%s", qgda_be->username); 00483 } 00484 if (0 == safe_strcmp (GDA_PASSWORD, option->option_name)) 00485 { 00486 /* don't log the password! :-) */ 00487 qgda_be->password = g_strdup (option->value); 00488 } 00489 if (0 == safe_strcmp (GDA_DATASOURCE, option->option_name)) 00490 { 00491 qgda_be->data_source_name = g_strdup (option->value); 00492 } 00493 } 00494 00495 static void 00496 load_config (QofBackend * be, KvpFrame * config) 00497 { 00498 QGdaBackend *qgda_be; 00499 00500 ENTER (" "); 00501 qgda_be = (QGdaBackend *) be; 00502 g_return_if_fail (qgda_be); 00503 qof_backend_option_foreach (config, option_cb, qgda_be); 00504 LEAVE (" "); 00505 } 00506 00507 static KvpFrame * 00508 get_config (QofBackend * be) 00509 { 00510 QofBackendOption *option; 00511 QGdaBackend *qgda_be; 00512 00513 if (!be) 00514 { 00515 return NULL; 00516 } 00517 ENTER (" "); 00518 qgda_be = (QGdaBackend *) be; 00519 g_return_val_if_fail (qgda_be, NULL); 00520 qof_backend_prepare_frame (be); 00521 option = g_new0 (QofBackendOption, 1); 00522 option->option_name = GDA_DBNAME; 00523 option->description = 00524 _("Name of the database to use."); 00525 option->tooltip = 00526 _("Override the default database name with " 00527 "a name of your own choice."); 00528 option->type = KVP_TYPE_STRING; 00529 option->value = (gpointer) qgda_be->database_name; 00530 qof_backend_prepare_option (be, option); 00531 g_free (option); 00532 option = g_new0 (QofBackendOption, 1); 00533 option->option_name = GDA_USERNAME; 00534 option->description = 00535 _("The username to use to access this data source."); 00536 option->tooltip = 00537 _("The username specified in the configuration of this " 00538 "data source that provides write access to the data."); 00539 option->type = KVP_TYPE_STRING; 00540 option->value = (gpointer) qgda_be->username; 00541 qof_backend_prepare_option (be, option); 00542 g_free (option); 00543 option = g_new0 (QofBackendOption, 1); 00544 option->option_name = GDA_PASSWORD; 00545 option->description = 00546 _("Password to use with the username."); 00547 option->tooltip = 00548 _("The password that is to be used with the specified " 00549 "username."); 00550 option->type = KVP_TYPE_STRING; 00551 option->value = (gpointer) qgda_be->password; 00552 qof_backend_prepare_option (be, option); 00553 g_free (option); 00554 option = g_new0 (QofBackendOption, 1); 00555 option->option_name = GDA_DATASOURCE; 00556 option->description = 00557 _("Name of this data source."); 00558 option->tooltip = 00559 _("The name of this data source as specified " 00560 "in the GDA configuration."); 00561 option->type = KVP_TYPE_STRING; 00562 option->value = (gpointer) qgda_be->password; 00563 qof_backend_prepare_option (be, option); 00564 g_free (option); 00565 LEAVE (" "); 00566 return qof_backend_complete_frame (be); 00567 } 00568 00569 static QofBackend * 00570 qgda_backend_new (void) 00571 { 00572 QGdaBackend *qgda_be; 00573 QofBackend *be; 00574 00575 ENTER (" "); 00576 qgda_be = g_new0(QGdaBackend, 1); 00577 be = (QofBackend*) qgda_be; 00578 qof_backend_init(be); 00579 gda_init (PACKAGE, "0.1", 0, NULL); 00580 qgda_be->client_pool = gda_client_new (); 00581 qgda_be->dbversion = QOF_OBJECT_VERSION; 00582 be->session_begin = qgda_session_begin; 00583 00584 be->session_end = qgda_session_end; 00585 be->destroy_backend = qgda_destroy_backend; 00586 be->load = qgda_db_load; 00587 be->save_may_clobber_data = NULL; 00588 be->begin = NULL; 00589 /* commit: write to gda, commit undo record. */ 00590 be->commit = qgda_modify; 00591 be->rollback = NULL; 00592 /* would need a QofQuery back to QofSqlQuery conversion. */ 00593 be->compile_query = NULL; 00594 /* unused */ 00595 be->free_query = NULL; 00596 be->run_query = NULL; 00597 be->counter = NULL; 00598 /* The QOF GDA backend might be multi-user */ 00599 be->events_pending = NULL; 00600 be->process_events = NULL; 00601 00602 be->sync = qgda_write_db; 00603 be->load_config = load_config; 00604 be->get_config = get_config; 00605 LEAVE (" "); 00606 00607 /* DEBUG */ 00608 qgda_be->data_source_name = "QOF_DEBUG"; 00609 qgda_be->database_name = "URI=/home/neil/test.gda"; 00610 qgda_be->provider_name = "XML"; 00611 qgda_be->source_description = "QOF GDA debug data"; 00612 /* end debug */ 00613 return be; 00614 } 00615 00616 static void 00617 qgda_provider_free (QofBackendProvider *prov) 00618 { 00619 prov->provider_name = NULL; 00620 prov->access_method = NULL; 00621 g_free (prov); 00622 } 00623 00624 void qof_gda_provider_init(void) 00625 { 00626 QofBackendProvider *prov; 00627 00628 bindtextdomain (PACKAGE, LOCALE_DIR); 00629 prov = g_new0 (QofBackendProvider, 1); 00630 prov->provider_name = "QOF GDA Backend Version 0.1"; 00631 prov->access_method = ACCESS_METHOD; 00632 prov->partial_book_supported = TRUE; 00633 prov->backend_new = qgda_backend_new; 00634 prov->check_data_type = qgda_determine_file_type; 00635 prov->provider_free = qgda_provider_free; 00636 qof_backend_register_provider (prov); 00637 }