qof-sqlite.c

00001 /****************************************************************
00002  *            qof-sqlite.c
00003  *
00004  *  Sun Jan 15 12:52:46 2006
00005  *  Copyright  2006  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 <glib/gstdio.h>
00026 #include <sqlite.h>
00027 #include <glib.h>
00028 #include <libintl.h>
00029 #include "qof.h"
00030 
00031 #define _(String) dgettext (GETTEXT_PACKAGE, String)
00032 #define QOF_MOD_SQLITE "qof-sqlite-module"
00033 #define ACCESS_METHOD "sqlite"
00034 
00035 /* Indicates an item with high priority.  */
00036 #define PRIORITY_HIGH       9
00037 /* Indicates an item with default priority. */
00038 #define PRIORITY_STANDARD   5
00039 /* Indicates a low priority item.  */
00040 #define PRIORITY_LOW        0
00041 /* Indicate an error to sqlite */
00042 #define QSQL_ERROR          -1
00043 
00044 #define END_DB_VERSION " dbversion int );"
00045 
00046 static QofLogModule log_module = QOF_MOD_SQLITE;
00047 
00048 typedef enum
00049 {
00050     /* no operation defined. init value. */
00051     SQL_NONE = 0,
00052     /* Create a new database */
00053     SQL_CREATE,
00054     /* Load all data from existing database. */
00055     SQL_LOAD,
00056     /* Write / sync all data to the database. */
00057     SQL_WRITE,
00058     /* Run a single INSERT statement. */
00059     SQL_INSERT,
00060     /* Run a single DELETE statement. */
00061     SQL_DELETE,
00062     /* Run a single UPDATE statement. */
00063     SQL_UPDATE
00064 }QsqlStatementType;
00065 
00066 typedef struct
00067 {
00068     QofBackend be;
00069     sqlite *sqliteh;
00070     QsqlStatementType stm_type;
00071     gint dbversion;
00072     gint create_handler;
00073     gint delete_handler;
00074     const gchar *fullpath;
00075     gchar *err;
00076     gchar *sql_str;
00077     gboolean error;
00078     QofIdType e_type;
00079     QofBook * book;
00080     QofErrorId err_delete, err_insert, err_update, err_create;
00081 } QSQLiteBackend;
00082 
00083 static gchar*
00084 qsql_param_to_sql(QofParam *param)
00085 {
00086     if(0 == safe_strcmp(param->param_type, QOF_TYPE_STRING))
00087         return g_strdup_printf(" %s mediumtext", param->param_name);
00088     if(0 == safe_strcmp(param->param_type, QOF_TYPE_BOOLEAN))
00089         return g_strdup_printf(" %s int", param->param_name);
00090     if(0 == safe_strcmp(param->param_type, QOF_TYPE_GUID))
00091         return g_strdup_printf(" %s char(32) PRIMARY KEY NOT NULL",
00092             param->param_name);
00093     if((0 == safe_strcmp(param->param_type, QOF_TYPE_NUMERIC))
00094         || (0 == safe_strcmp(param->param_type, QOF_TYPE_DOUBLE))
00095         || (0 == safe_strcmp(param->param_type, QOF_TYPE_DEBCRED)))
00096     {
00097         return g_strdup_printf(" %s double", param->param_name);
00098     }
00099     if(0 == safe_strcmp(param->param_type, QOF_TYPE_INT32))
00100         return g_strdup_printf(" %s int", param->param_name);
00101 #ifndef QOF_DISABLE_DEPRECATED
00102     if((0 == safe_strcmp(param->param_type, QOF_TYPE_DATE)) ||
00103         (0 == safe_strcmp(param->param_type, QOF_TYPE_TIME)))
00104 #else
00105     if(0 == safe_strcmp(param->param_type, QOF_TYPE_TIME))
00106 #endif
00107         return g_strdup_printf(" %s datetime", param->param_name);
00108     if(0 == safe_strcmp(param->param_type, QOF_TYPE_CHAR))
00109         return g_strdup_printf(" %s char(1)", param->param_name);
00110     if(0 == safe_strcmp(param->param_type, QOF_TYPE_KVP))
00111         return g_strdup_printf(" %s mediumtext", param->param_name);
00112     if(0 == safe_strcmp(param->param_type, QOF_TYPE_COLLECT))
00113         return g_strdup_printf(" %s char(32)", param->param_name);
00114     return g_strdup_printf(" %s char(32)", param->param_name);
00115 }
00116 
00122 struct QsqlBuilder
00123 {
00125     QSQLiteBackend *qsql_be;
00127     QofEntity *ent;
00129     gchar *sql_str;
00131     GList *dirty_list;
00133     gboolean exists;
00135     const QofParam * dirty;
00136 };
00137 
00138 static void
00139 create_param_list (QofParam *param, gpointer user_data)
00140 {
00141     struct QsqlBuilder *qb;
00142     qb = (struct QsqlBuilder *)user_data;
00143 
00144     if (!g_str_has_suffix (qb->sql_str, "("))
00145         qb->sql_str = g_strconcat (qb->sql_str, ", ", 
00146             param->param_name, NULL);
00147     else
00148         qb->sql_str = g_strconcat (qb->sql_str, 
00149             param->param_name, NULL);
00150 }
00151 
00152 static void
00153 create_each_param (QofParam *param, gpointer user_data)
00154 {
00155     gchar *value;
00156     struct QsqlBuilder *qb;
00157     qb = (struct QsqlBuilder *)user_data;
00158 
00160     value = qof_util_param_to_string (qb->ent, param);
00161     if (value)
00162         g_strescape (value, NULL);
00163     if (!value)
00164         value = g_strdup ("");
00165     if (!g_str_has_suffix (qb->sql_str, "("))
00166         qb->sql_str = g_strconcat (qb->sql_str, ", \"", 
00167             value, "\"", NULL);
00168     else
00169         qb->sql_str = g_strconcat (qb->sql_str, "\"",
00170             value, "\"", NULL);
00171 }
00172 
00173 /* need new-style event handlers for insert and update 
00174 insert runs after QOF_EVENT_CREATE
00175 delete runs before QOF_EVENT_DESTROY
00176 */
00177 static void 
00178 delete_event (QofEntity *ent, QofEventId event_type, 
00179               gpointer handler_data, gpointer event_data)
00180 {
00181     QofBackend *be;
00182     QSQLiteBackend *qsql_be;
00183     gchar gstr[GUID_ENCODING_LENGTH+1];
00184     gchar * sql_str;
00185 
00186     qsql_be = (QSQLiteBackend*)handler_data;
00187     be = (QofBackend*)qsql_be;
00188     if (!ent)
00189         return;
00190     if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
00191         return;
00192     ENTER (" %s", ent->e_type);
00193     switch (event_type)
00194     {
00195         case QOF_EVENT_DESTROY :
00196         {
00197             guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00198             sql_str = g_strdup_printf ("DELETE from %s ", 
00199                 ent->e_type);
00200             sql_str = g_strconcat (sql_str, 
00201                 "WHERE ", QOF_TYPE_GUID, "='", gstr,
00202                 "';", NULL);
00203             DEBUG (" sql_str=%s", sql_str);
00204             if(sqlite_exec (qsql_be->sqliteh, sql_str, 
00205                 NULL, qsql_be, &qsql_be->err) !=
00206                 SQLITE_OK)
00207             {
00208                 qof_error_set_be (be, qsql_be->err_delete);
00209                 qsql_be->error = TRUE;
00210                 PERR (" %s", qsql_be->err);
00211             }
00212             break;
00213         }
00214         default : break;
00215     }
00216     LEAVE (" ");    
00217 }
00218 
00219 static void 
00220 create_event (QofEntity *ent, QofEventId event_type, 
00221               gpointer handler_data, gpointer event_data)
00222 {
00223     QofBackend *be;
00224     struct QsqlBuilder qb;
00225     QSQLiteBackend *qsql_be;
00226 
00227     qsql_be = (QSQLiteBackend*)handler_data;
00228     be = (QofBackend*)qsql_be;
00229     if (!ent)
00230         return;
00231     if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
00232         return;
00233     ENTER (" %s", ent->e_type);
00234     switch (event_type)
00235     {
00236         case QOF_EVENT_CREATE :
00237         {
00238             qb.ent = ent;
00239             qb.sql_str = g_strdup_printf ("INSERT into %s (", 
00240                 ent->e_type);
00241             qof_class_param_foreach (ent->e_type, create_param_list, 
00242                 &qb);
00243             qb.sql_str = g_strconcat (qb.sql_str, ") VALUES (", NULL);
00244             qof_class_param_foreach (ent->e_type, 
00245                 create_each_param, &qb);
00246             qb.sql_str = g_strconcat (qb.sql_str, ");", NULL);
00247             DEBUG (" sql_str=%s", qb.sql_str);
00248             if(sqlite_exec (qsql_be->sqliteh, qb.sql_str, 
00249                 NULL, qsql_be, &qsql_be->err) !=
00250                 SQLITE_OK)
00251             {
00252                 qof_error_set_be (be, qsql_be->err_insert);
00253                 qsql_be->error = TRUE;
00254                 PERR (" %s", qsql_be->err);
00255             }
00256             else
00257                 ((QofInstance*)ent)->dirty = FALSE;
00258             break;
00259         }
00260         default : break;
00261     }
00262     LEAVE (" ");
00263 }
00264 
00265 static void
00266 qsql_modify (QofBackend *be, QofInstance *inst)
00267 {
00268     struct QsqlBuilder qb;
00269     QSQLiteBackend *qsql_be;
00270     gchar gstr[GUID_ENCODING_LENGTH+1];
00271     gchar * param_str;
00272 
00273     qsql_be = (QSQLiteBackend*)be;
00274     if (!inst)
00275         return;
00276     if (!inst->param)
00277         return;
00278     ENTER (" ");
00279 
00280     guid_to_string_buff (qof_instance_get_guid (inst), gstr);
00281     qb.ent = (QofEntity*)inst;
00282     param_str = qof_util_param_to_string (qb.ent, inst->param);
00283     if (param_str)
00284         g_strescape (param_str, NULL);
00285     qb.sql_str = g_strdup_printf ("UPDATE %s SET %s=\"",
00286         qb.ent->e_type, inst->param->param_name);
00287     qb.sql_str = g_strconcat (qb.sql_str, param_str, 
00288         "\" WHERE ", QOF_TYPE_GUID, "='", gstr,
00289         "';", NULL);
00290     DEBUG (" sql_str=%s param_Str=%s", qb.sql_str, param_str);
00291     if(sqlite_exec (qsql_be->sqliteh, qb.sql_str, 
00292         NULL, qsql_be, &qsql_be->err) !=
00293         SQLITE_OK)
00294     {
00295         qof_error_set_be (be, qsql_be->err_update);
00296         qsql_be->error = TRUE;
00297         PERR (" %s", qsql_be->err);
00298     }
00299     else
00300         inst->dirty = FALSE;
00301     LEAVE (" ");
00302 }
00303 
00304 static gint 
00305 qsql_record_foreach(gpointer data, gint col_num, gchar **strings,
00306                     gchar **columnNames)
00307 {
00308     QSQLiteBackend *qsql_be;
00309     const QofParam * param;
00310     QofInstance * inst;
00311     gint i;
00312 
00313     g_return_val_if_fail(data, QSQL_ERROR);
00314     qsql_be = (QSQLiteBackend*)data;
00315     qof_event_suspend ();
00316     inst = (QofInstance*)qof_object_new_instance (qsql_be->e_type,
00317         qsql_be->book);
00318     for(i = 0;i < col_num; i++)
00319     {
00320         /* get param and set as string */
00321         param = qof_class_get_parameter (qsql_be->e_type, 
00322             columnNames[i]);
00323         if (!param)
00324             continue;
00325         if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID))
00326         {
00327             GUID * guid;
00328             guid = guid_malloc();
00329             if (!string_to_guid(strings[i], guid))
00330             {
00331                 DEBUG (" set guid failed:%s", strings[i]);
00332                 return QSQL_ERROR;
00333             }
00334             qof_entity_set_guid (&inst->entity, guid);
00335         }
00336         if (strings[1])
00337             qof_util_param_set_string (&inst->entity, param, strings[i]);
00338     }
00339     qof_event_resume ();
00340     return SQLITE_OK;
00341 }
00342 
00343 /* used by create/insert */
00344 static void
00345 qsql_param_foreach(QofParam *param, gpointer data)
00346 {
00347     QSQLiteBackend *qsql_be;
00348     gchar *p_str;
00349 
00350     qsql_be = (QSQLiteBackend*)data;
00351     p_str = qsql_param_to_sql(param);
00352     qsql_be->sql_str = g_strconcat(qsql_be->sql_str, 
00353         p_str, ",", NULL);
00354     g_free(p_str);
00355 }
00356 
00357 static void
00358 update_param_foreach(QofParam *param, gpointer user_data)
00359 {
00360     struct QsqlBuilder * qb;
00361     gchar * value;
00362 
00363     qb = (struct QsqlBuilder*) user_data;
00364     if (param != qb->dirty)
00365         return;
00366     /* update table set name=val,name=val where guid=gstr; */
00367     value = qof_util_param_to_string (qb->ent, param);
00368     if (value)
00369         g_strescape (value, NULL);
00370     if (!value)
00371         value = g_strdup ("");
00372     if (g_str_has_suffix (qb->sql_str, " "))
00373         qb->sql_str = g_strconcat (qb->sql_str, 
00374         param->param_name, "=\"",  value, "\"", NULL);
00375     else
00376         qb->sql_str = g_strconcat (qb->sql_str, ",",
00377         param->param_name, "=\"", value, "\"", NULL);
00378 }
00379 
00380 static void
00381 update_dirty (gpointer value, gpointer user_data)
00382 {
00383     QofInstance * inst;
00384     QofEntity * ent;
00385     struct QsqlBuilder * qb;
00386     QSQLiteBackend *qsql_be;
00387     QofBackend * be;
00388     gchar gstr[GUID_ENCODING_LENGTH+1];
00389     gchar * param_str;
00390 
00391     qb = (struct QsqlBuilder*) user_data;
00392     qsql_be = qb->qsql_be;
00393     be = (QofBackend*)qsql_be;
00394     ent = (QofEntity*) value;
00395     inst = (QofInstance*) ent;
00396     if (!inst->dirty)
00397         return;
00398     ENTER (" ");
00399     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00400     /* qof_class_param_foreach  */
00401     qb->sql_str = g_strdup_printf ("UPDATE %s SET ",
00402         ent->e_type);
00403     qof_class_param_foreach (ent->e_type, update_param_foreach, qb);
00404     param_str = g_strdup_printf ("WHERE %s=\"%s\";",
00405         QOF_TYPE_GUID, gstr);
00406     qb->sql_str = g_strconcat (qb->sql_str, param_str, NULL);
00407     g_free (param_str);
00408     DEBUG (" update=%s", qb->sql_str);
00409     if(sqlite_exec (qsql_be->sqliteh, qb->sql_str, 
00410         NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00411     {
00412         qof_error_set_be (be, qsql_be->err_update);
00413         qsql_be->error = TRUE;
00414         PERR (" %s", qsql_be->err);
00415     }
00416     else
00417         inst->dirty = FALSE;
00418     LEAVE (" ");
00419     return;
00420 }
00421 
00422 static gint
00423 create_dirty_list (gpointer data, gint col_num, gchar **strings,
00424                    gchar **columnNames)
00425 {
00426     struct QsqlBuilder * qb;
00427     QofInstance * inst;
00428     const QofParam * param;
00429     gchar * value, *columnName, *tmp;
00430 
00431     qb = (struct QsqlBuilder*) data;
00432     /* qb->ent is the live data, strings is the sqlite data */
00433     inst = (QofInstance*) qb->ent;
00434     qb->exists = TRUE;
00435     if (!inst->dirty)
00436         return SQLITE_OK;
00437     columnName = columnNames[col_num];
00438     tmp = strings[col_num];
00439     param = qof_class_get_parameter (qb->ent->e_type, columnName);
00440     value = qof_util_param_to_string (qb->ent, param);
00441     PINFO (" tmp=%s value=%s columnName=%s", tmp, value, columnName);
00442     qb->dirty = param;
00443     qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent);
00444     DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list));
00445     return SQLITE_OK;
00446 }
00447 
00448 static gint
00449 mark_entity (gpointer data, gint col_num, gchar **strings,
00450                    gchar **columnNames)
00451 {
00452     struct QsqlBuilder * qb;
00453 
00454     qb = (struct QsqlBuilder*) data;
00455     qb->exists = TRUE;
00456     DEBUG (" already exists");
00457     return SQLITE_OK;
00458 }
00459 
00460 static void
00461 qsql_create (QofBackend *be, QofInstance *inst)
00462 {
00463     gchar gstr[GUID_ENCODING_LENGTH+1];
00464     QSQLiteBackend *qsql_be;
00465     struct QsqlBuilder qb;
00466     QofEntity * ent;
00467 
00468     qsql_be = (QSQLiteBackend*)be;
00469     if (!inst)
00470         return;
00471     ent = (QofEntity*) inst;    
00472     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00473     qb.sql_str = g_strdup_printf(
00474         "SELECT * FROM %s where guid = \"%s\";", ent->e_type, gstr);
00475     PINFO (" write: %s", qb.sql_str);
00476     qb.ent = ent;
00477     qb.dirty_list = NULL;
00478     qb.exists = FALSE;
00479     if(sqlite_exec (qsql_be->sqliteh, qb.sql_str, 
00480         mark_entity, &qb, &qsql_be->err) !=
00481         SQLITE_OK)
00482     {
00483         qof_error_set_be (be, qsql_be->err_update);
00484         qsql_be->error = TRUE;
00485         PERR (" %s", qsql_be->err);
00486     }
00487     if (!qb.exists)
00488     {
00489         /* create new entity */
00490         PINFO (" insert");
00491         qb.sql_str = g_strdup_printf ("INSERT into %s (", 
00492             ent->e_type);
00493         qof_class_param_foreach (ent->e_type, 
00494             create_param_list, &qb);
00495         qb.sql_str = g_strconcat (qb.sql_str, ") VALUES (", NULL);
00496         qof_class_param_foreach (ent->e_type, 
00497             create_each_param, &qb);
00498         qb.sql_str = g_strconcat (qb.sql_str, ");", NULL);
00499         DEBUG (" sql_str= %s", qb.sql_str);
00500         if(sqlite_exec (qsql_be->sqliteh, qb.sql_str, 
00501             NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00502         {
00503             qof_error_set_be (be, qsql_be->err_insert);
00504             qsql_be->error = TRUE;
00505             PERR (" %s", qsql_be->err);
00506         }
00507     }
00508     g_free (qb.sql_str);
00509 }
00510 
00511 static void
00512 check_state (QofEntity * ent, gpointer user_data)
00513 {
00514     gchar gstr[GUID_ENCODING_LENGTH+1];
00515     QSQLiteBackend *qsql_be;
00516     struct QsqlBuilder qb;
00517     QofBackend *be;
00518     QofInstance * inst;
00519 
00520     qsql_be = (QSQLiteBackend*) user_data;
00521     be = (QofBackend*)qsql_be;
00522     inst = (QofInstance*)ent;
00523     if (!inst->dirty)
00524         return;
00525     /* check if this entity already exists */
00526     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
00527     qb.sql_str = g_strdup_printf(
00528         "SELECT * FROM %s where guid = \"%s\";", ent->e_type, gstr);
00529     PINFO (" write: %s", qb.sql_str);
00530     qb.ent = ent;
00531     qb.dirty_list = NULL;
00532     /* assume entity does not yet exist in backend,
00533     e.g. being copied from another session. */
00534     qb.exists = FALSE;
00535     qb.qsql_be = qsql_be;
00536     /* update each dirty instance */
00537     /* Make a GList of dirty instances
00538     Don't update during a SELECT, 
00539     UPDATE will fail with DB_LOCKED */
00540     if(sqlite_exec (qsql_be->sqliteh, qb.sql_str, 
00541         create_dirty_list, &qb, &qsql_be->err) !=
00542         SQLITE_OK)
00543     {
00544         qof_error_set_be (be, qsql_be->err_update);
00545         qsql_be->error = TRUE;
00546         PERR (" %s", qsql_be->err);
00547     }
00548     if (!qb.exists)
00549     {
00550         /* create new entity */
00551         PINFO (" insert");
00552         qb.sql_str = g_strdup_printf ("INSERT into %s (", 
00553             ent->e_type);
00554         qof_class_param_foreach (ent->e_type, 
00555             create_param_list, &qb);
00556         qb.sql_str = g_strconcat (qb.sql_str, ") VALUES (", NULL);
00557         qof_class_param_foreach (ent->e_type, 
00558             create_each_param, &qb);
00559         qb.sql_str = g_strconcat (qb.sql_str, ");", NULL);
00560         DEBUG (" sql_str= %s", qb.sql_str);
00561         if(sqlite_exec (qsql_be->sqliteh, qb.sql_str, 
00562             NULL, qsql_be, &qsql_be->err) != SQLITE_OK)
00563         {
00564             qof_error_set_be (be, qsql_be->err_insert);
00565             qsql_be->error = TRUE;
00566             PERR (" %s", qsql_be->err);
00567         }
00568     }
00569     /* update instead */
00570     g_list_foreach (qb.dirty_list, update_dirty, &qb);
00571     g_free (qb.sql_str);
00572 }
00573 
00574 static void
00575 qsql_class_foreach(QofObject *obj, gpointer data)
00576 {
00577     QSQLiteBackend *qsql_be;
00578     QofBackend *be;
00579 
00580     qsql_be = (QSQLiteBackend*)data;
00581     be = (QofBackend*)qsql_be;
00582     qsql_be->e_type = obj->e_type;
00583     ENTER (" obj_type=%s", qsql_be->e_type);
00584     switch (qsql_be->stm_type)
00585     {
00586         case SQL_NONE :
00587         case SQL_INSERT :
00588         case SQL_DELETE :
00589         case SQL_UPDATE :
00590         {
00591             break;
00592         }
00593         case SQL_CREATE :
00594         {
00595             qsql_be->sql_str = g_strdup_printf(
00596                 "CREATE TABLE %s (", obj->e_type);
00597             qof_class_param_foreach(obj->e_type, 
00598                 qsql_param_foreach, data);
00599             qsql_be->sql_str = g_strconcat(qsql_be->sql_str,
00600                 END_DB_VERSION, NULL);
00601             if(sqlite_exec (qsql_be->sqliteh, qsql_be->sql_str, 
00602                 NULL, NULL, &qsql_be->err) != SQLITE_OK)
00603             {
00604                 qof_error_set_be (be, qsql_be->err_create);
00605                 qsql_be->error = TRUE;
00606                 PERR (" %s", qsql_be->err);
00607             }
00608             PINFO(" %s", qsql_be->sql_str);
00609             g_free(qsql_be->sql_str);
00610             break;
00611         }
00612         case SQL_LOAD :
00613         {
00614             qsql_be->sql_str = g_strdup_printf(
00615                 "SELECT * FROM %s;", obj->e_type);
00616             PINFO (" sql=%s", qsql_be->sql_str);
00617             if(sqlite_exec(qsql_be->sqliteh, qsql_be->sql_str, 
00618                 qsql_record_foreach, qsql_be, &qsql_be->err) !=
00619                     SQLITE_OK)
00620             {
00621                 qsql_be->error = TRUE;
00622                 PERR (" %s", qsql_be->err);
00623             }
00624             break;
00625         }
00626         case SQL_WRITE : 
00627         {
00628             if (!qof_book_not_saved (qsql_be->book))
00629                 break;
00630             qof_object_foreach (obj->e_type, qsql_be->book,
00631                 check_state, qsql_be);
00632             break;
00633         }
00634     }
00635     LEAVE (" ");
00636 }
00637 
00638 static void
00639 qsql_backend_createdb(QofBackend *be, QofSession *session)
00640 {
00641     FILE * f;
00642     QSQLiteBackend *qsql_be;
00643 
00644     g_return_if_fail(be || session);
00645     ENTER (" ");
00646     qsql_be = (QSQLiteBackend*)be;
00647     qsql_be->stm_type = SQL_CREATE;
00648     qsql_be->book = qof_session_get_book (session);
00649     DEBUG (" create_file %s", qsql_be->fullpath);
00650     f = fopen (qsql_be->fullpath, "a+");
00651     if (f)
00652         fclose (f);
00653         else
00654     {
00655         qof_error_set (session, qof_error_register 
00656             (_("Unable to open the output file '%s' - do you have "
00657             "permission to create this file?"), TRUE));
00658         qsql_be->error = TRUE;
00659         LEAVE (" unable to create new file '%s'", 
00660             qsql_be->fullpath);
00661         return;
00662     }
00663     qsql_be->sqliteh = sqlite_open (qsql_be->fullpath, 0644, 
00664         &qsql_be->err);
00665     if(!qsql_be->sqliteh)
00666     {
00667         qof_error_set_be (be, qsql_be->err_create);
00668         qsql_be->error = TRUE;
00669         LEAVE (" %s", qsql_be->err);
00670         return;
00671     }
00672     qof_object_foreach_type(qsql_class_foreach, qsql_be);
00673     LEAVE (" ");
00674 }
00675 
00676 static void
00677 qsql_backend_opendb (QofBackend *be, QofSession *session)
00678 {
00679     QSQLiteBackend *qsql_be;
00680 
00681     g_return_if_fail(be || session);
00682     ENTER (" ");
00683     qsql_be = (QSQLiteBackend*)be;
00684     qsql_be->sqliteh = sqlite_open (qsql_be->fullpath, 0666, 
00685         &qsql_be->err);
00686     if(!qsql_be->sqliteh)
00687     {
00688         qof_error_set_be (be, qof_error_register
00689         (_("Unable to open the sqlite database '%s'."), TRUE));
00690         qsql_be->error = TRUE;
00691         PERR (" %s", qsql_be->err);
00692     }
00693     LEAVE (" %s", qsql_be->fullpath);
00694 }
00695 
00696 static void
00697 qsqlite_session_begin(QofBackend *be, QofSession *session, const 
00698                       gchar *book_path, gboolean ignore_lock,
00699                       gboolean create_if_nonexistent)
00700 {
00701     QSQLiteBackend *qsql_be;
00702     gchar** pp;
00703     struct stat statinfo;
00704     gint stat_val;
00705 
00706     g_return_if_fail(be);
00707     ENTER (" book_path=%s", book_path);
00708     qsql_be = (QSQLiteBackend*)be;
00709     qsql_be->fullpath = NULL;
00710     if(book_path == NULL)
00711     {
00712         qof_error_set_be (be, qof_error_register
00713         (_("Please provide a filename for sqlite."), FALSE));
00714         qsql_be->error = TRUE;
00715         LEAVE (" bad URL");
00716         return;
00717     }
00718     /* book_path => sqlite_file_name */
00719     pp = g_strsplit(book_path, ":", 2);
00720     if(0 == safe_strcmp(pp[0], ACCESS_METHOD))
00721     {
00722         qsql_be->fullpath = g_strdup(pp[1]);
00723         g_strfreev (pp);
00724     }
00725     else
00726         qsql_be->fullpath = g_strdup (book_path);
00727     be->fullpath = g_strdup (qsql_be->fullpath);
00728     PINFO (" final path = %s", qsql_be->fullpath);
00729     stat_val = g_stat (qsql_be->fullpath, &statinfo);
00730     if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0)
00731         qsql_backend_createdb (be, session);
00732     if (!qsql_be->error)
00733         qsql_backend_opendb (be, session);
00734     if(qof_error_check_be (be) || qsql_be->error) 
00735     {
00736         LEAVE(" open failed"); 
00737         return; 
00738     }
00739     qsql_be->create_handler = 
00740         qof_event_register_handler (create_event, qsql_be);
00741     qsql_be->delete_handler = 
00742         qof_event_register_handler (delete_event, qsql_be);
00743     LEAVE (" db=%s", qsql_be->fullpath);
00744 }
00745 
00746 static void
00747 qsqlite_db_load (QofBackend *be, QofBook *book)
00748 {
00749     QSQLiteBackend *qsql_be;
00750 
00751     g_return_if_fail(be);
00752     ENTER (" ");
00753     qsql_be = (QSQLiteBackend*)be;
00754     qsql_be->stm_type = SQL_LOAD;
00755     qsql_be->book = book;
00756     /* iterate over registered objects */
00757     qof_object_foreach_type(qsql_class_foreach, qsql_be);
00758     LEAVE (" ");
00759 }
00760 
00761 static void
00762 qsqlite_write_db (QofBackend *be, QofBook *book)
00763 {
00764     QSQLiteBackend *qsql_be;
00765 
00766     g_return_if_fail(be);
00767     qsql_be = (QSQLiteBackend*)be;
00768     qsql_be->stm_type = SQL_WRITE;
00769     qsql_be->book = book;
00770     /* update each record with current state */
00771     qof_object_foreach_type(qsql_class_foreach, qsql_be);
00772 }
00773 
00774 static gboolean
00775 qsql_determine_file_type (const gchar *path)
00776 {
00777     if (!path)
00778         return FALSE;
00779     return TRUE;
00780 }
00781 
00782 static void
00783 qsqlite_session_end (QofBackend *be)
00784 {
00785     QSQLiteBackend *qsql_be;
00786 
00787     g_return_if_fail(be);
00788     qsql_be = (QSQLiteBackend*)be;
00789     if (qsql_be->sqliteh)
00790         sqlite_close (qsql_be->sqliteh);
00791 }
00792 
00793 static void
00794 qsqlite_destroy_backend (QofBackend *be)
00795 {
00796     QSQLiteBackend *qsql_be;
00797 
00798     g_return_if_fail(be);
00799     qsql_be = (QSQLiteBackend*)be;
00800     qof_event_unregister_handler (qsql_be->create_handler);
00801     qof_event_unregister_handler (qsql_be->delete_handler);
00802     g_free (be);
00803     g_free (qsql_be);
00804 }
00805 
00806 static void
00807 qsql_provider_free (QofBackendProvider *prov)
00808 {
00809     prov->provider_name = NULL;
00810     prov->access_method = NULL;
00811     g_free (prov);
00812 }
00813 
00814 static QofBackend*
00815 qsql_backend_new(void)
00816 {
00817     QSQLiteBackend *qsql_be;
00818     QofBackend *be;
00819 
00820     ENTER (" ");
00821     qsql_be = g_new0(QSQLiteBackend, 1);
00822     be = (QofBackend*) qsql_be;
00823     qof_backend_init(be);
00824     qsql_be->dbversion = QOF_OBJECT_VERSION;
00825     qsql_be->stm_type = SQL_NONE;
00826     qsql_be->err_delete = qof_error_register
00827         (_("Unable to delete record."), FALSE);
00828     qsql_be->err_create = qof_error_register
00829         (_("Unable to create record."), FALSE);
00830     qsql_be->err_insert = qof_error_register
00831         (_("Unable to insert a new record."), FALSE);
00832     qsql_be->err_update = qof_error_register
00833         (_("Unable to update existing record."), FALSE);
00834 
00835     be->session_begin = qsqlite_session_begin;
00836 
00837     be->session_end = qsqlite_session_end;
00838     be->destroy_backend = qsqlite_destroy_backend;
00839     be->load = qsqlite_db_load;
00840     be->save_may_clobber_data = NULL;
00841     /* begin: create an empty entity is none exists,
00842     even if events are suspended. */
00843     be->begin = qsql_create;
00844     /* commit: write to sqlite, commit undo record. */
00845     be->commit = qsql_modify;
00846     be->rollback = NULL;
00847     /* would need a QofQuery back to QofSqlQuery conversion. */
00848     be->compile_query = NULL;
00849     /* unused */
00850     be->free_query = NULL;
00851     be->run_query = NULL;
00852     be->counter = NULL;
00853     /* The QOF SQLite backend is not multi-user - all QOF users are the same. */
00854     be->events_pending = NULL;
00855     be->process_events = NULL;
00856 
00857     be->sync = qsqlite_write_db;
00858     be->load_config = NULL;
00859     be->get_config = NULL;
00860     LEAVE (" ");
00861     return be;
00862 }
00863 
00864 void qof_sqlite_provider_init(void)
00865 {
00866     QofBackendProvider *prov;
00867 
00868     ENTER (" ");
00869     prov = g_new0 (QofBackendProvider, 1);
00870     prov->provider_name = "QOF SQLite Backend Version 0.2";
00871     prov->access_method = ACCESS_METHOD;
00872     prov->partial_book_supported = TRUE;
00873     prov->backend_new = qsql_backend_new;
00874     prov->check_data_type = qsql_determine_file_type;
00875     prov->provider_free = qsql_provider_free;
00876     qof_backend_register_provider (prov);
00877     LEAVE (" ");
00878 }
00879 
00880 /* ================= END OF FILE =================== */

Generated on Sat Aug 18 19:02:55 2007 for QOF by  doxygen 1.5.2