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-2007  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 
00034 #define _(String) dgettext (GETTEXT_PACKAGE, String)
00035 #define ACCESS_METHOD "sqlite"
00036 
00043 #define PRIORITY_HIGH       9
00044 
00045 #define PRIORITY_STANDARD   5
00046 
00047 #define PRIORITY_LOW        0
00048 
00049 #define QSQL_ERROR          -1
00050 
00051 #define QSQL_KVP_TABLE "sqlite_kvp"
00052 
00053 #define END_DB_VERSION " dbversion int );"
00054 
00055 static QofLogModule log_module = QOF_MOD_SQLITE;
00056 static gboolean loading = FALSE;
00057 
00058 typedef enum
00059 {
00061     SQL_NONE = 0,
00063     SQL_CREATE,
00065     SQL_LOAD,
00067     SQL_WRITE,
00069     SQL_INSERT,
00071     SQL_DELETE,
00073     SQL_UPDATE
00074 } QsqlStatementType;
00075 
00082 typedef struct
00083 {
00084     QofBackend be;
00085     sqlite *sqliteh;
00086     QsqlStatementType stm_type;
00087     gint dbversion;
00088     gint create_handler;
00089     gint delete_handler;
00090     const gchar *fullpath;
00091     gchar *err;
00092     gboolean error;
00093     /* full hashtable of kvp records */
00094     GHashTable *kvp_table;
00095     /* hashtable relating the GUID to the kvp_id */
00096     GHashTable *kvp_id;
00097     /* highest kvp_id in the table */
00098     glong index;
00099     QofBook *book;
00100     QofErrorId err_delete, err_insert, err_update, err_create;
00101 } QSQLiteBackend;
00102 
00109 struct QsqlBuilder
00110 {
00112     QSQLiteBackend *qsql_be;
00114     QofEntity *ent;
00116     QofIdType e_type;
00118     gchar *sql_str;
00120     GList *dirty_list;
00122     gboolean exists;
00124     gboolean has_slots;
00126     const QofParam *dirty;
00127 };
00128 
00129 static inline gchar *
00130 add_to_sql (gchar * sql_str, const gchar * add)
00131 {
00132     gchar *old;
00133     old = g_strdup (sql_str);
00134     g_free (sql_str);
00135     sql_str = g_strconcat (old, add, NULL);
00136     g_free (old);
00137     return sql_str;
00138 }
00139 
00144 static QofIdTypeConst
00145 kvp_value_to_qof_type_helper (KvpValueType n)
00146 {
00147     switch (n)
00148     {
00149     case KVP_TYPE_GINT64:
00150         {
00151             return QOF_TYPE_INT64;
00152             break;
00153         }
00154     case KVP_TYPE_DOUBLE:
00155         {
00156             return QOF_TYPE_DOUBLE;
00157             break;
00158         }
00159     case KVP_TYPE_NUMERIC:
00160         {
00161             return QOF_TYPE_NUMERIC;
00162             break;
00163         }
00164     case KVP_TYPE_STRING:
00165         {
00166             return QOF_TYPE_STRING;
00167             break;
00168         }
00169     case KVP_TYPE_GUID:
00170         {
00171             return QOF_TYPE_GUID;
00172             break;
00173         }
00174 #ifndef QOF_DISABLE_DEPRECATED
00175     case KVP_TYPE_TIMESPEC:
00176         {
00177             return QOF_TYPE_DATE;
00178             break;
00179         }
00180 #endif
00181     case KVP_TYPE_BOOLEAN:
00182         {
00183             return QOF_TYPE_BOOLEAN;
00184             break;
00185         }
00186     case KVP_TYPE_TIME:
00187         {
00188             return QOF_TYPE_TIME;
00189             break;
00190         }
00191     default:
00192         {
00193             return NULL;
00194         }
00195     }
00196 }
00197 
00199 static KvpValueType
00200 sql_to_kvp_helper (const gchar * type_string)
00201 {
00202     if (0 == safe_strcmp (QOF_TYPE_INT64, type_string))
00203         return KVP_TYPE_GINT64;
00204     if (0 == safe_strcmp (QOF_TYPE_DOUBLE, type_string))
00205         return KVP_TYPE_DOUBLE;
00206     if (0 == safe_strcmp (QOF_TYPE_NUMERIC, type_string))
00207         return KVP_TYPE_NUMERIC;
00208     if (0 == safe_strcmp (QOF_TYPE_STRING, type_string))
00209         return KVP_TYPE_STRING;
00210     if (0 == safe_strcmp (QOF_TYPE_GUID, type_string))
00211         return KVP_TYPE_GUID;
00212 #ifndef QOF_DISABLE_DEPRECATED
00213     if (0 == safe_strcmp (QOF_TYPE_DATE, type_string))
00214         return KVP_TYPE_TIMESPEC;
00215 #endif
00216     if (0 == safe_strcmp (QOF_TYPE_TIME, type_string))
00217         return KVP_TYPE_TIME;
00218     return 0;
00219 }
00220 
00222 KvpValue *
00223 string_to_kvp_value (const gchar * content, KvpValueType type)
00224 {
00225     gchar *tail;
00226     gint64 cm_i64;
00227     gdouble cm_double;
00228     QofNumeric cm_numeric;
00229     GUID *cm_guid;
00230 #ifndef QOF_DISABLE_DEPRECATED
00231     struct tm kvp_time;
00232     time_t kvp_time_t;
00233     Timespec cm_date;
00234 #endif
00235 
00236     switch (type)
00237     {
00238     case KVP_TYPE_GINT64:
00239         {
00240             errno = 0;
00241             cm_i64 = strtoll (content, &tail, 0);
00242             if (errno == 0)
00243             {
00244                 return kvp_value_new_gint64 (cm_i64);
00245             }
00246             break;
00247         }
00248     case KVP_TYPE_DOUBLE:
00249         {
00250             errno = 0;
00251             cm_double = strtod (content, &tail);
00252             if (errno == 0)
00253                 return kvp_value_new_double (cm_double);
00254             break;
00255         }
00256     case KVP_TYPE_NUMERIC:
00257         {
00258             qof_numeric_from_string (content, &cm_numeric);
00259             return kvp_value_new_numeric (cm_numeric);
00260             break;
00261         }
00262     case KVP_TYPE_STRING:
00263         {
00264             return kvp_value_new_string (content);
00265             break;
00266         }
00267     case KVP_TYPE_GUID:
00268         {
00269             cm_guid = g_new0 (GUID, 1);
00270             if (TRUE == string_to_guid (content, cm_guid))
00271                 return kvp_value_new_guid (cm_guid);
00272             break;
00273         }
00274     case KVP_TYPE_TIME:
00275         {
00276             QofDate *qd;
00277             QofTime *qt;
00278             KvpValue *retval;
00279 
00280             qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC);
00281             if (qd)
00282             {
00283                 qt = qof_date_to_qtime (qd);
00284                 retval = kvp_value_new_time (qt);
00285                 qof_date_free (qd);
00286                 qof_time_free (qt);
00287                 return retval;
00288             }
00289             else
00290                 PERR (" failed to parse date");
00291         }
00292 #ifndef QOF_DISABLE_DEPRECATED
00293     case KVP_TYPE_TIMESPEC:
00294         {
00295             strptime (content, QOF_UTC_DATE_FORMAT, &kvp_time);
00296             kvp_time_t = mktime (&kvp_time);
00297             timespecFromTime_t (&cm_date, kvp_time_t);
00298             return kvp_value_new_timespec (cm_date);
00299             break;
00300         }
00301 #endif
00302     case KVP_TYPE_BOOLEAN:
00303         {
00304             gboolean val;
00305             val = qof_util_bool_to_int (content);
00306             return kvp_value_new_boolean (val);
00307         }
00308     default:
00309         break;
00310     }
00311     return NULL;
00312 }
00313 
00315 static void
00316 kvpvalue_to_sql (const gchar * key, KvpValue * val, gpointer builder)
00317 {
00318     QSQLiteBackend *qsql_be;
00319     struct QsqlBuilder *qb;
00320     KvpValueType n;
00321     gchar *full_path;
00322 
00323     full_path = NULL;
00324     ENTER (" ");
00325     qb = (struct QsqlBuilder *) builder;
00326     qsql_be = qb->qsql_be;
00327     g_return_if_fail (key && val && qsql_be);
00328     n = kvp_value_get_type (val);
00329     switch (n)
00330     {
00331     case KVP_TYPE_GINT64:
00332     case KVP_TYPE_DOUBLE:
00333     case KVP_TYPE_NUMERIC:
00334     case KVP_TYPE_STRING:
00335     case KVP_TYPE_GUID:
00336     case KVP_TYPE_TIME:
00337     case KVP_TYPE_BOOLEAN:
00338 #ifndef QOF_DISABLE_DEPRECATED
00339     case KVP_TYPE_TIMESPEC:
00340 #endif
00341         {
00342             /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext",
00343                "type mediumtext", "value text", */
00344 
00345             qb->sql_str =
00346                 g_strdup_printf (" kvp key=%s val=%s type=%s", key,
00347                 kvp_value_to_bare_string (val),
00348                 kvp_value_to_qof_type_helper (n));
00349             DEBUG (" %s", qb->sql_str);
00350             qb->has_slots = TRUE;
00351             break;
00352         }
00353     case KVP_TYPE_FRAME:
00354         {
00355             kvp_frame_for_each_slot (kvp_value_get_frame (val),
00356                 kvpvalue_to_sql, qb);
00357             break;
00358         }
00359     default:
00360         {
00361             PERR (" unsupported value = %d", kvp_value_get_type (val));
00362             break;
00363         }
00364     }
00365     LEAVE (" %s", qb->sql_str);
00366 }
00367 
00368 static gchar *
00369 string_param_to_sql (QofParam * param)
00370 {
00371     /* Handle the entity GUID. Ensure that reference GUIDs
00372        must not also try to be primary keys and can be NULL. */
00373     if ((0 == safe_strcmp (param->param_type, QOF_TYPE_GUID)) &&
00374         (0 == safe_strcmp (param->param_name, QOF_PARAM_GUID)))
00375         return g_strdup_printf (" %s char(32) primary key not null",
00376             param->param_name);
00377     if (0 == safe_strcmp (param->param_type, QOF_TYPE_GUID))
00378         return g_strdup_printf (" %s char(32)", param->param_name);
00379     /* avoid creating database fields for calculated values */
00380     if (!param->param_setfcn)
00381         return NULL;
00382     if (0 == safe_strcmp (param->param_type, QOF_TYPE_STRING))
00383         return g_strdup_printf (" %s mediumtext", param->param_name);
00384     if (0 == safe_strcmp (param->param_type, QOF_TYPE_BOOLEAN))
00385         return g_strdup_printf (" %s int", param->param_name);
00386     if ((0 == safe_strcmp (param->param_type, QOF_TYPE_NUMERIC))
00387         || (0 == safe_strcmp (param->param_type, QOF_TYPE_DOUBLE))
00388         || (0 == safe_strcmp (param->param_type, QOF_TYPE_DEBCRED)))
00389     {
00390         return g_strdup_printf (" %s text", param->param_name);
00391     }
00392     if (0 == safe_strcmp (param->param_type, QOF_TYPE_INT32))
00393         return g_strdup_printf (" %s int", param->param_name);
00394 #ifndef QOF_DISABLE_DEPRECATED
00395     if ((0 == safe_strcmp (param->param_type, QOF_TYPE_DATE)) ||
00396         (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME)))
00397 #else
00398     if (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME))
00399 #endif
00400         return g_strdup_printf (" %s datetime", param->param_name);
00401     if (0 == safe_strcmp (param->param_type, QOF_TYPE_CHAR))
00402         return g_strdup_printf (" %s char(1)", param->param_name);
00403     /* kvp data is stored separately - actually this is really
00404        a no-op because entities do not need a param_setfcn for kvp data. */
00405     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
00406         return g_strdup ("");
00407     if (0 == safe_strcmp (param->param_type, QOF_TYPE_COLLECT))
00408         return g_strdup_printf (" %s char(32)", param->param_name);
00409     /* catch references */
00410     return g_strdup_printf (" %s char(32)", param->param_name);
00411 }
00412 
00418 static void
00419 create_param_list (QofParam * param, gpointer builder)
00420 {
00421     struct QsqlBuilder *qb;
00422     qb = (struct QsqlBuilder *) builder;
00423 
00424     /* avoid creating database fields for calculated values */
00425     if (!param->param_setfcn)
00426         return;
00427     /* avoid setting KVP even if a param_setfcn has been set
00428        because a QofSetterFunc for KVP is quite pointless. */
00429     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
00430     {
00431         PINFO (" kvp support tag");
00432         return;
00433     }
00434     if (!g_str_has_suffix (qb->sql_str, "("))
00435     {
00436         gchar *add;
00437         add = g_strconcat (", ", param->param_name, NULL);
00438         qb->sql_str = add_to_sql (qb->sql_str, add);
00439         g_free (add);
00440     }
00441     else
00442         qb->sql_str = add_to_sql (qb->sql_str, param->param_name);
00443 }
00444 
00446 static void
00447 create_each_param (QofParam * param, gpointer builder)
00448 {
00449     gchar *value;
00450     struct QsqlBuilder *qb;
00451     qb = (struct QsqlBuilder *) builder;
00452     GList *references;
00453 
00454     /* avoid creating database fields for calculated values */
00455     if (!param->param_setfcn)
00456         return;
00457     /* avoid setting KVP even if a param_setfcn has been set
00458        because a QofSetterFunc for KVP is quite pointless. */
00459     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
00460         return;
00461     references = qof_class_get_referenceList (qb->ent->e_type);
00462     if (g_list_find (references, param))
00463     {
00466         QofEntity *e;
00467         e = param->param_getfcn (qb->ent, param);
00468         value = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00469         guid_to_string_buff (qof_entity_get_guid (e), value);
00470         PINFO (" ref=%p GUID=%s", e, value);
00471     }
00472     else
00473         value = qof_util_param_to_string (qb->ent, param);
00474     if (value)
00475         g_strescape (value, NULL);
00476     if (!value)
00477         value = g_strdup ("");
00478     if (!g_str_has_suffix (qb->sql_str, "("))
00479     {
00480         gchar *val;
00481         val = g_strconcat (", \"", value, "\"", NULL);
00482         qb->sql_str = add_to_sql (qb->sql_str, val);
00483         g_free (val);
00484     }
00485     else
00486     {
00487         gchar *val;
00488         val = g_strconcat ("\"", value, "\"", NULL);
00489         qb->sql_str = add_to_sql (qb->sql_str, val);
00490         g_free (val);
00491     }
00492 }
00493 
00498 static void
00499 delete_event (QofEntity * ent, QofEventId event_type,
00500     gpointer handler_data, gpointer event_data)
00501 {
00502     QofBackend *be;
00503     QSQLiteBackend *qsql_be;
00504     gchar *gstr, *sql_str;
00505 
00506     qsql_be = (QSQLiteBackend *) handler_data;
00507     be = (QofBackend *) qsql_be;
00508     if (!ent)
00509         return;
00510     if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
00511         return;
00512     /* do not try to delete if only a QofObject has been loaded. */
00513     if (!qof_class_is_registered (ent->e_type))
00514         return;
00515     switch (event_type)
00516     {
00517     case QOF_EVENT_DESTROY:
00518         {
00519             ENTER (" %s do_free=%d", ent->e_type,
00520                 ((QofInstance *) ent)->do_free);
00521             gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00522             guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00523             sql_str = g_strconcat ("DELETE from ", ent->e_type, " WHERE ",
00524                 QOF_TYPE_GUID, "='", gstr, "';", NULL);
00525             DEBUG (" sql_str=%s", sql_str);
00526             if (sqlite_exec (qsql_be->sqliteh, sql_str,
00527                     NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00528             {
00529                 qof_error_set_be (be, qsql_be->err_delete);
00530                 qsql_be->error = TRUE;
00531                 LEAVE (" error on delete:%s", qsql_be->err);
00532                 break;
00533             }
00535             /* SELECT kvp_id from QSQL_KVP_TABLE where guid = gstr */
00536             LEAVE (" %d", event_type);
00537             qsql_be->error = FALSE;
00538             g_free (gstr);
00539             break;
00540         }
00541     default:
00542         break;
00543     }
00544 }
00545 
00547 static void
00548 create_event (QofEntity * ent, QofEventId event_type,
00549     gpointer handler_data, gpointer event_data)
00550 {
00551     QofBackend *be;
00552     struct QsqlBuilder qb;
00553     QSQLiteBackend *qsql_be;
00554     gchar *gstr;
00555     KvpFrame *slots;
00556 
00557     qsql_be = (QSQLiteBackend *) handler_data;
00558     be = (QofBackend *) qsql_be;
00559     if (!ent)
00560         return;
00561     if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
00562         return;
00563     if (!qof_class_is_registered (ent->e_type))
00564         return;
00565     switch (event_type)
00566     {
00567     case QOF_EVENT_CREATE:
00568         {
00569             gchar *tmp;
00570             ENTER (" create:%s", ent->e_type);
00571             gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00572             guid_to_string_buff (qof_instance_get_guid ((QofInstance *)
00573                     ent), gstr);
00574             DEBUG (" guid=%s", gstr);
00575             qb.ent = ent;
00576             qb.sql_str =
00577                 g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
00578             qof_class_param_foreach (ent->e_type, create_param_list, &qb);
00579             tmp = g_strconcat (") VALUES (\"", gstr, "\" ", NULL);
00580             qb.sql_str = add_to_sql (qb.sql_str, tmp);
00581             g_free (tmp);
00582             qof_class_param_foreach (ent->e_type, create_each_param, &qb);
00583             qb.sql_str = add_to_sql (qb.sql_str, ");");
00584             DEBUG (" sql_str=%s", qb.sql_str);
00585             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00586                     NULL, &qb, &qsql_be->err) != SQLITE_OK)
00587             {
00588                 qof_error_set_be (be, qsql_be->err_insert);
00589                 qsql_be->error = TRUE;
00590                 PERR (" error on create_event:%s", qsql_be->err);
00591             }
00592             else
00593             {
00594                 ((QofInstance *) ent)->dirty = FALSE;
00595                 qsql_be->error = FALSE;
00596                 g_free (qb.sql_str);
00597                 g_free (gstr);
00598                 LEAVE (" ");
00599                 break;
00600             }
00601             /* insert sqlite_kvp data */
00602             slots = qof_instance_get_slots ((QofInstance *) ent);
00603             if (slots)
00604             {
00605                 /* id, guid, path, type, value */
00606                 qb.sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE,
00607                     "  (kvp_id \"", gstr, "\", ", NULL);
00608                 kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb);
00609                 qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION);
00610                 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00611                         NULL, &qb, &qsql_be->err) != SQLITE_OK)
00612                 {
00613                     qof_error_set_be (be, qsql_be->err_insert);
00614                     qsql_be->error = TRUE;
00615                     PERR (" error on KVP create_event:%s", qsql_be->err);
00616                 }
00617                 else
00618                 {
00619                     ((QofInstance *) ent)->dirty = FALSE;
00620                     qsql_be->error = FALSE;
00621                     g_free (qb.sql_str);
00622                     g_free (gstr);
00623                     LEAVE (" ");
00624                     break;
00625                 }
00626             }
00627             g_free (qb.sql_str);
00628             g_free (gstr);
00629             LEAVE (" ");
00630             break;
00631         }
00632     default:
00633         break;
00634     }
00635 }
00636 
00637 static void
00638 qsql_modify (QofBackend * be, QofInstance * inst)
00639 {
00640     struct QsqlBuilder qb;
00641     QSQLiteBackend *qsql_be;
00642     gchar *gstr, *param_str;
00643     KvpFrame *slots;
00644 
00645     qsql_be = (QSQLiteBackend *) be;
00646     qb.qsql_be = qsql_be;
00647     if (!inst)
00648         return;
00649     if (!inst->param)
00650         return;
00651     if (loading)
00652         return;
00653     if (!inst->param->param_setfcn)
00654         return;
00655     ENTER (" modified %s param:%s", ((QofEntity *) inst)->e_type,
00656         inst->param->param_name);
00657     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00658     guid_to_string_buff (qof_instance_get_guid (inst), gstr);
00659     qb.ent = (QofEntity *) inst;
00660     param_str = qof_util_param_to_string (qb.ent, inst->param);
00661     if (param_str)
00662         g_strescape (param_str, NULL);
00663     qb.sql_str = g_strconcat ("UPDATE ", qb.ent->e_type, " SET ",
00664         inst->param->param_name, " = \"", param_str,
00665         "\" WHERE ", QOF_TYPE_GUID, "='", gstr, "';", NULL);
00666     DEBUG (" sql_str=%s param_Str=%s", qb.sql_str, param_str);
00667     if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00668             NULL, &qb, &qsql_be->err) != SQLITE_OK)
00669     {
00670         qof_error_set_be (be, qsql_be->err_update);
00671         qsql_be->error = TRUE;
00672         PERR (" error on modify:%s", qsql_be->err);
00673     }
00674     else
00675     {
00676         inst->dirty = FALSE;
00677         g_free (qb.sql_str);
00678         g_free (gstr);
00679         qsql_be->error = FALSE;
00680         LEAVE (" ");
00681         return;
00682     }
00683     /* modify slot data */
00684     slots = qof_instance_get_slots (inst);
00685     if (slots)
00686     {
00687         /* update and delete KVP data */
00688         /* id, guid, path, type, value */
00689         qb.sql_str = g_strconcat ("UPDATE ", QSQL_KVP_TABLE,
00690             " SET  (kvp_id \"", gstr, "\", ", NULL);
00691         kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb);
00692         qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION);
00693         if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00694                 NULL, &qb, &qsql_be->err) != SQLITE_OK)
00695         {
00696             qof_error_set_be (be, qsql_be->err_insert);
00697             qsql_be->error = TRUE;
00698             PERR (" error on KVP create_event:%s", qsql_be->err);
00699         }
00700         else
00701         {
00702             ((QofInstance *) qb.ent)->dirty = FALSE;
00703             qsql_be->error = FALSE;
00704             g_free (qb.sql_str);
00705         }
00706     }
00707     g_free (gstr);
00708     LEAVE (" ");
00709 }
00710 
00712 static gint
00713 record_foreach (gpointer builder, gint col_num, gchar ** strings,
00714     gchar ** columnNames)
00715 {
00716     QSQLiteBackend *qsql_be;
00717     struct QsqlBuilder *qb;
00718     const QofParam *param;
00719     QofInstance *inst;
00720     QofEntity *ent;
00721     gint i;
00722 
00723     g_return_val_if_fail (builder, QSQL_ERROR);
00724     qb = (struct QsqlBuilder *) builder;
00725     qsql_be = qb->qsql_be;
00726     qof_event_suspend ();
00727     inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book);
00728     ent = &inst->entity;
00729     for (i = 0; i < col_num; i++)
00730     {
00731         /* get param and set as string */
00732         param = qof_class_get_parameter (qb->e_type, columnNames[i]);
00733         if (!param)
00734             continue;
00735         /* set the inst->param entry */
00736         inst->param = param;
00737         if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID))
00738         {
00739             GUID *guid;
00740             guid = guid_malloc ();
00741             if (!string_to_guid (strings[i], guid))
00742             {
00743                 DEBUG (" set guid failed:%s", strings[i]);
00744                 return QSQL_ERROR;
00745             }
00746             qof_entity_set_guid (ent, guid);
00747         }
00748         if (strings[i])
00749             qof_util_param_set_string (ent, param, strings[i]);
00750     }
00751     qof_event_resume ();
00752     return SQLITE_OK;
00753 }
00754 
00755 /* used by create/insert */
00756 static void
00757 string_param_foreach (QofParam * param, gpointer builder)
00758 {
00759     struct QsqlBuilder *qb;
00760     QSQLiteBackend *qsql_be;
00761     gchar *p_str, *old;
00762 
00763     qb = (struct QsqlBuilder *) builder;
00764     qsql_be = qb->qsql_be;
00765     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
00766         return;
00767     p_str = string_param_to_sql (param);
00768     /* skip empty values (no param_setfcn) */
00769     if (!p_str)
00770         return;
00771     old = g_strconcat (p_str, ",", NULL);
00772     qb->sql_str = add_to_sql (qb->sql_str, old);
00773     g_free (old);
00774     g_free (p_str);
00775 }
00776 
00777 static void
00778 update_param_foreach (QofParam * param, gpointer builder)
00779 {
00780     struct QsqlBuilder *qb;
00781     gchar *value, *add;
00782 
00783     qb = (struct QsqlBuilder *) builder;
00784     if (param != qb->dirty)
00785         return;
00786     /* update table set name=val,name=val where guid=gstr; */
00787     value = qof_util_param_to_string (qb->ent, param);
00788     if (value)
00789         g_strescape (value, NULL);
00790     if (!value)
00791         value = g_strdup ("");
00792     if (g_str_has_suffix (qb->sql_str, " "))
00793     {
00794         add = g_strconcat (param->param_name, "=\"", value, "\"", NULL);
00795         qb->sql_str = add_to_sql (qb->sql_str, add);
00796         g_free (add);
00797     }
00798     else
00799     {
00800         add =
00801             g_strconcat (",", param->param_name, "=\"", value, "\"", NULL);
00802         qb->sql_str = add_to_sql (qb->sql_str, add);
00803         g_free (add);
00804     }
00805 }
00806 
00807 static void
00808 update_dirty (gpointer value, gpointer builder)
00809 {
00810     QofInstance *inst;
00811     QofEntity *ent;
00812     struct QsqlBuilder *qb;
00813     QSQLiteBackend *qsql_be;
00814     QofBackend *be;
00815     gchar *gstr, *param_str;
00816 
00817     qb = (struct QsqlBuilder *) builder;
00818     qsql_be = qb->qsql_be;
00819     be = (QofBackend *) qsql_be;
00820     ent = (QofEntity *) value;
00821     inst = (QofInstance *) ent;
00822     if (!inst->dirty)
00823         return;
00824     ENTER (" ");
00825     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00826     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00827     /* qof_class_param_foreach  */
00828     qb->sql_str = g_strdup_printf ("UPDATE %s SET ", ent->e_type);
00829     qof_class_param_foreach (ent->e_type, update_param_foreach, qb);
00830     param_str = g_strdup_printf ("WHERE %s=\"%s\";", QOF_TYPE_GUID, gstr);
00831     qb->sql_str = add_to_sql (qb->sql_str, param_str);
00832     g_free (param_str);
00833     DEBUG (" update=%s", qb->sql_str);
00834     if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
00835             NULL, qb, &qsql_be->err) != SQLITE_OK)
00836     {
00837         qof_error_set_be (be, qsql_be->err_update);
00838         qsql_be->error = TRUE;
00839         PERR (" error on update_dirty:%s", qsql_be->err);
00840     }
00841     else
00842     {
00843         qof_error_get_message_be (be);
00844         qsql_be->error = FALSE;
00845         inst->dirty = FALSE;
00846     }
00847     LEAVE (" ");
00848     g_free (gstr);
00849     return;
00850 }
00851 
00852 static gint
00853 create_dirty_list (gpointer builder, gint col_num, gchar ** strings,
00854     gchar ** columnNames)
00855 {
00856     struct QsqlBuilder *qb;
00857     QofInstance *inst;
00858     const QofParam *param;
00859     gchar *value, *columnName, *tmp;
00860 
00861     param = NULL;
00862     qb = (struct QsqlBuilder *) builder;
00863     /* qb->ent is the live data, strings is the sqlite data */
00864     inst = (QofInstance *) qb->ent;
00865     qb->exists = TRUE;
00866     if (!inst->dirty)
00867         return SQLITE_OK;
00868     columnName = columnNames[col_num];
00869     tmp = strings[col_num];
00870     param = qof_class_get_parameter (qb->ent->e_type, columnName);
00871     if (!param)
00872         return SQLITE_OK;
00873     value = qof_util_param_to_string (qb->ent, param);
00874     qb->dirty = param;
00875     qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent);
00876     DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list));
00877     return SQLITE_OK;
00878 }
00879 
00880 static gint
00881 mark_entity (gpointer builder, gint col_num, gchar ** strings,
00882     gchar ** columnNames)
00883 {
00884     struct QsqlBuilder *qb;
00885 
00886     qb = (struct QsqlBuilder *) builder;
00887     qb->exists = TRUE;
00888     return SQLITE_OK;
00889 }
00890 
00891 static void
00892 qsql_create (QofBackend * be, QofInstance * inst)
00893 {
00894     gchar *gstr;
00895     QSQLiteBackend *qsql_be;
00896     struct QsqlBuilder qb;
00897     QofEntity *ent;
00898     KvpFrame *slots;
00899 
00900     qsql_be = (QSQLiteBackend *) be;
00901     if (!inst)
00902         return;
00903     if (loading)
00904         return;
00905     ent = (QofEntity *) inst;
00906     qof_event_suspend ();
00907     qb.has_slots = FALSE;
00908     ENTER (" %s", ent->e_type);
00909     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00910     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00911     qb.sql_str =
00912         g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";",
00913         ent->e_type, gstr);
00914     PINFO (" check exists: %s", qb.sql_str);
00915     qb.ent = ent;
00916     qb.dirty_list = NULL;
00917     qb.exists = FALSE;
00918     if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00919             mark_entity, &qb, &qsql_be->err) != SQLITE_OK)
00920     {
00921         qof_error_set_be (be, qsql_be->err_update);
00922         qsql_be->error = TRUE;
00923         PERR (" error on select :%s", qsql_be->err);
00924     }
00925     if (!qb.exists)
00926     {
00927         gchar *add;
00928         /* create new entity */
00929         qb.sql_str =
00930             g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
00931         qof_class_param_foreach (ent->e_type, create_param_list, &qb);
00932         add = g_strconcat (") VALUES (\"", gstr, "\"", NULL);
00933         qb.sql_str = add_to_sql (qb.sql_str, add);
00934         g_free (add);
00935         qof_class_param_foreach (ent->e_type, create_each_param, &qb);
00936         qb.sql_str = add_to_sql (qb.sql_str, ");");
00937         DEBUG (" sql_str= %s", qb.sql_str);
00938         if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00939                 NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00940         {
00941             qof_error_set_be (be, qsql_be->err_insert);
00942             qsql_be->error = TRUE;
00943             PERR (" error creating new entity:%s", qsql_be->err);
00944         }
00945         /* KVP here */
00946         slots = qof_instance_get_slots ((QofInstance *) ent);
00947         if (slots)
00948         {
00949             /* id, guid, path, type, value */
00950             qb.sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE,
00951                 "  (kvp_id, \"", gstr, "\", ", NULL);
00952             kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb);
00953             qb.sql_str = add_to_sql (qb.sql_str, ");");
00954         }
00955         if (qb.has_slots)
00956         {
00957             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
00958                     NULL, &qb, &qsql_be->err) != SQLITE_OK)
00959             {
00960                 qof_error_set_be (be, qsql_be->err_insert);
00961                 qsql_be->error = TRUE;
00962                 PERR (" error on KVP create_event:%s:%s", qsql_be->err,
00963                     qb.sql_str);
00964             }
00965             else
00966             {
00967                 ((QofInstance *) ent)->dirty = FALSE;
00968                 qsql_be->error = FALSE;
00969             }
00970         }
00971     }
00972     g_free (qb.sql_str);
00973     g_free (gstr);
00974     qof_event_resume ();
00975     LEAVE (" ");
00976 }
00977 
00978 static void
00979 check_state (QofEntity * ent, gpointer builder)
00980 {
00981     gchar *gstr;
00982     QSQLiteBackend *qsql_be;
00983     struct QsqlBuilder *qb;
00984     QofBackend *be;
00985     QofInstance *inst;
00986     KvpFrame *slots;
00987 
00988     qb = (struct QsqlBuilder *) builder;
00989     qsql_be = qb->qsql_be;
00990     be = (QofBackend *) qsql_be;
00991     inst = (QofInstance *) ent;
00992     if (!inst->dirty)
00993         return;
00994     /* check if this entity already exists */
00995     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
00996     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00997     qb->sql_str =
00998         g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";",
00999         ent->e_type, gstr);
01000     qb->ent = ent;
01001     qb->dirty_list = NULL;
01002     /* assume entity does not yet exist in backend,
01003        e.g. being copied from another session. */
01004     qb->exists = FALSE;
01005     qb->qsql_be = qsql_be;
01006     /* update each dirty instance */
01007     /* Make a GList of dirty instances
01008        Don't update during a SELECT,
01009        UPDATE will fail with DB_LOCKED */
01010     if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
01011             create_dirty_list, qb, &qsql_be->err) != SQLITE_OK)
01012     {
01013         qof_error_set_be (be, qsql_be->err_update);
01014         qsql_be->error = TRUE;
01015         PERR (" error on check_state:%s", qsql_be->err);
01016     }
01017     if (!qb->exists)
01018     {
01019         gchar *add;
01020         /* create new entity */
01021         qb->sql_str =
01022             g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
01023         qof_class_param_foreach (ent->e_type, create_param_list, &qb);
01024         add = g_strconcat (") VALUES (\"", gstr, "\" ", NULL);
01025         qb->sql_str = add_to_sql (qb->sql_str, add);
01026         g_free (add);
01027         qof_class_param_foreach (ent->e_type, create_each_param, &qb);
01028         qb->sql_str = add_to_sql (qb->sql_str, ");");
01029         DEBUG (" sql_str= %s", qb->sql_str);
01030         if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
01031                 NULL, qb, &qsql_be->err) != SQLITE_OK)
01032         {
01033             qof_error_set_be (be, qsql_be->err_insert);
01034             qsql_be->error = TRUE;
01035             PERR (" error on check_state create_new:%s", qsql_be->err);
01036         }
01037         g_free (qb->sql_str);
01038         /* create KVP data too */
01039         slots = qof_instance_get_slots ((QofInstance *) ent);
01040         if (slots)
01041         {
01042             /* id, guid, path, type, value */
01043             qb->sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE,
01044                 "  (kvp_id \"", gstr, "\", ", NULL);
01045             kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb);
01046             qb->sql_str = add_to_sql (qb->sql_str, END_DB_VERSION);
01047             if (sqlite_exec (qsql_be->sqliteh, qb->sql_str,
01048                     NULL, &qb, &qsql_be->err) != SQLITE_OK)
01049             {
01050                 qof_error_set_be (be, qsql_be->err_insert);
01051                 qsql_be->error = TRUE;
01052                 PERR (" error on KVP create_event:%s", qsql_be->err);
01053             }
01054             else
01055             {
01056                 ((QofInstance *) ent)->dirty = FALSE;
01057                 qsql_be->error = FALSE;
01058             }
01059         }
01060     }
01061     /* update instead */
01062     g_list_foreach (qb->dirty_list, update_dirty, &qb);
01063     g_free (qb->sql_str);
01064     g_free (gstr);
01065 }
01066 
01075 static gint
01076 build_kvp_table (gpointer builder, gint col_num, gchar ** strings,
01077     gchar ** columnNames)
01078 {
01079     QSQLiteBackend *qsql_be;
01080     struct QsqlBuilder *qb;
01081     KvpFrame *frame;
01082     KvpValueType type;
01083     KvpValue *value;
01084     glong max;
01085     gchar *tail;
01086 
01087     g_return_val_if_fail (builder, QSQL_ERROR);
01088     qb = (struct QsqlBuilder *) builder;
01089     max = 0;
01090     qsql_be = qb->qsql_be;
01091     g_return_val_if_fail ((col_num < 4), QSQL_ERROR);
01092     g_return_val_if_fail (strings[2], QSQL_ERROR);
01093     frame = kvp_frame_new ();
01094     /* columnNames = fields strings = values
01095        [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value
01096        get type from type_string */
01097     type = sql_to_kvp_helper (strings[3]);
01098     if (type == 0)
01099     {
01100         PERR (" invalid type returned from kvp table");
01101         return QSQL_ERROR;
01102     }
01103     /* use the type to make a KvpValue from value */
01104     value = string_to_kvp_value (strings[4], type);
01105     if (!value)
01106     {
01107         PERR (" invalid KvpValue for type: %d", type);
01108         return QSQL_ERROR;
01109     }
01110     /* add the KvpValue to the frame at path */
01111     kvp_frame_set_value (frame, strings[2], value);
01112     /* index the frame under the entity GUID */
01113     g_hash_table_insert (qsql_be->kvp_table, strings[1], frame);
01114     /* index the guid under the kvp_id */
01115     g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]);
01116     errno = 0;
01117     max = strtol (strings[0], &tail, 0);
01118     if (errno == 0)
01119     {
01120         qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index;
01121     }
01122     return SQLITE_OK;
01123 }
01124 
01126 static void
01127 qsql_load_kvp (QSQLiteBackend * qsql_be)
01128 {
01129     struct QsqlBuilder qb;
01130     QofBackend *be;
01131     gint sq_code;
01132 
01133     g_return_if_fail (qsql_be);
01134     sq_code = SQLITE_OK;
01135     be = (QofBackend *) qsql_be;
01136     qb.sql_str =
01137         g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE);
01138     sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table,
01139             &qb, &qsql_be->err);
01140     /* catch older files without a sqlite_kvp table */
01141     if (sq_code == SQLITE_ERROR)
01142     {
01143         g_free (qb.sql_str);
01144         qb.sql_str =
01145             g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
01146             QSQL_KVP_TABLE, "kvp_id int primary key not null",
01147             "guid char(32)", "path mediumtext", "type mediumtext",
01148             "value text", END_DB_VERSION);
01149         PINFO (" creating kvp table. sql=%s", qb.sql_str);
01150         if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
01151             record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
01152         {
01153             qsql_be->error = TRUE;
01154             PERR (" unable to create kvp table:%s", qsql_be->err);
01155         }
01156     }
01157     else if (sq_code != SQLITE_OK)
01158     {
01159         qof_error_set_be (be, qsql_be->err_create);
01160         qsql_be->error = TRUE;
01161         PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code);
01162     }
01163     g_free (qb.sql_str);
01164 }
01165 
01167 static void
01168 qsql_class_foreach (QofObject * obj, gpointer data)
01169 {
01170     struct QsqlBuilder qb;
01171     QSQLiteBackend *qsql_be;
01172     QofBackend *be;
01173 
01174     qsql_be = (QSQLiteBackend *) data;
01175     be = (QofBackend *) qsql_be;
01176     qb.qsql_be = qsql_be;
01177     qb.e_type = obj->e_type;
01178     ENTER (" obj_type=%s", qb.e_type);
01179     switch (qsql_be->stm_type)
01180     {
01181     case SQL_NONE:
01182     case SQL_INSERT:
01183     case SQL_DELETE:
01184     case SQL_UPDATE:
01185         {
01186             break;
01187         }
01188     case SQL_CREATE:
01189         {
01190             /* KVP is handled separately */
01191             qb.sql_str =
01192                 g_strdup_printf ("CREATE TABLE %s (", obj->e_type);
01193             qof_class_param_foreach (obj->e_type, string_param_foreach,
01194                 &qb);
01195             qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION);
01196             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
01197                     NULL, NULL, &qsql_be->err) != SQLITE_OK)
01198             {
01199                 qof_error_set_be (be, qsql_be->err_create);
01200                 qsql_be->error = TRUE;
01201                 PERR (" error on SQL_CREATE:%s", qsql_be->err);
01202             }
01203             g_free (qb.sql_str);
01204             break;
01205         }
01206     case SQL_LOAD:
01207         {
01208             qb.sql_str =
01209                 g_strdup_printf ("SELECT * FROM %s;", obj->e_type);
01210             PINFO (" sql=%s", qb.sql_str);
01211             if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
01212                     record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
01213             {
01214                 qsql_be->error = TRUE;
01215                 PERR (" error on SQL_LOAD:%s", qsql_be->err);
01216             }
01217             break;
01218         }
01219     case SQL_WRITE:
01220         {
01221             if (!qof_book_not_saved (qsql_be->book))
01222                 break;
01223             qof_object_foreach (obj->e_type, qsql_be->book, check_state,
01224                 &qb);
01225             break;
01226         }
01227     }
01228     LEAVE (" ");
01229 }
01230 
01231 static void
01232 qsql_backend_createdb (QofBackend * be, QofSession * session)
01233 {
01234     FILE *f;
01235     QSQLiteBackend *qsql_be;
01236     struct QsqlBuilder qb;
01237 
01238     g_return_if_fail (be || session);
01239     ENTER (" ");
01240     qsql_be = (QSQLiteBackend *) be;
01241     qsql_be->stm_type = SQL_CREATE;
01242     qb.qsql_be = qsql_be;
01243     qsql_be->book = qof_session_get_book (session);
01244     DEBUG (" create_file %s", qsql_be->fullpath);
01245     f = fopen (qsql_be->fullpath, "a+");
01246     if (f)
01247         fclose (f);
01248     else
01249     {
01250         qof_error_set (session, qof_error_register
01251             (_("Unable to open the output file '%s' - do you have "
01252                     "permission to create this file?"), TRUE));
01253         qsql_be->error = TRUE;
01254         LEAVE (" unable to create new file '%s'", qsql_be->fullpath);
01255         return;
01256     }
01257     qsql_be->sqliteh =
01258         sqlite_open (qsql_be->fullpath, 0644, &qsql_be->err);
01259     if (!qsql_be->sqliteh)
01260     {
01261         qof_error_set_be (be, qsql_be->err_create);
01262         qsql_be->error = TRUE;
01263         LEAVE (" unable to open sqlite:%s", qsql_be->err);
01264         return;
01265     }
01266     qof_object_foreach_type (qsql_class_foreach, qsql_be);
01267     /* create the KVP table here
01268        preset table name, internal_id, guid_as_string, path, type, value
01269      */
01270     qb.sql_str =
01271         g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
01272         QSQL_KVP_TABLE, "kvp_id int primary key not null",
01273         "guid char(32)", "path mediumtext", "type mediumtext",
01274         "value text", END_DB_VERSION);
01275     PINFO (" sql=%s", qb.sql_str);
01276     if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
01277             record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
01278     {
01279         qsql_be->error = TRUE;
01280         PERR (" unable to create kvp table:%s", qsql_be->err);
01281     }
01282     g_free (qb.sql_str);
01283     LEAVE (" ");
01284 }
01285 
01286 static void
01287 qsql_backend_opendb (QofBackend * be, QofSession * session)
01288 {
01289     QSQLiteBackend *qsql_be;
01290 
01291     g_return_if_fail (be || session);
01292     ENTER (" ");
01293     qsql_be = (QSQLiteBackend *) be;
01294     qsql_be->sqliteh =
01295         sqlite_open (qsql_be->fullpath, 0666, &qsql_be->err);
01296     if (!qsql_be->sqliteh)
01297     {
01298         qof_error_set_be (be, qof_error_register
01299             (_("Unable to open the sqlite database '%s'."), TRUE));
01300         qsql_be->error = TRUE;
01301         PERR (" %s", qsql_be->err);
01302     }
01303     LEAVE (" %s", qsql_be->fullpath);
01304 }
01305 
01306 static void
01307 qsqlite_session_begin (QofBackend * be, QofSession * session,
01308     const gchar * book_path, gboolean ignore_lock,
01309     gboolean create_if_nonexistent)
01310 {
01311     QSQLiteBackend *qsql_be;
01312     gchar **pp;
01313     struct stat statinfo;
01314     gint stat_val;
01315 
01316     g_return_if_fail (be);
01317     ENTER (" book_path=%s", book_path);
01318     qsql_be = (QSQLiteBackend *) be;
01319     qsql_be->fullpath = NULL;
01320     if (book_path == NULL)
01321     {
01322         qof_error_set_be (be, qof_error_register
01323             (_("Please provide a filename for sqlite."), FALSE));
01324         qsql_be->error = TRUE;
01325         LEAVE (" bad URL");
01326         return;
01327     }
01328     /* book_path => sqlite_file_name */
01329     pp = g_strsplit (book_path, ":", 2);
01330     if (0 == safe_strcmp (pp[0], ACCESS_METHOD))
01331     {
01332         qsql_be->fullpath = g_strdup (pp[1]);
01333         g_strfreev (pp);
01334     }
01335     else
01336         qsql_be->fullpath = g_strdup (book_path);
01337     be->fullpath = g_strdup (qsql_be->fullpath);
01338     PINFO (" final path = %s", qsql_be->fullpath);
01339     stat_val = g_stat (qsql_be->fullpath, &statinfo);
01340     if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0)
01341         qsql_backend_createdb (be, session);
01342     if (!qsql_be->error)
01343         qsql_backend_opendb (be, session);
01344     if (qof_error_check_be (be) || qsql_be->error)
01345     {
01346         LEAVE (" open failed");
01347         return;
01348     }
01349     qsql_be->create_handler =
01350         qof_event_register_handler (create_event, qsql_be);
01351     qsql_be->delete_handler =
01352         qof_event_register_handler (delete_event, qsql_be);
01353     LEAVE (" db=%s", qsql_be->fullpath);
01354 }
01355 
01356 static void
01357 qsqlite_db_load (QofBackend * be, QofBook * book)
01358 {
01359     QSQLiteBackend *qsql_be;
01360 
01361     g_return_if_fail (be);
01362     ENTER (" ");
01363     loading = TRUE;
01364     qsql_be = (QSQLiteBackend *) be;
01365     qsql_be->stm_type = SQL_LOAD;
01366     qsql_be->book = book;
01367     /* iterate over registered objects */
01368     qof_object_foreach_type (qsql_class_foreach, qsql_be);
01369     qsql_load_kvp (qsql_be);
01370     loading = FALSE;
01371     LEAVE (" ");
01372 }
01373 
01374 static void
01375 qsqlite_write_db (QofBackend * be, QofBook * book)
01376 {
01377     QSQLiteBackend *qsql_be;
01378 
01379     g_return_if_fail (be);
01380     qsql_be = (QSQLiteBackend *) be;
01381     qsql_be->stm_type = SQL_WRITE;
01382     qsql_be->book = book;
01383     /* update each record with current state */
01384     qof_object_foreach_type (qsql_class_foreach, qsql_be);
01385     /* update KVP */
01386 }
01387 
01388 static gboolean
01389 qsql_determine_file_type (const gchar * path)
01390 {
01391     if (!path)
01392         return FALSE;
01393     return TRUE;
01394 }
01395 
01396 static void
01397 qsqlite_session_end (QofBackend * be)
01398 {
01399     QSQLiteBackend *qsql_be;
01400 
01401     g_return_if_fail (be);
01402     qsql_be = (QSQLiteBackend *) be;
01403     if (qsql_be->sqliteh)
01404         sqlite_close (qsql_be->sqliteh);
01405 }
01406 
01407 static void
01408 qsqlite_destroy_backend (QofBackend * be)
01409 {
01410     QSQLiteBackend *qsql_be;
01411 
01412     g_return_if_fail (be);
01413     qsql_be = (QSQLiteBackend *) be;
01414     g_hash_table_destroy (qsql_be->kvp_table);
01415     g_hash_table_destroy (qsql_be->kvp_id);
01416     qof_event_unregister_handler (qsql_be->create_handler);
01417     qof_event_unregister_handler (qsql_be->delete_handler);
01418     g_free (be);
01419     g_free (qsql_be);
01420 }
01421 
01422 static void
01423 qsql_provider_free (QofBackendProvider * prov)
01424 {
01425     prov->provider_name = NULL;
01426     prov->access_method = NULL;
01427     g_free (prov);
01428 }
01429 
01445 static QofBackend *
01446 qsql_backend_new (void)
01447 {
01448     QSQLiteBackend *qsql_be;
01449     QofBackend *be;
01450 
01451     ENTER (" ");
01452     qsql_be = g_new0 (QSQLiteBackend, 1);
01453     be = (QofBackend *) qsql_be;
01454     qof_backend_init (be);
01455     qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal);
01456     qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal);
01457     qsql_be->dbversion = QOF_OBJECT_VERSION;
01458     qsql_be->stm_type = SQL_NONE;
01459     qsql_be->err_delete =
01460         qof_error_register (_("Unable to delete record."), FALSE);
01461     qsql_be->err_create =
01462         qof_error_register (_("Unable to create record."), FALSE);
01463     qsql_be->err_insert =
01464         qof_error_register (_("Unable to insert a new record."), FALSE);
01465     qsql_be->err_update =
01466         qof_error_register (_("Unable to update existing record."), FALSE);
01467     be->session_begin = qsqlite_session_begin;
01468 
01469     be->session_end = qsqlite_session_end;
01470     be->destroy_backend = qsqlite_destroy_backend;
01471     be->load = qsqlite_db_load;
01472     be->save_may_clobber_data = NULL;
01473     /* begin: create an empty entity if none exists,
01474        even if events are suspended. */
01475     be->begin = qsql_create;
01476     /* commit: write to sqlite, commit undo record. */
01477     be->commit = qsql_modify;
01478     be->rollback = NULL;
01479     /* would need a QofQuery back to QofSqlQuery conversion. */
01480     be->compile_query = NULL;
01481     /* unused */
01482     be->free_query = NULL;
01483     be->run_query = NULL;
01484     be->counter = NULL;
01485     /* The QOF SQLite backend is not multi-user - all QOF users are the same. */
01486     be->events_pending = NULL;
01487     be->process_events = NULL;
01488 
01489     be->sync = qsqlite_write_db;
01490     be->load_config = NULL;
01491     be->get_config = NULL;
01492     LEAVE (" ");
01493     return be;
01494 }
01495 
01496 void
01497 qof_sqlite_provider_init (void)
01498 {
01499     QofBackendProvider *prov;
01500 
01501     ENTER (" ");
01502     bindtextdomain (PACKAGE, LOCALE_DIR);
01503     prov = g_new0 (QofBackendProvider, 1);
01504     prov->provider_name = "QOF SQLite Backend Version 0.3";
01505     prov->access_method = ACCESS_METHOD;
01506     prov->partial_book_supported = TRUE;
01507     prov->backend_new = qsql_backend_new;
01508     prov->check_data_type = qsql_determine_file_type;
01509     prov->provider_free = qsql_provider_free;
01510     qof_backend_register_provider (prov);
01511     LEAVE (" ");
01512 }
01513 
01514 /* ================= END OF FILE =================== */

Generated on Thu Jan 31 22:50:25 2008 for QOF by  doxygen 1.5.4