QOF 0.8.4
Defines | Functions | Variables
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-sqlite-module"
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 663 of file qof-sqlite.c.

{
    QSQLiteBackend *qsql_be;
    struct QsqlBuilder *qb;
    KvpFrame *frame;
    KvpValueType type;
    KvpValue *value;
    gulong max;
    gchar *tail;

    g_return_val_if_fail (builder, QSQL_ERROR);
    qb = (struct QsqlBuilder *) builder;
    max = 0;
    qsql_be = qb->qsql_be;
    g_return_val_if_fail ((col_num < 4), QSQL_ERROR);
    g_return_val_if_fail (strings[2], QSQL_ERROR);
    frame = kvp_frame_new ();
    /* columnNames = fields strings = values
       [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value
       get type from type_string */
    type = qof_id_to_kvp_value_type (strings[3]);
    if (type == 0)
    {
        PERR (" invalid type returned from kvp table");
        return QSQL_ERROR;
    }
    /* use the type to make a KvpValue from value */
    value = string_to_kvp_value (strings[4], type);
    if (!value)
    {
        PERR (" invalid KvpValue for type: %d", type);
        return QSQL_ERROR;
    }
    /* add the KvpValue to the frame at path */
    kvp_frame_set_value (frame, strings[2], value);
    /* index the frame under the entity GUID */
    g_hash_table_insert (qsql_be->kvp_table, strings[1], frame);
    /* index the guid under the kvp_id */
    g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]);
    errno = 0;
    max = strtol (strings[0], &tail, 0);
    if (errno == 0)
    {
        qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index;
    }
    return SQLITE_OK;
}
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 293 of file qof-sqlite.c.

{
    QofBackend *be;
    struct QsqlBuilder qb;
    QSQLiteBackend *qsql_be;
    gchar *gstr;
    KvpFrame *slots;

    qsql_be = (QSQLiteBackend *) handler_data;
    be = (QofBackend *) qsql_be;
    if (!ent)
        return;
    if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK))
        return;
    if (!qof_class_is_registered (ent->e_type))
        return;
    switch (event_type)
    {
    case QOF_EVENT_CREATE:
        {
            ENTER (" insert:%s", ent->e_type);
            gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
            guid_to_string_buff (qof_instance_get_guid ((QofInstance *)
                    ent), gstr);
            DEBUG (" guid=%s", gstr);
            qb.ent = ent;
            qb.sql_str = qof_sql_entity_insert (ent);
            DEBUG (" sql_str=%s", qb.sql_str);
            if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
                NULL, &qb, &qsql_be->err) != SQLITE_OK)
            {
                qof_error_set_be (be, qsql_be->err_insert);
                qsql_be->error = TRUE;
                PERR (" error on create_event:%s", qsql_be->err);
            }
            else
            {
                ((QofInstance *) ent)->dirty = FALSE;
                qsql_be->error = FALSE;
                g_free (qb.sql_str);
                g_free (gstr);
                LEAVE (" ");
                break;
            }
            /* insert sqlite_kvp data */
            slots = qof_instance_get_slots ((QofInstance *) ent);
            if (slots)
            {
                /* id, guid, path, type, value */
                qb.sql_str = qof_sql_entity_insert (ent);
                if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
                    NULL, &qb, &qsql_be->err) != SQLITE_OK)
                {
                    qof_error_set_be (be, qsql_be->err_insert);
                    qsql_be->error = TRUE;
                    PERR (" error on KVP create_event:%s", qsql_be->err);
                }
                else
                {
                    ((QofInstance *) ent)->dirty = FALSE;
                    qsql_be->error = FALSE;
                    g_free (qb.sql_str);
                    g_free (gstr);
                    LEAVE (" ");
                    break;
                }
            }
            g_free (qb.sql_str);
            g_free (gstr);
            LEAVE (" ");
            break;
        }
    default:
        break;
    }
}
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.

{
    QSQLiteBackend *qsql_be;
    struct QsqlBuilder *qb;
    KvpValueType n;

    ENTER (" ");
    qb = (struct QsqlBuilder *) builder;
    qsql_be = qb->qsql_be;
    g_return_if_fail (key && val && qsql_be);
    n = kvp_value_get_type (val);
    switch (n)
    {
    case KVP_TYPE_GINT64:
    case KVP_TYPE_DOUBLE:
    case KVP_TYPE_NUMERIC:
    case KVP_TYPE_STRING:
    case KVP_TYPE_GUID:
    case KVP_TYPE_TIME:
    case KVP_TYPE_BOOLEAN:
        {
            /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext",
               "type mediumtext", "value text", */

            qb->sql_str =
                g_strdup_printf (" kvp key=%s val=%s type=%s", key,
                kvp_value_to_bare_string (val),
                kvp_value_type_to_qof_id (n));
            DEBUG (" %s", qb->sql_str);
            qb->has_slots = TRUE;
            break;
        }
    case KVP_TYPE_FRAME:
        {
            kvp_frame_for_each_slot (kvp_value_get_frame (val),
                kvpvalue_to_sql, qb);
            break;
        }
    default:
        {
            PERR (" unsupported value = %d", kvp_value_get_type (val));
            break;
        }
    }
    LEAVE (" %s", qb->sql_str);
}
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 1012 of file qof-sqlite.c.

{
    QSQLiteBackend *qsql_be;
    QofBackend *be;

    ENTER (" ");
    qsql_be = g_new0 (QSQLiteBackend, 1);
    be = (QofBackend *) qsql_be;
    qof_backend_init (be);
    qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal);
    qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal);
    qsql_be->dbversion = QOF_OBJECT_VERSION;
    qsql_be->stm_type = SQL_NONE;
    qsql_be->err_delete =
        qof_error_register (_("Unable to delete record."), FALSE);
    qsql_be->err_create =
        qof_error_register (_("Unable to create record."), FALSE);
    qsql_be->err_insert =
        qof_error_register (_("Unable to insert a new record."), FALSE);
    qsql_be->err_update =
        qof_error_register (_("Unable to update existing record."), FALSE);
    be->session_begin = qsqlite_session_begin;

    be->session_end = qsqlite_session_end;
    be->destroy_backend = qsqlite_destroy_backend;
    be->load = qsqlite_db_load;
    be->save_may_clobber_data = NULL;
    /* begin: create an empty entity if none exists,
       even if events are suspended. */
    be->begin = qsql_create;
    /* commit: write to sqlite, commit undo record. */
    be->commit = qsql_modify;
    be->rollback = NULL;
    /* would need a QofQuery back to QofSqlQuery conversion. */
    be->compile_query = NULL;
    /* unused */
    be->free_query = NULL;
    be->run_query = NULL;
    be->counter = NULL;
    /* The QOF SQLite backend is not multi-user - all QOF users are the same. */
    be->events_pending = NULL;
    be->process_events = NULL;

    be->sync = qsqlite_write_db;
    be->load_config = NULL;
    be->get_config = NULL;
    LEAVE (" ");
    return be;
}
static void qsql_class_foreach ( QofObject obj,
gpointer  data 
) [static]

receives QSQLiteBackend from QofBackend

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

{
    struct QsqlBuilder qb;
    QSQLiteBackend *qsql_be;
    QofBackend *be;

    qsql_be = (QSQLiteBackend *) data;
    be = (QofBackend *) qsql_be;
    qb.qsql_be = qsql_be;
    qb.e_type = obj->e_type;
    ENTER (" obj_type=%s", qb.e_type);
    switch (qsql_be->stm_type)
    {
    case SQL_NONE:
    case SQL_INSERT:
    case SQL_DELETE:
    case SQL_UPDATE:
        {
            break;
        }
    case SQL_CREATE:
        {
            /* KVP is handled separately */
            qb.sql_str = qof_sql_object_create_table (obj);
            if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
                    NULL, NULL, &qsql_be->err) != SQLITE_OK)
            {
                qof_error_set_be (be, qsql_be->err_create);
                qsql_be->error = TRUE;
                PERR (" error on SQL_CREATE:%s", qsql_be->err);
            }
            g_free (qb.sql_str);
            break;
        }
    case SQL_LOAD:
        {
            qb.sql_str =
                g_strdup_printf ("SELECT * FROM %s;", obj->e_type);
            PINFO (" sql=%s", qb.sql_str);
            if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
                    record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
            {
                qsql_be->error = TRUE;
                PERR (" error on SQL_LOAD:%s", qsql_be->err);
            }
            break;
        }
    case SQL_WRITE:
        {
            if (!qof_book_not_saved (qsql_be->book))
                break;
            qof_object_foreach (obj->e_type, qsql_be->book, check_state,
                &qb);
            break;
        }
    }
    LEAVE (" ");
}
static void qsql_load_kvp ( QSQLiteBackend *  qsql_be) [static]

only call once per book

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

{
    struct QsqlBuilder qb;
    QofBackend *be;
    gint sq_code;

    g_return_if_fail (qsql_be);
    sq_code = SQLITE_OK;
    be = (QofBackend *) qsql_be;
    qb.sql_str =
        g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE);
    sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table,
            &qb, &qsql_be->err);
    /* catch older files without a sqlite_kvp table */
    if (sq_code == SQLITE_ERROR)
    {
        g_free (qb.sql_str);
        qb.sql_str =
            g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
            QSQL_KVP_TABLE, "kvp_id int primary key not null",
            "guid char(32)", "path mediumtext", "type mediumtext",
            "value text", END_DB_VERSION);
        PINFO (" creating kvp table. sql=%s", qb.sql_str);
        if (sqlite_exec (qsql_be->sqliteh, qb.sql_str,
            record_foreach, &qb, &qsql_be->err) != SQLITE_OK)
        {
            qsql_be->error = TRUE;
            PERR (" unable to create kvp table:%s", qsql_be->err);
        }
    }
    else if (sq_code != SQLITE_OK)
    {
        qof_error_set_be (be, qsql_be->err_create);
        qsql_be->error = TRUE;
        PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code);
    }
    g_free (qb.sql_str);
}
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 417 of file qof-sqlite.c.

{
    QSQLiteBackend *qsql_be;
    struct QsqlBuilder *qb;
    const QofParam *param;
    QofInstance *inst;
    QofEntity *ent;
    gint i;

    g_return_val_if_fail (builder, QSQL_ERROR);
    qb = (struct QsqlBuilder *) builder;
    qsql_be = qb->qsql_be;
    qof_event_suspend ();
    inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book);
    ent = &inst->entity;
    for (i = 0; i < col_num; i++)
    {
        /* get param and set as string */
        param = qof_class_get_parameter (qb->e_type, columnNames[i]);
        if (!param)
            continue;
        /* set the inst->param entry */
        inst->param = param;
        if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID))
        {
            GUID *guid;
            guid = guid_malloc ();
            if (!string_to_guid (strings[i], guid))
            {
                DEBUG (" set guid failed:%s", strings[i]);
                return QSQL_ERROR;
            }
            qof_entity_set_guid (ent, guid);
        }
        if (strings[i])
            qof_util_param_set_string (ent, param, strings[i]);
    }
    qof_event_resume ();
    return SQLITE_OK;
}
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.

{
    gchar *tail;
    gint64 cm_i64;
    gdouble cm_double;
    QofNumeric cm_numeric;
    GUID *cm_guid;

    switch (type)
    {
    case KVP_TYPE_GINT64:
        {
            errno = 0;
            cm_i64 = strtoll (content, &tail, 0);
            if (errno == 0)
            {
                return kvp_value_new_gint64 (cm_i64);
            }
            break;
        }
    case KVP_TYPE_DOUBLE:
        {
            errno = 0;
            cm_double = strtod (content, &tail);
            if (errno == 0)
                return kvp_value_new_double (cm_double);
            break;
        }
    case KVP_TYPE_NUMERIC:
        {
            qof_numeric_from_string (content, &cm_numeric);
            return kvp_value_new_numeric (cm_numeric);
            break;
        }
    case KVP_TYPE_STRING:
        {
            return kvp_value_new_string (content);
            break;
        }
    case KVP_TYPE_GUID:
        {
            cm_guid = g_new0 (GUID, 1);
            if (TRUE == string_to_guid (content, cm_guid))
                return kvp_value_new_guid (cm_guid);
            break;
        }
    case KVP_TYPE_TIME:
        {
            QofDate *qd;
            QofTime *qt;
            KvpValue *retval;

            qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC);
            if (qd)
            {
                qt = qof_date_to_qtime (qd);
                retval = kvp_value_new_time (qt);
                qof_date_free (qd);
                qof_time_free (qt);
                return retval;
            }
            else
                PERR (" failed to parse date");
        }
    case KVP_TYPE_BOOLEAN:
        {
            gboolean val;
            val = qof_util_bool_to_int (content);
            return kvp_value_new_boolean (val);
        }
    default:
        break;
    }
    return NULL;
}