QOF  0.7.5
qof-sqlite.c
Go to the documentation of this file.
1 /****************************************************************
2  * qof-sqlite.c
3  *
4  * Sun Jan 15 12:52:46 2006
5  * Copyright 2006-2007 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 <errno.h>
26 #include <stdlib.h>
27 #include <time.h>
28 #include <glib/gstdio.h>
29 #include <sqlite.h>
30 #include <glib.h>
31 #include <libintl.h>
32 #include "qof.h"
33 
34 #define _(String) dgettext (GETTEXT_PACKAGE, String)
35 #define ACCESS_METHOD "sqlite"
36 
43 #define PRIORITY_HIGH 9
44 
45 #define PRIORITY_STANDARD 5
46 
47 #define PRIORITY_LOW 0
48 
49 #define QSQL_ERROR -1
50 
51 #define QSQL_KVP_TABLE "sqlite_kvp"
52 
53 #define END_DB_VERSION " dbversion int );"
54 
55 static QofLogModule log_module = QOF_MOD_SQLITE;
56 static gboolean loading = FALSE;
57 
58 typedef enum
59 {
61  SQL_NONE = 0,
75 
82 typedef struct
83 {
84  QofBackend be;
85  sqlite *sqliteh;
86  QsqlStatementType stm_type;
87  gint dbversion;
88  gint create_handler;
89  gint delete_handler;
90  const gchar *fullpath;
91  gchar *err;
92  gboolean error;
93  /* full hashtable of kvp records */
94  GHashTable *kvp_table;
95  /* hashtable relating the GUID to the kvp_id */
96  GHashTable *kvp_id;
97  /* highest kvp_id in the table */
98  glong index;
99  QofBook *book;
100  QofErrorId err_delete, err_insert, err_update, err_create;
101 } QSQLiteBackend;
102 
109 struct QsqlBuilder
110 {
112  QSQLiteBackend *qsql_be;
114  QofEntity *ent;
116  QofIdType e_type;
118  gchar *sql_str;
120  GList *dirty_list;
122  gboolean exists;
124  gboolean has_slots;
126  const QofParam *dirty;
127 };
128 
129 static inline gchar *
130 add_to_sql (gchar * sql_str, const gchar * add)
131 {
132  gchar *old;
133  old = g_strdup (sql_str);
134  g_free (sql_str);
135  sql_str = g_strconcat (old, add, NULL);
136  g_free (old);
137  return sql_str;
138 }
139 
144 static QofIdTypeConst
146 {
147  switch (n)
148  {
149  case KVP_TYPE_GINT64:
150  {
151  return QOF_TYPE_INT64;
152  break;
153  }
154  case KVP_TYPE_DOUBLE:
155  {
156  return QOF_TYPE_DOUBLE;
157  break;
158  }
159  case KVP_TYPE_NUMERIC:
160  {
161  return QOF_TYPE_NUMERIC;
162  break;
163  }
164  case KVP_TYPE_STRING:
165  {
166  return QOF_TYPE_STRING;
167  break;
168  }
169  case KVP_TYPE_GUID:
170  {
171  return QOF_TYPE_GUID;
172  break;
173  }
174 #ifndef QOF_DISABLE_DEPRECATED
175  case KVP_TYPE_TIMESPEC:
176  {
177  return QOF_TYPE_DATE;
178  break;
179  }
180 #endif
181  case KVP_TYPE_BOOLEAN:
182  {
183  return QOF_TYPE_BOOLEAN;
184  break;
185  }
186  case KVP_TYPE_TIME:
187  {
188  return QOF_TYPE_TIME;
189  break;
190  }
191  default:
192  {
193  return NULL;
194  }
195  }
196 }
197 
199 static KvpValueType
200 sql_to_kvp_helper (const gchar * type_string)
201 {
202  if (0 == safe_strcmp (QOF_TYPE_INT64, type_string))
203  return KVP_TYPE_GINT64;
204  if (0 == safe_strcmp (QOF_TYPE_DOUBLE, type_string))
205  return KVP_TYPE_DOUBLE;
206  if (0 == safe_strcmp (QOF_TYPE_NUMERIC, type_string))
207  return KVP_TYPE_NUMERIC;
208  if (0 == safe_strcmp (QOF_TYPE_STRING, type_string))
209  return KVP_TYPE_STRING;
210  if (0 == safe_strcmp (QOF_TYPE_GUID, type_string))
211  return KVP_TYPE_GUID;
212 #ifndef QOF_DISABLE_DEPRECATED
213  if (0 == safe_strcmp (QOF_TYPE_DATE, type_string))
214  return KVP_TYPE_TIMESPEC;
215 #endif
216  if (0 == safe_strcmp (QOF_TYPE_TIME, type_string))
217  return KVP_TYPE_TIME;
218  return 0;
219 }
220 
222 KvpValue *
223 string_to_kvp_value (const gchar * content, KvpValueType type)
224 {
225  gchar *tail;
226  gint64 cm_i64;
227  gdouble cm_double;
228  QofNumeric cm_numeric;
229  GUID *cm_guid;
230 #ifndef QOF_DISABLE_DEPRECATED
231  struct tm kvp_time;
232  time_t kvp_time_t;
233  Timespec cm_date;
234 #endif
235 
236  switch (type)
237  {
238  case KVP_TYPE_GINT64:
239  {
240  errno = 0;
241  cm_i64 = strtoll (content, &tail, 0);
242  if (errno == 0)
243  {
244  return kvp_value_new_gint64 (cm_i64);
245  }
246  break;
247  }
248  case KVP_TYPE_DOUBLE:
249  {
250  errno = 0;
251  cm_double = strtod (content, &tail);
252  if (errno == 0)
253  return kvp_value_new_double (cm_double);
254  break;
255  }
256  case KVP_TYPE_NUMERIC:
257  {
258  qof_numeric_from_string (content, &cm_numeric);
259  return kvp_value_new_numeric (cm_numeric);
260  break;
261  }
262  case KVP_TYPE_STRING:
263  {
264  return kvp_value_new_string (content);
265  break;
266  }
267  case KVP_TYPE_GUID:
268  {
269  cm_guid = g_new0 (GUID, 1);
270  if (TRUE == string_to_guid (content, cm_guid))
271  return kvp_value_new_guid (cm_guid);
272  break;
273  }
274  case KVP_TYPE_TIME:
275  {
276  QofDate *qd;
277  QofTime *qt;
278  KvpValue *retval;
279 
280  qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC);
281  if (qd)
282  {
283  qt = qof_date_to_qtime (qd);
284  retval = kvp_value_new_time (qt);
285  qof_date_free (qd);
286  qof_time_free (qt);
287  return retval;
288  }
289  else
290  PERR (" failed to parse date");
291  }
292 #ifndef QOF_DISABLE_DEPRECATED
293  case KVP_TYPE_TIMESPEC:
294  {
295  strptime (content, QOF_UTC_DATE_FORMAT, &kvp_time);
296  kvp_time_t = mktime (&kvp_time);
297  timespecFromTime_t (&cm_date, kvp_time_t);
298  return kvp_value_new_timespec (cm_date);
299  break;
300  }
301 #endif
302  case KVP_TYPE_BOOLEAN:
303  {
304  gboolean val;
305  val = qof_util_bool_to_int (content);
306  return kvp_value_new_boolean (val);
307  }
308  default:
309  break;
310  }
311  return NULL;
312 }
313 
315 static void
316 kvpvalue_to_sql (const gchar * key, KvpValue * val, gpointer builder)
317 {
318  QSQLiteBackend *qsql_be;
319  struct QsqlBuilder *qb;
320  KvpValueType n;
321  gchar *full_path;
322 
323  full_path = NULL;
324  ENTER (" ");
325  qb = (struct QsqlBuilder *) builder;
326  qsql_be = qb->qsql_be;
327  g_return_if_fail (key && val && qsql_be);
328  n = kvp_value_get_type (val);
329  switch (n)
330  {
331  case KVP_TYPE_GINT64:
332  case KVP_TYPE_DOUBLE:
333  case KVP_TYPE_NUMERIC:
334  case KVP_TYPE_STRING:
335  case KVP_TYPE_GUID:
336  case KVP_TYPE_TIME:
337  case KVP_TYPE_BOOLEAN:
338 #ifndef QOF_DISABLE_DEPRECATED
339  case KVP_TYPE_TIMESPEC:
340 #endif
341  {
342  /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext",
343  "type mediumtext", "value text", */
344 
345  qb->sql_str =
346  g_strdup_printf (" kvp key=%s val=%s type=%s", key,
349  DEBUG (" %s", qb->sql_str);
350  qb->has_slots = TRUE;
351  break;
352  }
353  case KVP_TYPE_FRAME:
354  {
356  kvpvalue_to_sql, qb);
357  break;
358  }
359  default:
360  {
361  PERR (" unsupported value = %d", kvp_value_get_type (val));
362  break;
363  }
364  }
365  LEAVE (" %s", qb->sql_str);
366 }
367 
368 static gchar *
369 string_param_to_sql (QofParam * param)
370 {
371  /* Handle the entity GUID. Ensure that reference GUIDs
372  must not also try to be primary keys and can be NULL. */
373  if ((0 == safe_strcmp (param->param_type, QOF_TYPE_GUID)) &&
374  (0 == safe_strcmp (param->param_name, QOF_PARAM_GUID)))
375  return g_strdup_printf (" %s char(32) primary key not null",
376  param->param_name);
377  if (0 == safe_strcmp (param->param_type, QOF_TYPE_GUID))
378  return g_strdup_printf (" %s char(32)", param->param_name);
379  /* avoid creating database fields for calculated values */
380  if (!param->param_setfcn)
381  return NULL;
382  if (0 == safe_strcmp (param->param_type, QOF_TYPE_STRING))
383  return g_strdup_printf (" %s mediumtext", param->param_name);
384  if (0 == safe_strcmp (param->param_type, QOF_TYPE_BOOLEAN))
385  return g_strdup_printf (" %s int", param->param_name);
386  if ((0 == safe_strcmp (param->param_type, QOF_TYPE_NUMERIC))
387  || (0 == safe_strcmp (param->param_type, QOF_TYPE_DOUBLE))
388  || (0 == safe_strcmp (param->param_type, QOF_TYPE_DEBCRED)))
389  {
390  return g_strdup_printf (" %s text", param->param_name);
391  }
392  if (0 == safe_strcmp (param->param_type, QOF_TYPE_INT32))
393  return g_strdup_printf (" %s int", param->param_name);
394 #ifndef QOF_DISABLE_DEPRECATED
395  if ((0 == safe_strcmp (param->param_type, QOF_TYPE_DATE)) ||
396  (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME)))
397 #else
398  if (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME))
399 #endif
400  return g_strdup_printf (" %s datetime", param->param_name);
401  if (0 == safe_strcmp (param->param_type, QOF_TYPE_CHAR))
402  return g_strdup_printf (" %s char(1)", param->param_name);
403  /* kvp data is stored separately - actually this is really
404  a no-op because entities do not need a param_setfcn for kvp data. */
405  if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
406  return g_strdup ("");
407  if (0 == safe_strcmp (param->param_type, QOF_TYPE_COLLECT))
408  return g_strdup_printf (" %s char(32)", param->param_name);
409  /* catch references */
410  return g_strdup_printf (" %s char(32)", param->param_name);
411 }
412 
418 static void
419 create_param_list (QofParam * param, gpointer builder)
420 {
421  struct QsqlBuilder *qb;
422  qb = (struct QsqlBuilder *) builder;
423 
424  /* avoid creating database fields for calculated values */
425  if (!param->param_setfcn)
426  return;
427  /* avoid setting KVP even if a param_setfcn has been set
428  because a QofSetterFunc for KVP is quite pointless. */
429  if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
430  {
431  PINFO (" kvp support tag");
432  return;
433  }
434  if (!g_str_has_suffix (qb->sql_str, "("))
435  {
436  gchar *add;
437  add = g_strconcat (", ", param->param_name, NULL);
438  qb->sql_str = add_to_sql (qb->sql_str, add);
439  g_free (add);
440  }
441  else
442  qb->sql_str = add_to_sql (qb->sql_str, param->param_name);
443 }
444 
446 static void
447 create_each_param (QofParam * param, gpointer builder)
448 {
449  gchar *value;
450  struct QsqlBuilder *qb;
451  qb = (struct QsqlBuilder *) builder;
452  GList *references;
453 
454  /* avoid creating database fields for calculated values */
455  if (!param->param_setfcn)
456  return;
457  /* avoid setting KVP even if a param_setfcn has been set
458  because a QofSetterFunc for KVP is quite pointless. */
459  if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
460  return;
461  references = qof_class_get_referenceList (qb->ent->e_type);
462  if (g_list_find (references, param))
463  {
466  QofEntity *e;
467  e = param->param_getfcn (qb->ent, param);
468  value = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
470  PINFO (" ref=%p GUID=%s", e, value);
471  }
472  else
473  value = qof_util_param_to_string (qb->ent, param);
474  if (value)
475  g_strescape (value, NULL);
476  if (!value)
477  value = g_strdup ("");
478  if (!g_str_has_suffix (qb->sql_str, "("))
479  {
480  gchar *val;
481  val = g_strconcat (", \"", value, "\"", NULL);
482  qb->sql_str = add_to_sql (qb->sql_str, val);
483  g_free (val);
484  }
485  else
486  {
487  gchar *val;
488  val = g_strconcat ("\"", value, "\"", NULL);
489  qb->sql_str = add_to_sql (qb->sql_str, val);
490  g_free (val);
491  }
492 }
493 
498 static void
499 delete_event (QofEntity * ent, QofEventId event_type,
500  gpointer handler_data, gpointer event_data)
501 {
502  QofBackend *be;
503  QSQLiteBackend *qsql_be;
504  gchar *gstr, *sql_str;
505 
506  qsql_be = (QSQLiteBackend *) handler_data;
507  be = (QofBackend *) qsql_be;
508  if (!ent)
509  return;
510  if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
511  return;
512  /* do not try to delete if only a QofObject has been loaded. */
513  if (!qof_class_is_registered (ent->e_type))
514  return;
515  switch (event_type)
516  {
517  case QOF_EVENT_DESTROY:
518  {
519  ENTER (" %s do_free=%d", ent->e_type,
520  ((QofInstance *) ent)->do_free);
521  gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
523  sql_str = g_strconcat ("DELETE from ", ent->e_type, " WHERE ",
524  QOF_TYPE_GUID, "='", gstr, "';", NULL);
525  DEBUG (" sql_str=%s", sql_str);
526  if (sqlite_exec (qsql_be->sqliteh, sql_str,
527  NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
528  {
529  qof_error_set_be (be, qsql_be->err_delete);
530  qsql_be->error = TRUE;
531  LEAVE (" error on delete:%s", qsql_be->err);
532  break;
533  }
535  /* SELECT kvp_id from QSQL_KVP_TABLE where guid = gstr */
536  LEAVE (" %d", event_type);
537  qsql_be->error = FALSE;
538  g_free (gstr);
539  break;
540  }
541  default:
542  break;
543  }
544 }
545 
547 static void
548 create_event (QofEntity * ent, QofEventId event_type,
549  gpointer handler_data, gpointer event_data)
550 {
551  QofBackend *be;
552  struct QsqlBuilder qb;
553  QSQLiteBackend *qsql_be;
554  gchar *gstr;
555  KvpFrame *slots;
556 
557  qsql_be = (QSQLiteBackend *) handler_data;
558  be = (QofBackend *) qsql_be;
559  if (!ent)
560  return;
561  if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
562  return;
563  if (!qof_class_is_registered (ent->e_type))
564  return;
565  switch (event_type)
566  {
567  case QOF_EVENT_CREATE:
568  {
569  gchar *tmp;
570  ENTER (" create:%s", ent->e_type);
571  gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
573  ent), gstr);
574  DEBUG (" guid=%s", gstr);
575  qb.ent = ent;
576  qb.sql_str =
577  g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
578  qof_class_param_foreach (ent->e_type, create_param_list, &qb);
579  tmp = g_strconcat (") VALUES (\"", gstr, "\" ", NULL);
580  qb.sql_str = add_to_sql (qb.sql_str, tmp);
581  g_free (tmp);
582  qof_class_param_foreach (ent->e_type, create_each_param, &qb);
583  qb.sql_str = add_to_sql (qb.sql_str, ");");
584  DEBUG (" sql_str=%s", qb.sql_str);
585  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
586  NULL, &qb, &qsql_be->err) != SQLITE_OK)
587  {
588  qof_error_set_be (be, qsql_be->err_insert);
589  qsql_be->error = TRUE;
590  PERR (" error on create_event:%s", qsql_be->err);
591  }
592  else
593  {
594  ((QofInstance *) ent)->dirty = FALSE;
595  qsql_be->error = FALSE;
596  g_free (qb.sql_str);
597  g_free (gstr);
598  LEAVE (" ");
599  break;
600  }
601  /* insert sqlite_kvp data */
602  slots = qof_instance_get_slots ((QofInstance *) ent);
603  if (slots)
604  {
605  /* id, guid, path, type, value */
606  qb.sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE,
607  " (kvp_id \"", gstr, "\", ", NULL);
609  qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION);
610  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
611  NULL, &qb, &qsql_be->err) != SQLITE_OK)
612  {
613  qof_error_set_be (be, qsql_be->err_insert);
614  qsql_be->error = TRUE;
615  PERR (" error on KVP create_event:%s", qsql_be->err);
616  }
617  else
618  {
619  ((QofInstance *) ent)->dirty = FALSE;
620  qsql_be->error = FALSE;
621  g_free (qb.sql_str);
622  g_free (gstr);
623  LEAVE (" ");
624  break;
625  }
626  }
627  g_free (qb.sql_str);
628  g_free (gstr);
629  LEAVE (" ");
630  break;
631  }
632  default:
633  break;
634  }
635 }
636 
637 static void
638 qsql_modify (QofBackend * be, QofInstance * inst)
639 {
640  struct QsqlBuilder qb;
641  QSQLiteBackend *qsql_be;
642  gchar *gstr, *param_str;
643  KvpFrame *slots;
644 
645  qsql_be = (QSQLiteBackend *) be;
646  qb.qsql_be = qsql_be;
647  if (!inst)
648  return;
649  if (!inst->param)
650  return;
651  if (loading)
652  return;
653  if (!inst->param->param_setfcn)
654  return;
655  ENTER (" modified %s param:%s", ((QofEntity *) inst)->e_type,
656  inst->param->param_name);
657  gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
659  qb.ent = (QofEntity *) inst;
660  param_str = qof_util_param_to_string (qb.ent, inst->param);
661  if (param_str)
662  g_strescape (param_str, NULL);
663  qb.sql_str = g_strconcat ("UPDATE ", qb.ent->e_type, " SET ",
664  inst->param->param_name, " = \"", param_str,
665  "\" WHERE ", QOF_TYPE_GUID, "='", gstr, "';", NULL);
666  DEBUG (" sql_str=%s param_Str=%s", qb.sql_str, param_str);
667  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
668  NULL, &qb, &qsql_be->err) != SQLITE_OK)
669  {
670  qof_error_set_be (be, qsql_be->err_update);
671  qsql_be->error = TRUE;
672  PERR (" error on modify:%s", qsql_be->err);
673  }
674  else
675  {
676  inst->dirty = FALSE;
677  g_free (qb.sql_str);
678  g_free (gstr);
679  qsql_be->error = FALSE;
680  LEAVE (" ");
681  return;
682  }
683  /* modify slot data */
684  slots = qof_instance_get_slots (inst);
685  if (slots)
686  {
687  /* update and delete KVP data */
688  /* id, guid, path, type, value */
689  qb.sql_str = g_strconcat ("UPDATE ", QSQL_KVP_TABLE,
690  " SET (kvp_id \"", gstr, "\", ", NULL);
692  qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION);
693  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
694  NULL, &qb, &qsql_be->err) != SQLITE_OK)
695  {
696  qof_error_set_be (be, qsql_be->err_insert);
697  qsql_be->error = TRUE;
698  PERR (" error on KVP create_event:%s", qsql_be->err);
699  }
700  else
701  {
702  ((QofInstance *) qb.ent)->dirty = FALSE;
703  qsql_be->error = FALSE;
704  g_free (qb.sql_str);
705  }
706  }
707  g_free (gstr);
708  LEAVE (" ");
709 }
710 
712 static gint
713 record_foreach (gpointer builder, gint col_num, gchar ** strings,
714  gchar ** columnNames)
715 {
716  QSQLiteBackend *qsql_be;
717  struct QsqlBuilder *qb;
718  const QofParam *param;
719  QofInstance *inst;
720  QofEntity *ent;
721  gint i;
722 
723  g_return_val_if_fail (builder, QSQL_ERROR);
724  qb = (struct QsqlBuilder *) builder;
725  qsql_be = qb->qsql_be;
727  inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book);
728  ent = &inst->entity;
729  for (i = 0; i < col_num; i++)
730  {
731  /* get param and set as string */
732  param = qof_class_get_parameter (qb->e_type, columnNames[i]);
733  if (!param)
734  continue;
735  /* set the inst->param entry */
736  inst->param = param;
737  if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID))
738  {
739  GUID *guid;
740  guid = guid_malloc ();
741  if (!string_to_guid (strings[i], guid))
742  {
743  DEBUG (" set guid failed:%s", strings[i]);
744  return QSQL_ERROR;
745  }
746  qof_entity_set_guid (ent, guid);
747  }
748  if (strings[i])
749  qof_util_param_set_string (ent, param, strings[i]);
750  }
751  qof_event_resume ();
752  return SQLITE_OK;
753 }
754 
755 /* used by create/insert */
756 static void
757 string_param_foreach (QofParam * param, gpointer builder)
758 {
759  struct QsqlBuilder *qb;
760  QSQLiteBackend *qsql_be;
761  gchar *p_str, *old;
762 
763  qb = (struct QsqlBuilder *) builder;
764  qsql_be = qb->qsql_be;
765  if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
766  return;
767  p_str = string_param_to_sql (param);
768  /* skip empty values (no param_setfcn) */
769  if (!p_str)
770  return;
771  old = g_strconcat (p_str, ",", NULL);
772  qb->sql_str = add_to_sql (qb->sql_str, old);
773  g_free (old);
774  g_free (p_str);
775 }
776 
777 static void
778 update_param_foreach (QofParam * param, gpointer builder)
779 {
780  struct QsqlBuilder *qb;
781  gchar *value, *add;
782 
783  qb = (struct QsqlBuilder *) builder;
784  if (param != qb->dirty)
785  return;
786  /* update table set name=val,name=val where guid=gstr; */
787  value = qof_util_param_to_string (qb->ent, param);
788  if (value)
789  g_strescape (value, NULL);
790  if (!value)
791  value = g_strdup ("");
792  if (g_str_has_suffix (qb->sql_str, " "))
793  {
794  add = g_strconcat (param->param_name, "=\"", value, "\"", NULL);
795  qb->sql_str = add_to_sql (qb->sql_str, add);
796  g_free (add);
797  }
798  else
799  {
800  add =
801  g_strconcat (",", param->param_name, "=\"", value, "\"", NULL);
802  qb->sql_str = add_to_sql (qb->sql_str, add);
803  g_free (add);
804  }
805 }
806 
807 static void
808 update_dirty (gpointer value, gpointer builder)
809 {
810  QofInstance *inst;
811  QofEntity *ent;
812  struct QsqlBuilder *qb;
813  QSQLiteBackend *qsql_be;
814  QofBackend *be;
815  gchar *gstr, *param_str;
816 
817  qb = (struct QsqlBuilder *) builder;
818  qsql_be = qb->qsql_be;
819  be = (QofBackend *) qsql_be;
820  ent = (QofEntity *) value;
821  inst = (QofInstance *) ent;
822  if (!inst->dirty)
823  return;
824  ENTER (" ");
825  gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
827  /* qof_class_param_foreach */
828  qb->sql_str = g_strdup_printf ("UPDATE %s SET ", ent->e_type);
829  qof_class_param_foreach (ent->e_type, update_param_foreach, qb);
830  param_str = g_strdup_printf ("WHERE %s=\"%s\";", QOF_TYPE_GUID, gstr);
831  qb->sql_str = add_to_sql (qb->sql_str, param_str);
832  g_free (param_str);
833  DEBUG (" update=%s", qb->sql_str);
834  if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
835  NULL, qb, &qsql_be->err) != SQLITE_OK)
836  {
837  qof_error_set_be (be, qsql_be->err_update);
838  qsql_be->error = TRUE;
839  PERR (" error on update_dirty:%s", qsql_be->err);
840  }
841  else
842  {
844  qsql_be->error = FALSE;
845  inst->dirty = FALSE;
846  }
847  LEAVE (" ");
848  g_free (gstr);
849  return;
850 }
851 
852 static gint
853 create_dirty_list (gpointer builder, gint col_num, gchar ** strings,
854  gchar ** columnNames)
855 {
856  struct QsqlBuilder *qb;
857  QofInstance *inst;
858  const QofParam *param;
859  gchar *value, *columnName, *tmp;
860 
861  param = NULL;
862  qb = (struct QsqlBuilder *) builder;
863  /* qb->ent is the live data, strings is the sqlite data */
864  inst = (QofInstance *) qb->ent;
865  qb->exists = TRUE;
866  if (!inst->dirty)
867  return SQLITE_OK;
868  columnName = columnNames[col_num];
869  tmp = strings[col_num];
870  param = qof_class_get_parameter (qb->ent->e_type, columnName);
871  if (!param)
872  return SQLITE_OK;
873  value = qof_util_param_to_string (qb->ent, param);
874  qb->dirty = param;
875  qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent);
876  DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list));
877  return SQLITE_OK;
878 }
879 
880 static gint
881 mark_entity (gpointer builder, gint col_num, gchar ** strings,
882  gchar ** columnNames)
883 {
884  struct QsqlBuilder *qb;
885 
886  qb = (struct QsqlBuilder *) builder;
887  qb->exists = TRUE;
888  return SQLITE_OK;
889 }
890 
891 static void
892 qsql_create (QofBackend * be, QofInstance * inst)
893 {
894  gchar *gstr;
895  QSQLiteBackend *qsql_be;
896  struct QsqlBuilder qb;
897  QofEntity *ent;
898  KvpFrame *slots;
899 
900  qsql_be = (QSQLiteBackend *) be;
901  if (!inst)
902  return;
903  if (loading)
904  return;
905  ent = (QofEntity *) inst;
907  qb.has_slots = FALSE;
908  ENTER (" %s", ent->e_type);
909  gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
911  qb.sql_str =
912  g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";",
913  ent->e_type, gstr);
914  PINFO (" check exists: %s", qb.sql_str);
915  qb.ent = ent;
916  qb.dirty_list = NULL;
917  qb.exists = FALSE;
918  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
919  mark_entity, &qb, &qsql_be->err) != SQLITE_OK)
920  {
921  qof_error_set_be (be, qsql_be->err_update);
922  qsql_be->error = TRUE;
923  PERR (" error on select :%s", qsql_be->err);
924  }
925  if (!qb.exists)
926  {
927  gchar *add;
928  /* create new entity */
929  qb.sql_str =
930  g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
931  qof_class_param_foreach (ent->e_type, create_param_list, &qb);
932  add = g_strconcat (") VALUES (\"", gstr, "\"", NULL);
933  qb.sql_str = add_to_sql (qb.sql_str, add);
934  g_free (add);
935  qof_class_param_foreach (ent->e_type, create_each_param, &qb);
936  qb.sql_str = add_to_sql (qb.sql_str, ");");
937  DEBUG (" sql_str= %s", qb.sql_str);
938  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
939  NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
940  {
941  qof_error_set_be (be, qsql_be->err_insert);
942  qsql_be->error = TRUE;
943  PERR (" error creating new entity:%s", qsql_be->err);
944  }
945  /* KVP here */
946  slots = qof_instance_get_slots ((QofInstance *) ent);
947  if (slots)
948  {
949  /* id, guid, path, type, value */
950  qb.sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE,
951  " (kvp_id, \"", gstr, "\", ", NULL);
953  qb.sql_str = add_to_sql (qb.sql_str, ");");
954  }
955  if (qb.has_slots)
956  {
957  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
958  NULL, &qb, &qsql_be->err) != SQLITE_OK)
959  {
960  qof_error_set_be (be, qsql_be->err_insert);
961  qsql_be->error = TRUE;
962  PERR (" error on KVP create_event:%s:%s", qsql_be->err,
963  qb.sql_str);
964  }
965  else
966  {
967  ((QofInstance *) ent)->dirty = FALSE;
968  qsql_be->error = FALSE;
969  }
970  }
971  }
972  g_free (qb.sql_str);
973  g_free (gstr);
974  qof_event_resume ();
975  LEAVE (" ");
976 }
977 
978 static void
979 check_state (QofEntity * ent, gpointer builder)
980 {
981  gchar *gstr;
982  QSQLiteBackend *qsql_be;
983  struct QsqlBuilder *qb;
984  QofBackend *be;
985  QofInstance *inst;
986  KvpFrame *slots;
987 
988  qb = (struct QsqlBuilder *) builder;
989  qsql_be = qb->qsql_be;
990  be = (QofBackend *) qsql_be;
991  inst = (QofInstance *) ent;
992  if (!inst->dirty)
993  return;
994  /* check if this entity already exists */
995  gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
997  qb->sql_str =
998  g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";",
999  ent->e_type, gstr);
1000  qb->ent = ent;
1001  qb->dirty_list = NULL;
1002  /* assume entity does not yet exist in backend,
1003  e.g. being copied from another session. */
1004  qb->exists = FALSE;
1005  qb->qsql_be = qsql_be;
1006  /* update each dirty instance */
1007  /* Make a GList of dirty instances
1008  Don't update during a SELECT,
1009  UPDATE will fail with DB_LOCKED */
1010  if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
1011  create_dirty_list, qb, &qsql_be->err) != SQLITE_OK)
1012  {
1013  qof_error_set_be (be, qsql_be->err_update);
1014  qsql_be->error = TRUE;
1015  PERR (" error on check_state:%s", qsql_be->err);
1016  }
1017  if (!qb->exists)
1018  {
1019  gchar *add;
1020  /* create new entity */
1021  qb->sql_str =
1022  g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
1023  qof_class_param_foreach (ent->e_type, create_param_list, &qb);
1024  add = g_strconcat (") VALUES (\"", gstr, "\" ", NULL);
1025  qb->sql_str = add_to_sql (qb->sql_str, add);
1026  g_free (add);
1027  qof_class_param_foreach (ent->e_type, create_each_param, &qb);
1028  qb->sql_str = add_to_sql (qb->sql_str, ");");
1029  DEBUG (" sql_str= %s", qb->sql_str);
1030  if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
1031  NULL, qb, &qsql_be->err) != SQLITE_OK)
1032  {
1033  qof_error_set_be (be, qsql_be->err_insert);
1034  qsql_be->error = TRUE;
1035  PERR (" error on check_state create_new:%s", qsql_be->err);
1036  }
1037  g_free (qb->sql_str);
1038  /* create KVP data too */
1039  slots = qof_instance_get_slots ((QofInstance *) ent);
1040  if (slots)
1041  {
1042  /* id, guid, path, type, value */
1043  qb->sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE,
1044  " (kvp_id \"", gstr, "\", ", NULL);
1046  qb->sql_str = add_to_sql (qb->sql_str, END_DB_VERSION);
1047  if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
1048  NULL, &qb, &qsql_be->err) != SQLITE_OK)
1049  {
1050  qof_error_set_be (be, qsql_be->err_insert);
1051  qsql_be->error = TRUE;
1052  PERR (" error on KVP create_event:%s", qsql_be->err);
1053  }
1054  else
1055  {
1056  ((QofInstance *) ent)->dirty = FALSE;
1057  qsql_be->error = FALSE;
1058  }
1059  }
1060  }
1061  /* update instead */
1062  g_list_foreach (qb->dirty_list, update_dirty, &qb);
1063  g_free (qb->sql_str);
1064  g_free (gstr);
1065 }
1066 
1075 static gint
1076 build_kvp_table (gpointer builder, gint col_num, gchar ** strings,
1077  gchar ** columnNames)
1078 {
1079  QSQLiteBackend *qsql_be;
1080  struct QsqlBuilder *qb;
1081  KvpFrame *frame;
1082  KvpValueType type;
1083  KvpValue *value;
1084  glong max;
1085  gchar *tail;
1086 
1087  g_return_val_if_fail (builder, QSQL_ERROR);
1088  qb = (struct QsqlBuilder *) builder;
1089  max = 0;
1090  qsql_be = qb->qsql_be;
1091  g_return_val_if_fail ((col_num < 4), QSQL_ERROR);
1092  g_return_val_if_fail (strings[2], QSQL_ERROR);
1093  frame = kvp_frame_new ();
1094  /* columnNames = fields strings = values
1095  [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value
1096  get type from type_string */
1097  type = sql_to_kvp_helper (strings[3]);
1098  if (type == 0)
1099  {
1100  PERR (" invalid type returned from kvp table");
1101  return QSQL_ERROR;
1102  }
1103  /* use the type to make a KvpValue from value */
1104  value = string_to_kvp_value (strings[4], type);
1105  if (!value)
1106  {
1107  PERR (" invalid KvpValue for type: %d", type);
1108  return QSQL_ERROR;
1109  }
1110  /* add the KvpValue to the frame at path */
1111  kvp_frame_set_value (frame, strings[2], value);
1112  /* index the frame under the entity GUID */
1113  g_hash_table_insert (qsql_be->kvp_table, strings[1], frame);
1114  /* index the guid under the kvp_id */
1115  g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]);
1116  errno = 0;
1117  max = strtol (strings[0], &tail, 0);
1118  if (errno == 0)
1119  {
1120  qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index;
1121  }
1122  return SQLITE_OK;
1123 }
1124 
1126 static void
1127 qsql_load_kvp (QSQLiteBackend * qsql_be)
1128 {
1129  struct QsqlBuilder qb;
1130  QofBackend *be;
1131  gint sq_code;
1132 
1133  g_return_if_fail (qsql_be);
1134  sq_code = SQLITE_OK;
1135  be = (QofBackend *) qsql_be;
1136  qb.sql_str =
1137  g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE);
1138  sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table,
1139  &qb, &qsql_be->err);
1140  /* catch older files without a sqlite_kvp table */
1141  if (sq_code == SQLITE_ERROR)
1142  {
1143  g_free (qb.sql_str);
1144  qb.sql_str =
1145  g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
1146  QSQL_KVP_TABLE, "kvp_id int primary key not null",
1147  "guid char(32)", "path mediumtext", "type mediumtext",
1148  "value text", END_DB_VERSION);
1149  PINFO (" creating kvp table. sql=%s", qb.sql_str);
1150  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
1151  record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
1152  {
1153  qsql_be->error = TRUE;
1154  PERR (" unable to create kvp table:%s", qsql_be->err);
1155  }
1156  }
1157  else if (sq_code != SQLITE_OK)
1158  {
1159  qof_error_set_be (be, qsql_be->err_create);
1160  qsql_be->error = TRUE;
1161  PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code);
1162  }
1163  g_free (qb.sql_str);
1164 }
1165 
1167 static void
1168 qsql_class_foreach (QofObject * obj, gpointer data)
1169 {
1170  struct QsqlBuilder qb;
1171  QSQLiteBackend *qsql_be;
1172  QofBackend *be;
1173 
1174  qsql_be = (QSQLiteBackend *) data;
1175  be = (QofBackend *) qsql_be;
1176  qb.qsql_be = qsql_be;
1177  qb.e_type = obj->e_type;
1178  ENTER (" obj_type=%s", qb.e_type);
1179  switch (qsql_be->stm_type)
1180  {
1181  case SQL_NONE:
1182  case SQL_INSERT:
1183  case SQL_DELETE:
1184  case SQL_UPDATE:
1185  {
1186  break;
1187  }
1188  case SQL_CREATE:
1189  {
1190  /* KVP is handled separately */
1191  qb.sql_str =
1192  g_strdup_printf ("CREATE TABLE %s (", obj->e_type);
1193  qof_class_param_foreach (obj->e_type, string_param_foreach,
1194  &qb);
1195  qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION);
1196  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
1197  NULL, NULL, &qsql_be->err) != SQLITE_OK)
1198  {
1199  qof_error_set_be (be, qsql_be->err_create);
1200  qsql_be->error = TRUE;
1201  PERR (" error on SQL_CREATE:%s", qsql_be->err);
1202  }
1203  g_free (qb.sql_str);
1204  break;
1205  }
1206  case SQL_LOAD:
1207  {
1208  qb.sql_str =
1209  g_strdup_printf ("SELECT * FROM %s;", obj->e_type);
1210  PINFO (" sql=%s", qb.sql_str);
1211  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
1212  record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
1213  {
1214  qsql_be->error = TRUE;
1215  PERR (" error on SQL_LOAD:%s", qsql_be->err);
1216  }
1217  break;
1218  }
1219  case SQL_WRITE:
1220  {
1221  if (!qof_book_not_saved (qsql_be->book))
1222  break;
1223  qof_object_foreach (obj->e_type, qsql_be->book, check_state,
1224  &qb);
1225  break;
1226  }
1227  }
1228  LEAVE (" ");
1229 }
1230 
1231 static void
1232 qsql_backend_createdb (QofBackend * be, QofSession * session)
1233 {
1234  FILE *f;
1235  QSQLiteBackend *qsql_be;
1236  struct QsqlBuilder qb;
1237 
1238  g_return_if_fail (be || session);
1239  ENTER (" ");
1240  qsql_be = (QSQLiteBackend *) be;
1241  qsql_be->stm_type = SQL_CREATE;
1242  qb.qsql_be = qsql_be;
1243  qsql_be->book = qof_session_get_book (session);
1244  DEBUG (" create_file %s", qsql_be->fullpath);
1245  f = fopen (qsql_be->fullpath, "a+");
1246  if (f)
1247  fclose (f);
1248  else
1249  {
1251  (_("Unable to open the output file '%s' - do you have "
1252  "permission to create this file?"), TRUE));
1253  qsql_be->error = TRUE;
1254  LEAVE (" unable to create new file '%s'", qsql_be->fullpath);
1255  return;
1256  }
1257  qsql_be->sqliteh =
1258  sqlite_open (qsql_be->fullpath, 0644, &qsql_be->err);
1259  if (!qsql_be->sqliteh)
1260  {
1261  qof_error_set_be (be, qsql_be->err_create);
1262  qsql_be->error = TRUE;
1263  LEAVE (" unable to open sqlite:%s", qsql_be->err);
1264  return;
1265  }
1267  /* create the KVP table here
1268  preset table name, internal_id, guid_as_string, path, type, value
1269  */
1270  qb.sql_str =
1271  g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
1272  QSQL_KVP_TABLE, "kvp_id int primary key not null",
1273  "guid char(32)", "path mediumtext", "type mediumtext",
1274  "value text", END_DB_VERSION);
1275  PINFO (" sql=%s", qb.sql_str);
1276  if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
1277  record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
1278  {
1279  qsql_be->error = TRUE;
1280  PERR (" unable to create kvp table:%s", qsql_be->err);
1281  }
1282  g_free (qb.sql_str);
1283  LEAVE (" ");
1284 }
1285 
1286 static void
1287 qsql_backend_opendb (QofBackend * be, QofSession * session)
1288 {
1289  QSQLiteBackend *qsql_be;
1290 
1291  g_return_if_fail (be || session);
1292  ENTER (" ");
1293  qsql_be = (QSQLiteBackend *) be;
1294  qsql_be->sqliteh =
1295  sqlite_open (qsql_be->fullpath, 0666, &qsql_be->err);
1296  if (!qsql_be->sqliteh)
1297  {
1298  qof_error_set_be (be, qof_error_register
1299  (_("Unable to open the sqlite database '%s'."), TRUE));
1300  qsql_be->error = TRUE;
1301  PERR (" %s", qsql_be->err);
1302  }
1303  LEAVE (" %s", qsql_be->fullpath);
1304 }
1305 
1306 static void
1307 qsqlite_session_begin (QofBackend * be, QofSession * session,
1308  const gchar * book_path, gboolean ignore_lock,
1309  gboolean create_if_nonexistent)
1310 {
1311  QSQLiteBackend *qsql_be;
1312  gchar **pp;
1313  struct stat statinfo;
1314  gint stat_val;
1315 
1316  g_return_if_fail (be);
1317  ENTER (" book_path=%s", book_path);
1318  qsql_be = (QSQLiteBackend *) be;
1319  qsql_be->fullpath = NULL;
1320  if (book_path == NULL)
1321  {
1322  qof_error_set_be (be, qof_error_register
1323  (_("Please provide a filename for sqlite."), FALSE));
1324  qsql_be->error = TRUE;
1325  LEAVE (" bad URL");
1326  return;
1327  }
1328  /* book_path => sqlite_file_name */
1329  pp = g_strsplit (book_path, ":", 2);
1330  if (0 == safe_strcmp (pp[0], ACCESS_METHOD))
1331  {
1332  qsql_be->fullpath = g_strdup (pp[1]);
1333  g_strfreev (pp);
1334  }
1335  else
1336  qsql_be->fullpath = g_strdup (book_path);
1337  be->fullpath = g_strdup (qsql_be->fullpath);
1338  PINFO (" final path = %s", qsql_be->fullpath);
1339  stat_val = g_stat (qsql_be->fullpath, &statinfo);
1340  if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0)
1341  qsql_backend_createdb (be, session);
1342  if (!qsql_be->error)
1343  qsql_backend_opendb (be, session);
1344  if (qof_error_check_be (be) || qsql_be->error)
1345  {
1346  LEAVE (" open failed");
1347  return;
1348  }
1349  qsql_be->create_handler =
1351  qsql_be->delete_handler =
1353  LEAVE (" db=%s", qsql_be->fullpath);
1354 }
1355 
1356 static void
1357 qsqlite_db_load (QofBackend * be, QofBook * book)
1358 {
1359  QSQLiteBackend *qsql_be;
1360 
1361  g_return_if_fail (be);
1362  ENTER (" ");
1363  loading = TRUE;
1364  qsql_be = (QSQLiteBackend *) be;
1365  qsql_be->stm_type = SQL_LOAD;
1366  qsql_be->book = book;
1367  /* iterate over registered objects */
1369  qsql_load_kvp (qsql_be);
1370  loading = FALSE;
1371  LEAVE (" ");
1372 }
1373 
1374 static void
1375 qsqlite_write_db (QofBackend * be, QofBook * book)
1376 {
1377  QSQLiteBackend *qsql_be;
1378 
1379  g_return_if_fail (be);
1380  qsql_be = (QSQLiteBackend *) be;
1381  qsql_be->stm_type = SQL_WRITE;
1382  qsql_be->book = book;
1383  /* update each record with current state */
1385  /* update KVP */
1386 }
1387 
1388 static gboolean
1389 qsql_determine_file_type (const gchar * path)
1390 {
1391  if (!path)
1392  return FALSE;
1393  return TRUE;
1394 }
1395 
1396 static void
1397 qsqlite_session_end (QofBackend * be)
1398 {
1399  QSQLiteBackend *qsql_be;
1400 
1401  g_return_if_fail (be);
1402  qsql_be = (QSQLiteBackend *) be;
1403  if (qsql_be->sqliteh)
1404  sqlite_close (qsql_be->sqliteh);
1405 }
1406 
1407 static void
1408 qsqlite_destroy_backend (QofBackend * be)
1409 {
1410  QSQLiteBackend *qsql_be;
1411 
1412  g_return_if_fail (be);
1413  qsql_be = (QSQLiteBackend *) be;
1414  g_hash_table_destroy (qsql_be->kvp_table);
1415  g_hash_table_destroy (qsql_be->kvp_id);
1416  qof_event_unregister_handler (qsql_be->create_handler);
1417  qof_event_unregister_handler (qsql_be->delete_handler);
1418  g_free (be);
1419  g_free (qsql_be);
1420 }
1421 
1422 static void
1423 qsql_provider_free (QofBackendProvider * prov)
1424 {
1425  prov->provider_name = NULL;
1426  prov->access_method = NULL;
1427  g_free (prov);
1428 }
1429 
1445 static QofBackend *
1447 {
1448  QSQLiteBackend *qsql_be;
1449  QofBackend *be;
1450 
1451  ENTER (" ");
1452  qsql_be = g_new0 (QSQLiteBackend, 1);
1453  be = (QofBackend *) qsql_be;
1454  qof_backend_init (be);
1455  qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal);
1456  qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal);
1457  qsql_be->dbversion = QOF_OBJECT_VERSION;
1458  qsql_be->stm_type = SQL_NONE;
1459  qsql_be->err_delete =
1460  qof_error_register (_("Unable to delete record."), FALSE);
1461  qsql_be->err_create =
1462  qof_error_register (_("Unable to create record."), FALSE);
1463  qsql_be->err_insert =
1464  qof_error_register (_("Unable to insert a new record."), FALSE);
1465  qsql_be->err_update =
1466  qof_error_register (_("Unable to update existing record."), FALSE);
1467  be->session_begin = qsqlite_session_begin;
1468 
1469  be->session_end = qsqlite_session_end;
1470  be->destroy_backend = qsqlite_destroy_backend;
1471  be->load = qsqlite_db_load;
1472  be->save_may_clobber_data = NULL;
1473  /* begin: create an empty entity if none exists,
1474  even if events are suspended. */
1475  be->begin = qsql_create;
1476  /* commit: write to sqlite, commit undo record. */
1477  be->commit = qsql_modify;
1478  be->rollback = NULL;
1479  /* would need a QofQuery back to QofSqlQuery conversion. */
1480  be->compile_query = NULL;
1481  /* unused */
1482  be->free_query = NULL;
1483  be->run_query = NULL;
1484  be->counter = NULL;
1485  /* The QOF SQLite backend is not multi-user - all QOF users are the same. */
1486  be->events_pending = NULL;
1487  be->process_events = NULL;
1488 
1489  be->sync = qsqlite_write_db;
1490  be->load_config = NULL;
1491  be->get_config = NULL;
1492  LEAVE (" ");
1493  return be;
1494 }
1495 
1496 void
1498 {
1499  QofBackendProvider *prov;
1500 
1501  ENTER (" ");
1502  bindtextdomain (PACKAGE, LOCALE_DIR);
1503  prov = g_new0 (QofBackendProvider, 1);
1504  prov->provider_name = "QOF SQLite Backend Version 0.3";
1505  prov->access_method = ACCESS_METHOD;
1506  prov->partial_book_supported = TRUE;
1507  prov->backend_new = qsql_backend_new;
1508  prov->check_data_type = qsql_determine_file_type;
1509  prov->provider_free = qsql_provider_free;
1511  LEAVE (" ");
1512 }
1513 
1514 /* ================= END OF FILE =================== */