QOF  0.8.7
qof-sqlite.c
Go to the documentation of this file.
00001 /****************************************************************
00002  *            qof-sqlite.c
00003  *
00004  *  Sun Jan 15 12:52:46 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 <errno.h>
00026 #include <stdlib.h>
00027 #include <time.h>
00028 #include <glib/gstdio.h>
00029 #include <sqlite.h>
00030 #include <glib.h>
00031 #include <libintl.h>
00032 #include "qof.h"
00033 #include "qofsql-p.h"
00034 #include "qof-sqlite.h"
00035 #include "kvputil-p.h"
00036 
00037 #define _(String) dgettext (GETTEXT_PACKAGE, String)
00038 #define ACCESS_METHOD "sqlite"
00039 
00046 #define PRIORITY_HIGH       9
00047 
00048 #define PRIORITY_STANDARD   5
00049 
00050 #define PRIORITY_LOW        0
00051 
00052 #define QSQL_ERROR          -1
00053 
00054 #undef QSQL_KVP_TABLE
00055 #define QSQL_KVP_TABLE "sqlite_kvp"
00056 
00057 #define END_DB_VERSION " dbversion int );"
00058 
00059 static QofLogModule log_module = QOF_MOD_SQLITE;
00060 static gboolean loading = FALSE;
00061 
00068 typedef struct
00069 {
00070     QofBackend be;
00071     sqlite *sqliteh;
00072     QsqlStatementType stm_type;
00073     gint dbversion;
00074     gint create_handler;
00075     gint delete_handler;
00076     const gchar *fullpath;
00077     gchar *err;
00078     gboolean error;
00079     /* full hashtable of kvp records */
00080     GHashTable *kvp_table;
00081     /* hashtable relating the GUID to the kvp_id */
00082     GHashTable *kvp_id;
00083     /* highest kvp_id in the table */
00084     gulong index;
00085     QofBook *book;
00086     QofErrorId err_delete, err_insert, err_update, err_create;
00087 } QSQLiteBackend;
00088 
00095 struct QsqlBuilder
00096 {
00098     QSQLiteBackend *qsql_be;
00100     QofEntity *ent;
00102     QofIdType e_type;
00104     gchar *sql_str;
00106     GList *dirty_list;
00108     gboolean exists;
00110     gboolean has_slots;
00112     const QofParam *dirty;
00113 };
00114 
00116 static KvpValue *
00117 string_to_kvp_value (const gchar * content, KvpValueType type)
00118 {
00119     gchar *tail;
00120     gint64 cm_i64;
00121     gdouble cm_double;
00122     QofNumeric cm_numeric;
00123     GUID *cm_guid;
00124 
00125     switch (type)
00126     {
00127     case KVP_TYPE_GINT64:
00128         {
00129             errno = 0;
00130             cm_i64 = strtoll (content, &tail, 0);
00131             if (errno == 0)
00132             {
00133                 return kvp_value_new_gint64 (cm_i64);
00134             }
00135             break;
00136         }
00137     case KVP_TYPE_DOUBLE:
00138         {
00139             errno = 0;
00140             cm_double = strtod (content, &tail);
00141             if (errno == 0)
00142                 return kvp_value_new_double (cm_double);
00143             break;
00144         }
00145     case KVP_TYPE_NUMERIC:
00146         {
00147             qof_numeric_from_string (content, &cm_numeric);
00148             return kvp_value_new_numeric (cm_numeric);
00149             break;
00150         }
00151     case KVP_TYPE_STRING:
00152         {
00153             return kvp_value_new_string (content);
00154             break;
00155         }
00156     case KVP_TYPE_GUID:
00157         {
00158             cm_guid = g_new0 (GUID, 1);
00159             if (TRUE == string_to_guid (content, cm_guid))
00160                 return kvp_value_new_guid (cm_guid);
00161             break;
00162         }
00163     case KVP_TYPE_TIME:
00164         {
00165             QofDate *qd;
00166             QofTime *qt;
00167             KvpValue *retval;
00168 
00169             qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC);
00170             if (qd)
00171             {
00172                 qt = qof_date_to_qtime (qd);
00173                 retval = kvp_value_new_time (qt);
00174                 qof_date_free (qd);
00175                 qof_time_free (qt);
00176                 return retval;
00177             }
00178             else
00179                 PERR (" failed to parse date");
00180         }
00181     case KVP_TYPE_BOOLEAN:
00182         {
00183             gboolean val;
00184             val = qof_util_bool_to_int (content);
00185             return kvp_value_new_boolean (val);
00186         }
00187     default:
00188         break;
00189     }
00190     return NULL;
00191 }
00192 
00194 static G_GNUC_UNUSED void
00195 kvpvalue_to_sql (const gchar * key, KvpValue * val, gpointer builder)
00196 {
00197     QSQLiteBackend *qsql_be;
00198     struct QsqlBuilder *qb;
00199     KvpValueType n;
00200 
00201     ENTER (" ");
00202     qb = (struct QsqlBuilder *) builder;
00203     qsql_be = qb->qsql_be;
00204     g_return_if_fail (key && val && qsql_be);
00205     n = kvp_value_get_type (val);
00206     switch (n)
00207     {
00208     case KVP_TYPE_GINT64:
00209     case KVP_TYPE_DOUBLE:
00210     case KVP_TYPE_NUMERIC:
00211     case KVP_TYPE_STRING:
00212     case KVP_TYPE_GUID:
00213     case KVP_TYPE_TIME:
00214     case KVP_TYPE_BOOLEAN:
00215         {
00216             /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext",
00217                "type mediumtext", "value text", */
00218 
00219             qb->sql_str =
00220                 g_strdup_printf (" kvp key=%s val=%s type=%s", key,
00221                 kvp_value_to_bare_string (val),
00222                 kvp_value_type_to_qof_id (n));
00223             DEBUG (" %s", qb->sql_str);
00224             qb->has_slots = TRUE;
00225             break;
00226         }
00227     case KVP_TYPE_FRAME:
00228         {
00229             kvp_frame_for_each_slot (kvp_value_get_frame (val),
00230                 kvpvalue_to_sql, qb);
00231             break;
00232         }
00233     default:
00234         {
00235             PERR (" unsupported value = %d", kvp_value_get_type (val));
00236             break;
00237         }
00238     }
00239     LEAVE (" %s", qb->sql_str);
00240 }
00241 
00246 static void
00247 delete_event (QofEntity * ent, QofEventId event_type,
00248     gpointer handler_data, gpointer event_data)
00249 {
00250     QofBackend *be;
00251     QSQLiteBackend *qsql_be;
00252     gchar *gstr, *sql_str;
00253 
00254     qsql_be = (QSQLiteBackend *) handler_data;
00255     be = (QofBackend *) qsql_be;
00256     if (!ent)
00257         return;
00258     if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
00259         return;
00260     /* do not try to delete if only a QofObject has been loaded. */
00261     if (!qof_class_is_registered (ent->e_type))
00262         return;
00263     switch (event_type)
00264     {
00265     case QOF_EVENT_DESTROY:
00266         {
00267             ENTER (" %s do_free=%d", ent->e_type,
00268                 ((QofInstance *) ent)->do_free);
00269             gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00270             guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00271             sql_str = qof_sql_entity_delete (ent);
00272             DEBUG (" sql_str=%s", sql_str);
00273             if (sqlite_exec (qsql_be->sqliteh, sql_str,
00274                     NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00275             {
00276                 qof_error_set_be (be, qsql_be->err_delete);
00277                 qsql_be->error = TRUE;
00278                 LEAVE (" error on delete:%s", qsql_be->err);
00279                 break;
00280             }
00281             LEAVE (" %d", event_type);
00282             qsql_be->error = FALSE;
00283             g_free (gstr);
00284             break;
00285         }
00286     default:
00287         break;
00288     }
00289 }
00290 
00292 static void
00293 create_event (QofEntity * ent, QofEventId event_type,
00294     gpointer handler_data, gpointer event_data)
00295 {
00296     QofBackend *be;
00297     struct QsqlBuilder qb;
00298     QSQLiteBackend *qsql_be;
00299     gchar *gstr;
00300     KvpFrame *slots;
00301 
00302     qsql_be = (QSQLiteBackend *) handler_data;
00303     be = (QofBackend *) qsql_be;
00304     if (!ent)
00305         return;
00306     if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
00307         return;
00308     if (!qof_class_is_registered (ent->e_type))
00309         return;
00310     switch (event_type)
00311     {
00312     case QOF_EVENT_CREATE:
00313         {
00314             ENTER (" insert:%s", ent->e_type);
00315             gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00316             guid_to_string_buff (qof_instance_get_guid ((QofInstance *)
00317                     ent), gstr);
00318             DEBUG (" guid=%s", gstr);
00319             qb.ent = ent;
00320             qb.sql_str = qof_sql_entity_insert (ent);
00322             DEBUG (" sql_str=%s", qb.sql_str);
00323             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00324                 NULL, &qb, &qsql_be->err) != SQLITE_OK)
00325             {
00326                 qof_error_set_be (be, qsql_be->err_insert);
00327                 qsql_be->error = TRUE;
00328                 PERR (" error on create_event:%s", qsql_be->err);
00329             }
00330             else
00331             {
00332                 ((QofInstance *) ent)->dirty = FALSE;
00333                 qsql_be->error = FALSE;
00334                 g_free (qb.sql_str);
00335                 g_free (gstr);
00336                 LEAVE (" ");
00337                 break;
00338             }
00339             /* insert sqlite_kvp data */
00340             slots = qof_instance_get_slots ((QofInstance *) ent);
00341             if (slots)
00342             {
00343                 /* id, guid, path, type, value */
00344                 qb.sql_str = qof_sql_entity_insert (ent);
00345                 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00346                     NULL, &qb, &qsql_be->err) != SQLITE_OK)
00347                 {
00348                     qof_error_set_be (be, qsql_be->err_insert);
00349                     qsql_be->error = TRUE;
00350                     PERR (" error on KVP create_event:%s", qsql_be->err);
00351                 }
00352                 else
00353                 {
00354                     ((QofInstance *) ent)->dirty = FALSE;
00355                     qsql_be->error = FALSE;
00356                     g_free (qb.sql_str);
00357                     g_free (gstr);
00358                     LEAVE (" ");
00359                     break;
00360                 }
00361             }
00362             g_free (qb.sql_str);
00363             g_free (gstr);
00364             LEAVE (" ");
00365             break;
00366         }
00367     default:
00368         break;
00369     }
00370 }
00371 
00372 static void
00373 qsql_modify (QofBackend * be, QofInstance * inst)
00374 {
00375     struct QsqlBuilder qb;
00376     QSQLiteBackend *qsql_be;
00377 
00378     qsql_be = (QSQLiteBackend *) be;
00379     qb.qsql_be = qsql_be;
00380     if (!inst)
00381         return;
00382     if (!inst->param)
00383         return;
00384     if (loading)
00385         return;
00386     if (!inst->param->param_setfcn)
00387         return;
00388     ENTER (" modified %s param:%s", ((QofEntity *) inst)->e_type,
00389         inst->param->param_name);
00390     qb.sql_str = qof_sql_entity_update ((QofEntity*)inst);
00391     if (!qb.sql_str)
00392     {
00393         LEAVE (" null string");
00394         return;
00395     }
00396     DEBUG (" sql_str=%s", qb.sql_str);
00397     if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00398             NULL, &qb, &qsql_be->err) != SQLITE_OK)
00399     {
00400         qof_error_set_be (be, qsql_be->err_update);
00401         qsql_be->error = TRUE;
00402         PERR (" error on modify:%s", qsql_be->err);
00403     }
00404     else
00405     {
00406         inst->dirty = FALSE;
00407         g_free (qb.sql_str);
00408         qsql_be->error = FALSE;
00409         LEAVE (" ");
00410         return;
00411     }
00412     LEAVE (" ");
00413 }
00414 
00416 static gint
00417 record_foreach (gpointer builder, gint col_num, gchar ** strings,
00418     gchar ** columnNames)
00419 {
00420     QSQLiteBackend *qsql_be;
00421     struct QsqlBuilder *qb;
00422     const QofParam *param;
00423     QofInstance *inst;
00424     QofEntity *ent;
00425     gint i;
00426 
00427     g_return_val_if_fail (builder, QSQL_ERROR);
00428     qb = (struct QsqlBuilder *) builder;
00429     qsql_be = qb->qsql_be;
00430     qof_event_suspend ();
00431     inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book);
00432     ent = &inst->entity;
00433     for (i = 0; i < col_num; i++)
00434     {
00435         /* get param and set as string */
00436         param = qof_class_get_parameter (qb->e_type, columnNames[i]);
00437         if (!param)
00438             continue;
00439         /* set the inst->param entry */
00440         inst->param = param;
00441         if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID))
00442         {
00443             GUID *guid;
00444             guid = guid_malloc ();
00445             if (!string_to_guid (strings[i], guid))
00446             {
00447                 DEBUG (" set guid failed:%s", strings[i]);
00448                 return QSQL_ERROR;
00449             }
00450             qof_entity_set_guid (ent, guid);
00451         }
00452         if (strings[i])
00453             qof_util_param_set_string (ent, param, strings[i]);
00454     }
00455     qof_event_resume ();
00456     return SQLITE_OK;
00457 }
00458 
00459 static void
00460 update_dirty (gpointer value, gpointer builder)
00461 {
00462     QofInstance *inst;
00463     QofEntity *ent;
00464     struct QsqlBuilder *qb;
00465     QSQLiteBackend *qsql_be;
00466     QofBackend *be;
00467     gchar *gstr;
00468 
00469     qb = (struct QsqlBuilder *) builder;
00470     qsql_be = qb->qsql_be;
00471     be = (QofBackend *) qsql_be;
00472     ent = (QofEntity *) value;
00473     inst = (QofInstance *) ent;
00474     if (!inst->dirty)
00475         return;
00476     ENTER (" ");
00477     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00478     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00479     /* qof_class_param_foreach  */
00480     qb->sql_str = qof_sql_entity_update (ent);
00481     if (!qb->sql_str)
00482     {
00483         LEAVE (" null string");
00484         return;
00485     }
00486     DEBUG (" update=%s", qb->sql_str);
00487     if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
00488             NULL, qb, &qsql_be->err) != SQLITE_OK)
00489     {
00490         qof_error_set_be (be, qsql_be->err_update);
00491         qsql_be->error = TRUE;
00492         PERR (" error on update_dirty:%s", qsql_be->err);
00493     }
00494     else
00495     {
00496         qof_error_get_message_be (be);
00497         qsql_be->error = FALSE;
00498         inst->dirty = FALSE;
00499     }
00500     LEAVE (" ");
00501     g_free (gstr);
00502     return;
00503 }
00504 
00505 static gint
00506 create_dirty_list (gpointer builder, gint col_num, gchar ** strings,
00507     gchar ** columnNames)
00508 {
00509     struct QsqlBuilder *qb;
00510     QofInstance *inst;
00511     const QofParam *param;
00512     gchar * G_GNUC_UNUSED value, *columnName, * G_GNUC_UNUSED tmp;
00513 
00514     param = NULL;
00515     qb = (struct QsqlBuilder *) builder;
00516     /* qb->ent is the live data, strings is the sqlite data */
00517     inst = (QofInstance *) qb->ent;
00518     qb->exists = TRUE;
00519     if (!inst->dirty)
00520         return SQLITE_OK;
00521     columnName = columnNames[col_num];
00522     tmp = strings[col_num];
00523     param = qof_class_get_parameter (qb->ent->e_type, columnName);
00524     if (!param)
00525         return SQLITE_OK;
00526     value = qof_util_param_to_string (qb->ent, param);
00527     qb->dirty = param;
00528     qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent);
00529     DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list));
00530     return SQLITE_OK;
00531 }
00532 
00533 static gint
00534 mark_entity (gpointer builder, gint col_num, gchar ** strings,
00535     gchar ** columnNames)
00536 {
00537     struct QsqlBuilder *qb;
00538 
00539     qb = (struct QsqlBuilder *) builder;
00540     qb->exists = TRUE;
00541     return SQLITE_OK;
00542 }
00543 
00544 static void
00545 qsql_create (QofBackend * be, QofInstance * inst)
00546 {
00547     gchar *gstr;
00548     QSQLiteBackend *qsql_be;
00549     struct QsqlBuilder qb;
00550     QofEntity *ent;
00551 
00552     qsql_be = (QSQLiteBackend *) be;
00553     if (!inst)
00554         return;
00555     if (loading)
00556         return;
00557     ent = (QofEntity *) inst;
00558     qof_event_suspend ();
00559     qb.has_slots = FALSE;
00560     ENTER (" %s", ent->e_type);
00561     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00562     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00563     qb.sql_str =
00564         g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";",
00565         ent->e_type, gstr);
00566     PINFO (" check exists: %s", qb.sql_str);
00567     qb.ent = ent;
00568     qb.dirty_list = NULL;
00569     qb.exists = FALSE;
00570     if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00571             mark_entity, &qb, &qsql_be->err) != SQLITE_OK)
00572     {
00573         qof_error_set_be (be, qsql_be->err_update);
00574         qsql_be->error = TRUE;
00575         PERR (" error on select :%s", qsql_be->err);
00576     }
00577     if (!qb.exists)
00578     {
00579         /* create new entity */
00580         qb.sql_str = qof_sql_entity_insert (ent);
00581         DEBUG (" sql_str= %s", qb.sql_str);
00582         if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00583                 NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00584         {
00585             qof_error_set_be (be, qsql_be->err_insert);
00586             qsql_be->error = TRUE;
00587             PERR (" error creating new entity:%s", qsql_be->err);
00588         }
00589     }
00590     g_free (qb.sql_str);
00591     g_free (gstr);
00592     qof_event_resume ();
00593     LEAVE (" ");
00594 }
00595 
00596 static void
00597 check_state (QofEntity * ent, gpointer builder)
00598 {
00599     gchar *gstr;
00600     QSQLiteBackend *qsql_be;
00601     struct QsqlBuilder *qb;
00602     QofBackend *be;
00603     QofInstance *inst;
00604 
00605     qb = (struct QsqlBuilder *) builder;
00606     qsql_be = qb->qsql_be;
00607     be = (QofBackend *) qsql_be;
00608     inst = (QofInstance *) ent;
00609     if (!inst->dirty)
00610         return;
00611     /* check if this entity already exists */
00612     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00613     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00614     qb->sql_str =
00615         g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";",
00616         ent->e_type, gstr);
00617     qb->ent = ent;
00618     qb->dirty_list = NULL;
00619     /* assume entity does not yet exist in backend,
00620        e.g. being copied from another session. */
00621     qb->exists = FALSE;
00622     qb->qsql_be = qsql_be;
00623     /* update each dirty instance */
00624     /* Make a GList of dirty instances
00625        Don't update during a SELECT,
00626        UPDATE will fail with DB_LOCKED */
00627     if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
00628             create_dirty_list, qb, &qsql_be->err) != SQLITE_OK)
00629     {
00630         qof_error_set_be (be, qsql_be->err_update);
00631         qsql_be->error = TRUE;
00632         PERR (" error on check_state:%s", qsql_be->err);
00633     }
00634     if (!qb->exists)
00635     {
00636         /* create new entity */
00637         qb->sql_str = qof_sql_entity_insert (ent);
00638         DEBUG (" sql_str= %s", qb->sql_str);
00639         if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
00640                 NULL, qb, &qsql_be->err) != SQLITE_OK)
00641         {
00642             qof_error_set_be (be, qsql_be->err_insert);
00643             qsql_be->error = TRUE;
00644             PERR (" error on check_state create_new:%s", qsql_be->err);
00645         }
00646         g_free (qb->sql_str);
00647     }
00648     /* update instead */
00649     g_list_foreach (qb->dirty_list, update_dirty, &qb);
00650     g_free (qb->sql_str);
00651     g_free (gstr);
00652 }
00653 
00662 static gint
00663 build_kvp_table (gpointer builder, gint col_num, gchar ** strings,
00664     gchar ** columnNames)
00665 {
00666     QSQLiteBackend *qsql_be;
00667     struct QsqlBuilder *qb;
00668     KvpFrame *frame;
00669     KvpValueType type;
00670     KvpValue *value;
00671     gulong max;
00672     gchar *tail;
00673 
00674     g_return_val_if_fail (builder, QSQL_ERROR);
00675     qb = (struct QsqlBuilder *) builder;
00676     max = 0;
00677     qsql_be = qb->qsql_be;
00678     g_return_val_if_fail ((col_num < 4), QSQL_ERROR);
00679     g_return_val_if_fail (strings[2], QSQL_ERROR);
00680     frame = kvp_frame_new ();
00681     /* columnNames = fields strings = values
00682        [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value
00683        get type from type_string */
00684     type = qof_id_to_kvp_value_type (strings[3]);
00685     if (type == 0)
00686     {
00687         PERR (" invalid type returned from kvp table");
00688         return QSQL_ERROR;
00689     }
00690     /* use the type to make a KvpValue from value */
00691     value = string_to_kvp_value (strings[4], type);
00692     if (!value)
00693     {
00694         PERR (" invalid KvpValue for type: %d", type);
00695         return QSQL_ERROR;
00696     }
00697     /* add the KvpValue to the frame at path */
00698     kvp_frame_set_value (frame, strings[2], value);
00699     /* index the frame under the entity GUID */
00700     g_hash_table_insert (qsql_be->kvp_table, strings[1], frame);
00701     /* index the guid under the kvp_id */
00702     g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]);
00703     errno = 0;
00704     max = strtol (strings[0], &tail, 0);
00705     if (errno == 0)
00706     {
00707         qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index;
00708     }
00709     return SQLITE_OK;
00710 }
00711 
00713 static void
00714 qsql_load_kvp (QSQLiteBackend * qsql_be)
00715 {
00716     struct QsqlBuilder qb;
00717     QofBackend *be;
00718     gint sq_code;
00719 
00720     g_return_if_fail (qsql_be);
00721     sq_code = SQLITE_OK;
00722     be = (QofBackend *) qsql_be;
00723     qb.sql_str =
00724         g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE);
00725     sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table,
00726             &qb, &qsql_be->err);
00727     /* catch older files without a sqlite_kvp table */
00728     if (sq_code == SQLITE_ERROR)
00729     {
00730         g_free (qb.sql_str);
00731         qb.sql_str =
00732             g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
00733             QSQL_KVP_TABLE, "kvp_id int primary key not null",
00734             "guid char(32)", "path mediumtext", "type mediumtext",
00735             "value text", END_DB_VERSION);
00736         PINFO (" creating kvp table. sql=%s", qb.sql_str);
00737         if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00738             record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
00739         {
00740             qsql_be->error = TRUE;
00741             PERR (" unable to create kvp table:%s", qsql_be->err);
00742         }
00743     }
00744     else if (sq_code != SQLITE_OK)
00745     {
00746         qof_error_set_be (be, qsql_be->err_create);
00747         qsql_be->error = TRUE;
00748         PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code);
00749     }
00750     g_free (qb.sql_str);
00751 }
00752 
00754 static void
00755 qsql_class_foreach (QofObject * obj, gpointer data)
00756 {
00757     struct QsqlBuilder qb;
00758     QSQLiteBackend *qsql_be;
00759     QofBackend *be;
00760 
00761     qsql_be = (QSQLiteBackend *) data;
00762     be = (QofBackend *) qsql_be;
00763     qb.qsql_be = qsql_be;
00764     qb.e_type = obj->e_type;
00765     ENTER (" obj_type=%s", qb.e_type);
00766     switch (qsql_be->stm_type)
00767     {
00768     case SQL_NONE:
00769     case SQL_INSERT:
00770     case SQL_DELETE:
00771     case SQL_UPDATE:
00772         {
00773             break;
00774         }
00775     case SQL_CREATE:
00776         {
00777             /* KVP is handled separately */
00778             qb.sql_str = qof_sql_object_create_table (obj);
00779             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00780                     NULL, NULL, &qsql_be->err) != SQLITE_OK)
00781             {
00782                 qof_error_set_be (be, qsql_be->err_create);
00783                 qsql_be->error = TRUE;
00784                 PERR (" error on SQL_CREATE:%s", qsql_be->err);
00785             }
00786             g_free (qb.sql_str);
00787             break;
00788         }
00789     case SQL_LOAD:
00790         {
00791             qb.sql_str =
00792                 g_strdup_printf ("SELECT * FROM %s;", obj->e_type);
00793             PINFO (" sql=%s", qb.sql_str);
00794             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00795                     record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
00796             {
00797                 qsql_be->error = TRUE;
00798                 PERR (" error on SQL_LOAD:%s", qsql_be->err);
00799             }
00800             break;
00801         }
00802     case SQL_WRITE:
00803         {
00804             if (!qof_book_not_saved (qsql_be->book))
00805                 break;
00806             qof_object_foreach (obj->e_type, qsql_be->book, check_state,
00807                 &qb);
00808             break;
00809         }
00810     }
00811     LEAVE (" ");
00812 }
00813 
00814 static void
00815 qsql_backend_createdb (QofBackend * be, QofSession * session)
00816 {
00817     FILE *f;
00818     QSQLiteBackend *qsql_be;
00819     struct QsqlBuilder G_GNUC_UNUSED qb;
00820 
00821     g_return_if_fail (be || session);
00822     ENTER (" ");
00823     qsql_be = (QSQLiteBackend *) be;
00824     qsql_be->stm_type = SQL_CREATE;
00825     qb.qsql_be = qsql_be;
00826     qsql_be->book = qof_session_get_book (session);
00827     DEBUG (" create_file %s", qsql_be->fullpath);
00828     f = fopen (qsql_be->fullpath, "a+");
00829     if (f)
00830         fclose (f);
00831     else
00832     {
00833         qof_error_set (session, qof_error_register
00834             (_("Unable to open the output file '%s' - do you have "
00835                     "permission to create this file?"), TRUE));
00836         qsql_be->error = TRUE;
00837         LEAVE (" unable to create new file '%s'", qsql_be->fullpath);
00838         return;
00839     }
00840     qsql_be->sqliteh =
00841         sqlite_open (qsql_be->fullpath, 0644, &qsql_be->err);
00842     if (!qsql_be->sqliteh)
00843     {
00844         qof_error_set_be (be, qsql_be->err_create);
00845         qsql_be->error = TRUE;
00846         LEAVE (" unable to open sqlite:%s", qsql_be->err);
00847         return;
00848     }
00849     qof_object_foreach_type (qsql_class_foreach, qsql_be);
00850     LEAVE (" ");
00851 }
00852 
00853 static void
00854 qsql_backend_opendb (QofBackend * be, QofSession * session)
00855 {
00856     QSQLiteBackend *qsql_be;
00857 
00858     g_return_if_fail (be || session);
00859     ENTER (" ");
00860     qsql_be = (QSQLiteBackend *) be;
00861     qsql_be->sqliteh =
00862         sqlite_open (qsql_be->fullpath, 0666, &qsql_be->err);
00863     if (!qsql_be->sqliteh)
00864     {
00865         qof_error_set_be (be, qof_error_register
00866             (_("Unable to open the sqlite database '%s'."), TRUE));
00867         qsql_be->error = TRUE;
00868         PERR (" %s", qsql_be->err);
00869     }
00870     LEAVE (" %s", qsql_be->fullpath);
00871 }
00872 
00873 static void
00874 qsqlite_session_begin (QofBackend * be, QofSession * session,
00875     const gchar * book_path, gboolean ignore_lock,
00876     gboolean create_if_nonexistent)
00877 {
00878     QSQLiteBackend *qsql_be;
00879     gchar **pp;
00880     struct stat statinfo;
00881     gint G_GNUC_UNUSED stat_val;
00882 
00883     g_return_if_fail (be);
00884     ENTER (" book_path=%s", book_path);
00885     qsql_be = (QSQLiteBackend *) be;
00886     qsql_be->fullpath = NULL;
00887     if (book_path == NULL)
00888     {
00889         qof_error_set_be (be, qof_error_register
00890             (_("Please provide a filename for sqlite."), FALSE));
00891         qsql_be->error = TRUE;
00892         LEAVE (" bad URL");
00893         return;
00894     }
00895     /* book_path => sqlite_file_name */
00896     pp = g_strsplit (book_path, ":", 2);
00897     if (0 == safe_strcmp (pp[0], ACCESS_METHOD))
00898     {
00899         qsql_be->fullpath = g_strdup (pp[1]);
00900         g_strfreev (pp);
00901     }
00902     else
00903         qsql_be->fullpath = g_strdup (book_path);
00904     be->fullpath = g_strdup (qsql_be->fullpath);
00905     PINFO (" final path = %s", qsql_be->fullpath);
00906     stat_val = g_stat (qsql_be->fullpath, &statinfo);
00907     if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0)
00908         qsql_backend_createdb (be, session);
00909     if (!qsql_be->error)
00910         qsql_backend_opendb (be, session);
00911     if (qof_error_check_be (be) || qsql_be->error)
00912     {
00913         LEAVE (" open failed");
00914         return;
00915     }
00916     qsql_be->create_handler =
00917         qof_event_register_handler (create_event, qsql_be);
00918     qsql_be->delete_handler =
00919         qof_event_register_handler (delete_event, qsql_be);
00920     LEAVE (" db=%s", qsql_be->fullpath);
00921 }
00922 
00923 static void
00924 qsqlite_db_load (QofBackend * be, QofBook * book)
00925 {
00926     QSQLiteBackend *qsql_be;
00927 
00928     g_return_if_fail (be);
00929     ENTER (" ");
00930     loading = TRUE;
00931     qsql_be = (QSQLiteBackend *) be;
00932     qsql_be->stm_type = SQL_LOAD;
00933     qsql_be->book = book;
00934     /* iterate over registered objects */
00935     qof_object_foreach_type (qsql_class_foreach, qsql_be);
00936     qsql_load_kvp (qsql_be);
00937     loading = FALSE;
00938     LEAVE (" ");
00939 }
00940 
00941 static void
00942 qsqlite_write_db (QofBackend * be, QofBook * book)
00943 {
00944     QSQLiteBackend *qsql_be;
00945 
00946     g_return_if_fail (be);
00947     qsql_be = (QSQLiteBackend *) be;
00948     qsql_be->stm_type = SQL_WRITE;
00949     qsql_be->book = book;
00950     /* update each record with current state */
00951     qof_object_foreach_type (qsql_class_foreach, qsql_be);
00952 }
00953 
00954 static gboolean
00955 qsql_determine_file_type (const gchar * path)
00956 {
00957     if (!path)
00958         return FALSE;
00959     return TRUE;
00960 }
00961 
00962 static void
00963 qsqlite_session_end (QofBackend * be)
00964 {
00965     QSQLiteBackend *qsql_be;
00966 
00967     g_return_if_fail (be);
00968     qsql_be = (QSQLiteBackend *) be;
00969     if (qsql_be->sqliteh)
00970         sqlite_close (qsql_be->sqliteh);
00971 }
00972 
00973 static void
00974 qsqlite_destroy_backend (QofBackend * be)
00975 {
00976     QSQLiteBackend *qsql_be;
00977 
00978     g_return_if_fail (be);
00979     qsql_be = (QSQLiteBackend *) be;
00980     g_hash_table_destroy (qsql_be->kvp_table);
00981     g_hash_table_destroy (qsql_be->kvp_id);
00982     qof_event_unregister_handler (qsql_be->create_handler);
00983     qof_event_unregister_handler (qsql_be->delete_handler);
00984     g_free (be);
00985     g_free (qsql_be);
00986 }
00987 
00988 static void
00989 qsql_provider_free (QofBackendProvider * prov)
00990 {
00991     prov->provider_name = NULL;
00992     prov->access_method = NULL;
00993     g_free (prov);
00994 }
00995 
01011 static QofBackend *
01012 qsql_backend_new (void)
01013 {
01014     QSQLiteBackend *qsql_be;
01015     QofBackend *be;
01016 
01017     ENTER (" ");
01018     qsql_be = g_new0 (QSQLiteBackend, 1);
01019     be = (QofBackend *) qsql_be;
01020     qof_backend_init (be);
01021     qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal);
01022     qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal);
01023     qsql_be->dbversion = QOF_OBJECT_VERSION;
01024     qsql_be->stm_type = SQL_NONE;
01025     qsql_be->err_delete =
01026         qof_error_register (_("Unable to delete record."), FALSE);
01027     qsql_be->err_create =
01028         qof_error_register (_("Unable to create record."), FALSE);
01029     qsql_be->err_insert =
01030         qof_error_register (_("Unable to insert a new record."), FALSE);
01031     qsql_be->err_update =
01032         qof_error_register (_("Unable to update existing record."), FALSE);
01033     be->session_begin = qsqlite_session_begin;
01034 
01035     be->session_end = qsqlite_session_end;
01036     be->destroy_backend = qsqlite_destroy_backend;
01037     be->load = qsqlite_db_load;
01038     be->save_may_clobber_data = NULL;
01039     /* begin: create an empty entity if none exists,
01040        even if events are suspended. */
01041     be->begin = qsql_create;
01042     /* commit: write to sqlite, commit undo record. */
01043     be->commit = qsql_modify;
01044     be->rollback = NULL;
01045     /* would need a QofQuery back to QofSqlQuery conversion. */
01046     be->compile_query = NULL;
01047     /* unused */
01048     be->free_query = NULL;
01049     be->run_query = NULL;
01050     be->counter = NULL;
01051     /* The QOF SQLite backend is not multi-user - all QOF users are the same. */
01052     be->events_pending = NULL;
01053     be->process_events = NULL;
01054 
01055     be->sync = qsqlite_write_db;
01056     be->load_config = NULL;
01057     be->get_config = NULL;
01058     LEAVE (" ");
01059     return be;
01060 }
01061 
01062 void
01063 qof_sqlite_provider_init (void)
01064 {
01065     QofBackendProvider *prov;
01066 
01067     ENTER (" ");
01068     bindtextdomain (PACKAGE, LOCALE_DIR);
01069     qof_sql_entity_set_kvp_tablename (QSQL_KVP_TABLE);
01070     prov = g_new0 (QofBackendProvider, 1);
01071     prov->provider_name = "QOF SQLite Backend Version 0.4";
01072     prov->access_method = ACCESS_METHOD;
01073     prov->partial_book_supported = TRUE;
01074     prov->backend_new = qsql_backend_new;
01075     prov->check_data_type = qsql_determine_file_type;
01076     prov->provider_free = qsql_provider_free;
01077     qof_backend_register_provider (prov);
01078     LEAVE (" ");
01079 }
01080 
01081 /* ================= END OF FILE =================== */