QOF 0.8.2

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