QOF  0.7.5
qof-gda.c
Go to the documentation of this file.
1 /********************************************************************
2  * qof-gda.c
3  *
4  * Sat Sep 9 13:11:17 2006
5  * Copyright 2006-2008 Neil Williams
6  * linux@codehelp.co.uk
7  ********************************************************************/
8 /*
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #include "config.h"
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <libintl.h>
28 #include <libgda/libgda.h>
29 #include "qof.h"
30 #include "qof-gda.h"
31 
32 #define _(String) dgettext (GETTEXT_PACKAGE, String)
33 #define ACCESS_METHOD "gda"
34 #define LIBGDA_DIR ".qofgda"
35 #define GDA_DBNAME "gda-database-name"
36 #define GDA_USERNAME "gda-username"
37 #define GDA_PASSWORD "gda-password"
38 #define GDA_DATASOURCE "qof-gda-source"
39 
45 static QofLogModule log_module = QOF_MOD_GDA;
46 
47 typedef struct
48 {
49  QofBackend be;
50  GdaClient * client_pool;
51  GdaConnection * connection;
52  GdaCommand * command;
53  GdaDataModel * dm;
54  GValue * gda_value;
55  /* GdaTransaction is now just a string label */
56  gchar * undo_trans, * commit_trans;
57  GError * gda_err;
58  const GdaColumn *gda_param;
59  GList * entities;
60  gint dbversion;
61  gint create_handler;
62  gint delete_handler;
63  const gchar *fullpath;
64  const gchar * table_name; /* revised each iteration. */
65  GSList * field_list;
66  /* QofBackendOption settings: */
67  gchar * data_source_name;
68  gchar * provider_name;
69  gchar * database_name;
70  gchar * source_description;
71  gchar * username;
72  gchar * password;
73  /* end QofBackendOption */
74  gchar *err;
75  gchar *sql_str;
76  gboolean error;
77  QofIdType e_type;
78  QofBook * book;
79 } QGdaBackend;
80 
81 static gboolean
82 qgda_determine_file_type (const gchar * path)
83 {
84  if (!path)
85  return FALSE;
86  /* accept all requests for the gda: access_method */
87  return TRUE;
88 }
89 
90 static void
91 qgda_modify (QofBackend *be, QofInstance *inst)
92 {
93 
94 }
95 
96 static GdaColumn *
97 qoftype_to_gdafield (QofIdTypeConst qoftype)
98 {
99  GdaColumn *p;
100 
101  p = gda_column_new();
102  gda_column_set_allow_null (p, TRUE);
103  gda_column_set_g_type (p, G_TYPE_NONE);
104  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_STRING))
105  gda_column_set_g_type (p, G_TYPE_STRING);
106  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_GUID))
107  {
108  gda_column_set_g_type (p, G_TYPE_STRING);
109  gda_column_set_allow_null (p, FALSE);
110  gda_column_set_primary_key (p, TRUE);
111  }
112  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_CHAR))
113  gda_column_set_g_type (p, G_TYPE_STRING);
114  if ((0 == safe_strcasecmp (qoftype, QOF_TYPE_DOUBLE)) ||
115  (0 == safe_strcasecmp (qoftype, QOF_TYPE_NUMERIC)) ||
116  (0 == safe_strcasecmp (qoftype, QOF_TYPE_DEBCRED)))
117  gda_column_set_g_type (p, G_TYPE_DOUBLE);
118  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_TIME))
119  gda_column_set_g_type (p, G_TYPE_DATE);
120  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_BOOLEAN))
121  gda_column_set_g_type (p, G_TYPE_BOOLEAN);
122  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_INT32))
123  gda_column_set_g_type (p, G_TYPE_INT);
124  if (0 == safe_strcasecmp (qoftype, QOF_TYPE_INT64))
125  gda_column_set_g_type (p, G_TYPE_INT64);
126 /* if (0 == safe_strcasecmp (qoftype, QOF_TYPE_KVP))
127  gda_column_set_g_type (p, G_TYPE_); ??
128 */
129  if (gda_column_get_g_type (p) == G_TYPE_NONE)
130  {
131  g_free (p);
132  return NULL;
133  }
134  return p;
135 }
136 
137 static void
138 convert_params (QofParam * param, gpointer user_data)
139 {
140  GdaColumn * p;
141  QGdaBackend * qgda_be;
142 
143  qgda_be = (QGdaBackend*)user_data;
144  if (!param)
145  return;
146  if (0 == safe_strcasecmp (param->param_type, QOF_ID_BOOK))
147  return;
148  p = qoftype_to_gdafield (param->param_type);
149  if (!p)
150  {
151  DEBUG (" unsupported QofParam: %s %s",
152  param->param_name, param->param_type);
153  return;
154  }
155  gda_column_set_name (p, param->param_name);
156  gda_column_set_table (p, qgda_be->table_name);
157  qgda_be->field_list = g_slist_append (qgda_be->field_list, p);
158  PINFO (" name=%s table=%s type=%s", param->param_name,
159  qgda_be->table_name, param->param_type);
160 }
161 
162 static void
163 build_table (gpointer value, gpointer user_data)
164 {
165  QGdaBackend * qgda_be;
166  GdaParameterList * plist;
167  GError * qgda_err;
168  gint c;
169 
170  qgda_err = NULL;
171  qgda_be = (QGdaBackend*)user_data;
172  if (!gda_connection_is_opened (qgda_be->connection))
173  {
174  /* this probably needs to be a user error/ */
175  PERR (" no connection to gda available");
176  return;
177  }
178  PINFO (" length=%d", g_slist_length(qgda_be->field_list));
179  c = g_slist_length(qgda_be->field_list);
180  if (c > 0)
181  {
182  gchar * text;
183 
184  /* need a plain text SQL statement for the table.
185  (meaning that this will end up looking a lot like
186  QSQLiteBackend).
187  */
188  text = g_strdup ("create table ... ");
189  plist = NULL;
190  qgda_be->command = gda_command_new (text, GDA_COMMAND_TYPE_SQL,
191  GDA_COMMAND_OPTION_STOP_ON_ERRORS);
192  gda_connection_execute_non_select_command (qgda_be->connection,
193  qgda_be->command, plist, &qgda_err);
194  /* plist contains a GdaParameterList of results - probably ignore. */
195  if (qgda_err)
196  {
197  /* handle the error here */
198  g_clear_error (&qgda_err);
199  }
200  gda_command_free (qgda_be->command);
201  }
202 }
203 
204 static void
205 create_tables (QofObject * obj, gpointer user_data)
206 {
207  QGdaBackend * qgda_be;
208 
209  qgda_be = (QGdaBackend*)user_data;
210  if (qgda_be->field_list)
211  g_slist_free (qgda_be->field_list);
212  qgda_be->field_list = NULL;
213  qgda_be->table_name = obj->e_type;
214  qof_class_param_foreach (obj->e_type, convert_params,
215  qgda_be);
216  g_slist_foreach (qgda_be->field_list, build_table, qgda_be);
217 }
218 
219 static gboolean
220 create_data_source (QGdaBackend * qgda_be)
221 {
222  gchar * cnc_string;
223  QofBackend * be;
224  GdaProviderInfo * prov;
225 
226  ENTER (" ");
227  be = (QofBackend*)qgda_be;
228  if (!qgda_be->data_source_name)
229  {
230  qof_error_set_be (be, qof_error_register
231  (_("GDA: Missing data source name."), FALSE));
232  LEAVE (" empty data source name");
233  return FALSE;
234  }
235  prov = gda_config_get_provider_by_name (qgda_be->provider_name);
236  if (!prov)
237  {
238  gchar * msg;
239 
240  msg = g_strdup_printf (_("GDA Provider '%s' could not be found"),
241  qgda_be->provider_name);
242  qof_error_set_be (be, qof_error_register(msg, FALSE));
243  g_free (msg);
244  LEAVE (" provider '%s' not found", qgda_be->provider_name);
245  return FALSE;
246  }
247 /* cnc_string = g_strconcat ("DATABASE=", qgda_be->database_name,
248  NULL);*/
249  cnc_string = g_strdup ("URI=/home/neil/gda-test.db");
250  /* creates db within source if db does not exist */
251  gda_config_save_data_source (qgda_be->data_source_name,
252  qgda_be->provider_name, cnc_string,
253  qgda_be->source_description, qgda_be->username,
254  qgda_be->password, TRUE);
255  /* create tables per QofObject */
256  qof_object_foreach_type (create_tables, qgda_be);
257  /* gda_connection_create_table (don't log password) */
258  LEAVE (" created data source for %s, %s, %s, %s",
259  qgda_be->data_source_name,
260  qgda_be->provider_name, cnc_string,
261  qgda_be->username);
262  return TRUE;
263 }
264 
265 static void
266 qgda_session_begin(QofBackend *be, QofSession *session, const
267  gchar *book_path, gboolean ignore_lock,
268  gboolean create_if_nonexistent)
269 {
270  QGdaBackend *qgda_be;
271  GError * qgda_err;
272 // GList * connection_errors, *node;
273 
274  qgda_err = NULL;
275  /* cannot use ignore_lock */
276  PINFO (" gda session start");
277  qgda_be = (QGdaBackend*)be;
278  be->fullpath = g_strdup (book_path);
279  if(book_path == NULL)
280  {
281  qof_error_set_be (be, qof_error_register
282  (_("GDA: No data source path specified."), FALSE));
283  qgda_be->error = TRUE;
284  LEAVE (" bad URL");
285  return;
286  }
287  /* check/create the ~/.libgda location. */
288  {
289  gchar * gdahome;
290  struct stat lg;
291  gint ret;
292 
293  ret = g_stat (g_get_home_dir(), &lg);
294  if (ret)
295  {
296  qof_error_set_be (be, qof_error_register
297  (_("GDA: Unable to locate your home directory."),
298  FALSE));
299  qgda_be->error = TRUE;
300  LEAVE (" unable to use stat on home_dir.");
301  return;
302  }
303  gdahome = g_strconcat (g_get_home_dir(),
304  "/", LIBGDA_DIR, NULL);
305  if (!S_ISDIR (lg.st_mode) || lg.st_size == 0)
306  ret = g_mkdir_with_parents (gdahome, 0700);
307  if (ret)
308  {
309  qof_error_set_be (be, qof_error_register
310  (_("GDA: Unable to create a .libgda directory "
311  "within your home directory."), FALSE));
312  qgda_be->error = TRUE;
313  LEAVE (" unable to create '%s' 0700", gdahome);
314  return;
315  }
316  g_free (gdahome);
317  }
318  {
319  /* check data source */
320  GdaDataSourceInfo * source;
321  gboolean created;
322 
323  created = FALSE;
324  source = gda_config_find_data_source
325  (qgda_be->data_source_name);
326  if (!source && create_if_nonexistent)
327  {
328  DEBUG (" no source, creating . . .");
329  created = create_data_source (qgda_be);
330  }
331  if (!source && !created)
332  {
333  qof_error_set_be (be, qof_error_register
334  (_("GDA: No data source found at '%s' - "
335  "Try loading data from another file "
336  "and write to gda: again to create the "
337  "GDA data source."), TRUE));
338  DEBUG (" no source but set not to create.");
339  qgda_be->error = TRUE;
340  return;
341  }
342  }
343  PINFO (" trying for a connection");
344  /* use the username and password that created the source */
345  qgda_be->connection = gda_client_open_connection
346  (qgda_be->client_pool, qgda_be->data_source_name,
347  NULL, NULL, GDA_CONNECTION_OPTIONS_DONT_SHARE, &qgda_err);
348  if (qgda_be->connection)
349  {
350  PINFO (" appear to be connected.");
351  /* create tables per QofObject */
352  qof_object_foreach_type (create_tables, qgda_be);
353  }
354  else
355  {
356  gchar * msg;
357 
358  msg = g_strdup_printf (
359  _("GDA encountered an error '%s' using data source '%s'."),
360  qgda_err->message, qgda_be->data_source_name);
361  qof_error_set_be (be, qof_error_register (msg, FALSE));
362  PERR (" failed to connect to GDA: '%s'", msg);
363  qgda_be->error = TRUE;
364  g_free (msg);
365  }
366 }
367 
368 static void
369 load_entities (gpointer value, gpointer user_data)
370 {
371  gint column_id, row_id;
372  GdaDataModel * dm;
373  QGdaBackend * qgda_be;
374 
375  qgda_be = (QGdaBackend*)user_data;
376  dm = (GdaDataModel*)value;
377  if (!dm)
378  {
379  qgda_be->error = TRUE;
380  DEBUG (" empty data model on load");
381  return;
382  }
383  for (column_id = 0; column_id < gda_data_model_get_n_columns (dm);
384  column_id++)
385  g_print("%s\t", gda_data_model_get_column_title (dm, column_id));
386  g_print("\n");
387  for (row_id = 0; row_id < gda_data_model_get_n_rows (dm); row_id++) {
388  for (column_id = 0; column_id < gda_data_model_get_n_columns (dm);
389  column_id++)
390  {
391  gchar *str;
392 
393  qgda_be->gda_value = (GValue*)gda_data_model_get_value_at
394  (dm, column_id, row_id);
395  str = gda_value_stringify (qgda_be->gda_value);
396  g_print ("%s\t", str);
397  g_free (str);
398  }
399  g_print("\n");
400  }
401  g_object_unref(dm);
402 }
403 
404 static void
405 qgda_class_foreach (QofObject * obj, gpointer data)
406 {
407  QGdaBackend *qgda_be;
408  GError * qgda_err;
409 
410  qgda_err = NULL;
411  qgda_be = (QGdaBackend*)data;
412  qgda_be->sql_str = g_strdup_printf(
413  "SELECT * FROM %s;", obj->e_type);
414  PINFO (" sql=%s", qgda_be->sql_str);
415  qgda_be->command = gda_command_new (qgda_be->sql_str,
416  GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS);
417  qgda_be->entities = gda_connection_execute_command (qgda_be->connection,
418  qgda_be->command, NULL, &qgda_err);
419  g_list_foreach (qgda_be->entities, load_entities, qgda_be);
420  gda_command_free (qgda_be->command);
421 }
422 
423 static void
424 qgda_db_load (QofBackend *be, QofBook *book)
425 {
426  QGdaBackend *qgda_be;
427 
428  qgda_be = (QGdaBackend*)be;
429  if (qgda_be->error)
430  return;
431  /* select all */
432  qgda_be->book = book;
433  qof_object_foreach_type(qgda_class_foreach, qgda_be);
434 }
435 
436 static void
437 qgda_write_db (QofBackend *be, QofBook *book)
438 {
439 
440 }
441 
442 static void
443 qgda_session_end (QofBackend *be)
444 {
445  QGdaBackend *qgda_be;
446 
447  qgda_be = (QGdaBackend*)be;
448  if (qgda_be->dm)
449  g_object_unref(G_OBJECT(qgda_be->dm));
450  gda_client_close_all_connections (qgda_be->client_pool);
451 }
452 
453 static void
454 qgda_destroy_backend (QofBackend *be)
455 {
456  QGdaBackend *qgda_be;
457 
458  qgda_be = (QGdaBackend*)be;
459  if (qgda_be)
460  g_object_unref(G_OBJECT(qgda_be->client_pool));
461  qof_event_unregister_handler (qgda_be->create_handler);
462  qof_event_unregister_handler (qgda_be->delete_handler);
463  g_free (be);
464  g_free (qgda_be);
465 }
466 
467 static void
468 option_cb (QofBackendOption * option, gpointer data)
469 {
470  QGdaBackend * qgda_be;
471 
472  qgda_be = (QGdaBackend *) data;
473  g_return_if_fail (qgda_be);
474  if (0 == safe_strcmp (GDA_DBNAME, option->option_name))
475  {
476  qgda_be->database_name = g_strdup (option->value);
477  PINFO (" database name = %s", qgda_be->database_name);
478  }
479  if (0 == safe_strcmp (GDA_USERNAME, option->option_name))
480  {
481  qgda_be->username = g_strdup (option->value);
482  PINFO (" username=%s", qgda_be->username);
483  }
484  if (0 == safe_strcmp (GDA_PASSWORD, option->option_name))
485  {
486  /* don't log the password! :-) */
487  qgda_be->password = g_strdup (option->value);
488  }
489  if (0 == safe_strcmp (GDA_DATASOURCE, option->option_name))
490  {
491  qgda_be->data_source_name = g_strdup (option->value);
492  }
493 }
494 
495 static void
496 load_config (QofBackend * be, KvpFrame * config)
497 {
498  QGdaBackend *qgda_be;
499 
500  ENTER (" ");
501  qgda_be = (QGdaBackend *) be;
502  g_return_if_fail (qgda_be);
503  qof_backend_option_foreach (config, option_cb, qgda_be);
504  LEAVE (" ");
505 }
506 
507 static KvpFrame *
508 get_config (QofBackend * be)
509 {
510  QofBackendOption *option;
511  QGdaBackend *qgda_be;
512 
513  if (!be)
514  {
515  return NULL;
516  }
517  ENTER (" ");
518  qgda_be = (QGdaBackend *) be;
519  g_return_val_if_fail (qgda_be, NULL);
521  option = g_new0 (QofBackendOption, 1);
522  option->option_name = GDA_DBNAME;
523  option->description =
524  _("Name of the database to use.");
525  option->tooltip =
526  _("Override the default database name with "
527  "a name of your own choice.");
528  option->type = KVP_TYPE_STRING;
529  option->value = (gpointer) qgda_be->database_name;
530  qof_backend_prepare_option (be, option);
531  g_free (option);
532  option = g_new0 (QofBackendOption, 1);
533  option->option_name = GDA_USERNAME;
534  option->description =
535  _("The username to use to access this data source.");
536  option->tooltip =
537  _("The username specified in the configuration of this "
538  "data source that provides write access to the data.");
539  option->type = KVP_TYPE_STRING;
540  option->value = (gpointer) qgda_be->username;
541  qof_backend_prepare_option (be, option);
542  g_free (option);
543  option = g_new0 (QofBackendOption, 1);
544  option->option_name = GDA_PASSWORD;
545  option->description =
546  _("Password to use with the username.");
547  option->tooltip =
548  _("The password that is to be used with the specified "
549  "username.");
550  option->type = KVP_TYPE_STRING;
551  option->value = (gpointer) qgda_be->password;
552  qof_backend_prepare_option (be, option);
553  g_free (option);
554  option = g_new0 (QofBackendOption, 1);
555  option->option_name = GDA_DATASOURCE;
556  option->description =
557  _("Name of this data source.");
558  option->tooltip =
559  _("The name of this data source as specified "
560  "in the GDA configuration.");
561  option->type = KVP_TYPE_STRING;
562  option->value = (gpointer) qgda_be->password;
563  qof_backend_prepare_option (be, option);
564  g_free (option);
565  LEAVE (" ");
566  return qof_backend_complete_frame (be);
567 }
568 
569 static QofBackend *
570 qgda_backend_new (void)
571 {
572  QGdaBackend *qgda_be;
573  QofBackend *be;
574 
575  ENTER (" ");
576  qgda_be = g_new0(QGdaBackend, 1);
577  be = (QofBackend*) qgda_be;
578  qof_backend_init(be);
579  gda_init (PACKAGE, "0.1", 0, NULL);
580  qgda_be->client_pool = gda_client_new ();
581  qgda_be->dbversion = QOF_OBJECT_VERSION;
582  be->session_begin = qgda_session_begin;
583 
584  be->session_end = qgda_session_end;
585  be->destroy_backend = qgda_destroy_backend;
586  be->load = qgda_db_load;
587  be->save_may_clobber_data = NULL;
588  be->begin = NULL;
589  /* commit: write to gda, commit undo record. */
590  be->commit = qgda_modify;
591  be->rollback = NULL;
592  /* would need a QofQuery back to QofSqlQuery conversion. */
593  be->compile_query = NULL;
594  /* unused */
595  be->free_query = NULL;
596  be->run_query = NULL;
597  be->counter = NULL;
598  /* The QOF GDA backend might be multi-user */
599  be->events_pending = NULL;
600  be->process_events = NULL;
601 
602  be->sync = qgda_write_db;
603  be->load_config = load_config;
604  be->get_config = get_config;
605  LEAVE (" ");
606 
607 /* DEBUG */
608  qgda_be->data_source_name = "QOF_DEBUG";
609  qgda_be->database_name = "URI=/home/neil/test.gda";
610  qgda_be->provider_name = "XML";
611  qgda_be->source_description = "QOF GDA debug data";
612 /* end debug */
613  return be;
614 }
615 
616 static void
617 qgda_provider_free (QofBackendProvider *prov)
618 {
619  prov->provider_name = NULL;
620  prov->access_method = NULL;
621  g_free (prov);
622 }
623 
625 {
626  QofBackendProvider *prov;
627 
628  bindtextdomain (PACKAGE, LOCALE_DIR);
629  prov = g_new0 (QofBackendProvider, 1);
630  prov->provider_name = "QOF GDA Backend Version 0.1";
631  prov->access_method = ACCESS_METHOD;
632  prov->partial_book_supported = TRUE;
633  prov->backend_new = qgda_backend_new;
634  prov->check_data_type = qgda_determine_file_type;
635  prov->provider_free = qgda_provider_free;
637 }