qof-sqlite.c File Reference

Public interface of qof-backend-sqlite. More...

#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <glib/gstdio.h>
#include <sqlite.h>
#include <glib.h>
#include <libintl.h>
#include "qof.h"
#include "qofsql-p.h"
#include "qof-sqlite.h"
#include "kvputil-p.h"

Go to the source code of this file.

Defines

#define _(String)   dgettext (GETTEXT_PACKAGE, String)
#define ACCESS_METHOD   "sqlite"
#define PRIORITY_HIGH   9
#define PRIORITY_STANDARD   5
#define PRIORITY_LOW   0
#define QSQL_ERROR   -1
#define QSQL_KVP_TABLE   "sqlite_kvp"
#define END_DB_VERSION   " dbversion int );"

Functions

static KvpValuestring_to_kvp_value (const gchar *content, KvpValueType type)
static G_GNUC_UNUSED void kvpvalue_to_sql (const gchar *key, KvpValue *val, gpointer builder)
static void delete_event (QofEntity *ent, QofEventId event_type, gpointer handler_data, gpointer event_data)
 use the new-style event handlers for insert and update insert runs after QOF_EVENT_CREATE delete runs before QOF_EVENT_DESTROY
static void create_event (QofEntity *ent, QofEventId event_type, gpointer handler_data, gpointer event_data)
static void qsql_modify (QofBackend *be, QofInstance *inst)
static gint record_foreach (gpointer builder, gint col_num, gchar **strings, gchar **columnNames)
static void update_dirty (gpointer value, gpointer builder)
static gint create_dirty_list (gpointer builder, gint col_num, gchar **strings, gchar **columnNames)
static gint mark_entity (gpointer builder, gint col_num, gchar **strings, gchar **columnNames)
static void qsql_create (QofBackend *be, QofInstance *inst)
static void check_state (QofEntity *ent, gpointer builder)
static gint build_kvp_table (gpointer builder, gint col_num, gchar **strings, gchar **columnNames)
 chekc kvp data once per record
static void qsql_load_kvp (QSQLiteBackend *qsql_be)
static void qsql_class_foreach (QofObject *obj, gpointer data)
static void qsql_backend_createdb (QofBackend *be, QofSession *session)
static void qsql_backend_opendb (QofBackend *be, QofSession *session)
static void qsqlite_session_begin (QofBackend *be, QofSession *session, const gchar *book_path, gboolean ignore_lock, gboolean create_if_nonexistent)
static void qsqlite_db_load (QofBackend *be, QofBook *book)
static void qsqlite_write_db (QofBackend *be, QofBook *book)
static gboolean qsql_determine_file_type (const gchar *path)
static void qsqlite_session_end (QofBackend *be)
static void qsqlite_destroy_backend (QofBackend *be)
static void qsql_provider_free (QofBackendProvider *prov)
static QofBackendqsql_backend_new (void)
 Starts the backend and creates the context.
void qof_sqlite_provider_init (void)
 Initialises the SQLite backend.

Variables

static QofLogModule log_module = QOF_MOD_SQLITE
static gboolean loading = FALSE


Detailed Description

Public interface of qof-backend-sqlite.

Author:
Copyright 2006-2008 Neil Williams <linux@codehelp.co.uk>

Definition in file qof-sqlite.c.


Define Documentation

#define PRIORITY_HIGH   9

Indicates an item with high priority.

Definition at line 46 of file qof-sqlite.c.

#define PRIORITY_LOW   0

Indicates a low priority item.

Definition at line 50 of file qof-sqlite.c.

#define PRIORITY_STANDARD   5

Indicates an item with default priority.

Definition at line 48 of file qof-sqlite.c.

#define QSQL_ERROR   -1

Indicate an error to sqlite

Definition at line 52 of file qof-sqlite.c.

#define QSQL_KVP_TABLE   "sqlite_kvp"

One KVP table per file for all instances.

Definition at line 55 of file qof-sqlite.c.


Function Documentation

static gint build_kvp_table ( gpointer  builder,
gint  col_num,
gchar **  strings,
gchar **  columnNames 
) [static]

chekc kvp data once per record

creates a new KvpFrame as data for a GHashTable with the guid as key

Todo:
improve error checking support in case the SQLite data is tweaked manually.

Definition at line 665 of file qof-sqlite.c.

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 }

static void create_event ( QofEntity ent,
QofEventId  event_type,
gpointer  handler_data,
gpointer  event_data 
) [static]

receives QSQLiteBackend, passes on QsqlBuilder

Bug:
create one func to create_param_list and create_each_param

Definition at line 295 of file qof-sqlite.c.

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 }

static G_GNUC_UNUSED void kvpvalue_to_sql ( const gchar *  key,
KvpValue val,
gpointer  builder 
) [static]

returns the VALUES for INSERT in pre-defined order

Definition at line 195 of file qof-sqlite.c.

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 }

static QofBackend* qsql_backend_new ( void   )  [static]

Starts the backend and creates the context.

Note:
Take care when handling the main QSQLiteBackend context and the QsqlBuilder context. QSQLiteBackend contains the long-term data, QsqlBuilder the transient. Only QSQLiteBackend is guaranteed to exist at any one time. All functions need to be able to locate the QSQLiteBackend. Functions started from the QofBackend routines or from the event handlers will be passed the QofBackend which can be cast to QSQLiteBackend. Internal functions create a local QsqlBuilder struct and set the QSQLiteBackend pointer before passing a pointer to the QsqlBuilder. Use the qsql_ prefix only for functions that are started from QofBackend and the _event suffix for QofEvent.

Definition at line 1014 of file qof-sqlite.c.

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 }

static void qsql_class_foreach ( QofObject obj,
gpointer  data 
) [static]

receives QSQLiteBackend from QofBackend

Definition at line 757 of file qof-sqlite.c.

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 }

static void qsql_load_kvp ( QSQLiteBackend *  qsql_be  )  [static]

only call once per book

Definition at line 716 of file qof-sqlite.c.

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 }

static gint record_foreach ( gpointer  builder,
gint  col_num,
gchar **  strings,
gchar **  columnNames 
) [static]

Todo:
need a KVP version to load data into the slots

Definition at line 419 of file qof-sqlite.c.

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 }

static KvpValue* string_to_kvp_value ( const gchar *  content,
KvpValueType  type 
) [static]

Todo:
reconcile the duplication with the QSF (and GDA) version

Definition at line 117 of file qof-sqlite.c.

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 }


Generated on Mon Jul 13 05:15:21 2009 for QOF by  doxygen 1.5.9