[evolution-data-server] EBookBackendSqliteDB, e-sqlite3-vfs.[ch]: Add sqlitedb cache for addressbook. It can be used as summ
- From: Chenthill Palanisamy <pchen src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] EBookBackendSqliteDB, e-sqlite3-vfs.[ch]: Add sqlitedb cache for addressbook. It can be used as summ
- Date: Mon, 13 Jun 2011 10:54:28 +0000 (UTC)
commit 1c45fab30784fa1e870620585d1550539d2c978a
Author: Chenthill Palanisamy <pchenthill novell com>
Date: Mon Jun 13 16:20:44 2011 +0530
EBookBackendSqliteDB, e-sqlite3-vfs.[ch]: Add sqlitedb cache for addressbook. It can
be used as summary+cache.
addressbook/libedata-book/Makefile.am | 12 +
.../libedata-book/e-book-backend-sqlitedb-test.c | 214 +++
.../libedata-book/e-book-backend-sqlitedb.c | 1687 ++++++++++++++++++++
.../libedata-book/e-book-backend-sqlitedb.h | 190 +++
configure.ac | 5 +
libebackend/Makefile.am | 4 +
libebackend/e-sqlite3-vfs.c | 338 ++++
libebackend/e-sqlite3-vfs.h | 26 +
8 files changed, 2476 insertions(+), 0 deletions(-)
---
diff --git a/addressbook/libedata-book/Makefile.am b/addressbook/libedata-book/Makefile.am
index 584151c..c102932 100644
--- a/addressbook/libedata-book/Makefile.am
+++ b/addressbook/libedata-book/Makefile.am
@@ -18,6 +18,7 @@ libedata_book_1_2_la_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/addressbook \
$(DB_CFLAGS) \
+ $(SQLITE3_CFLAGS) \
$(EVOLUTION_ADDRESSBOOK_CFLAGS)
libedata_book_1_2_la_SOURCES = \
@@ -26,6 +27,7 @@ libedata_book_1_2_la_SOURCES = \
e-book-backend-summary.c \
e-book-backend-cache.c \
e-book-backend-db-cache.c \
+ e-book-backend-sqlitedb.c \
e-book-backend-sync.c \
e-book-backend.c \
e-data-book-view.c \
@@ -39,6 +41,7 @@ libedata_book_1_2_la_LIBADD = \
$(top_builddir)/libedataserver/libedataserver-1.2.la \
$(top_builddir)/libebackend/libebackend-1.2.la \
$(DB_LIBS) \
+ $(SQLITE3_LIBS) \
$(EVOLUTION_ADDRESSBOOK_LIBS)
libedata_book_1_2_la_LDFLAGS = \
@@ -57,10 +60,19 @@ libedata_bookinclude_HEADERS = \
e-data-book-view.h \
e-data-book.h \
e-book-backend-cache.h \
+ e-book-backend-sqlitedb.h \
e-book-backend-db-cache.h
factorydir = $(libexecdir)
+noinst_PROGRAMS = e-book-backend-sqlitedb-test
+
+e_book_backend_sqlitedb_test_CPPFLAGS = \
+ $(libedata_book_1_2_la_CPPFLAGS)
+e_book_backend_sqlitedb_test_SOURCES = e-book-backend-sqlitedb-test.c
+e_book_backend_sqlitedb_test_LDADD = \
+ libedata-book-1.2.la
+
factory_PROGRAMS = e-addressbook-factory
e_addressbook_factory_CPPFLAGS = \
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb-test.c b/addressbook/libedata-book/e-book-backend-sqlitedb-test.c
new file mode 100644
index 0000000..bc67590
--- /dev/null
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb-test.c
@@ -0,0 +1,214 @@
+/*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-book-backend-sqlitedb.c
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * Authors: Chenthill Palanisamy <pchenthill novell com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <libebook/e-book-query.h>
+#include "e-book-backend-sqlitedb.h"
+
+static GMainLoop *main_loop;
+static gchar *cache_path;
+const gchar *op;
+GError *error;
+
+const gchar *email="test localhost";
+const gchar *folderid = "test_folder_id";
+const gchar *folder_name = "test_folder";
+const gchar *uid ="pas-id-4DCB9FF200000000";
+
+const gchar *vcard_str =
+"BEGIN:VCARD\n"
+"VERSION:3.0\n"
+"URL:test.com\n"
+"TITLE:\n"
+"ROLE:\n"
+"X-EVOLUTION-MANAGER:\n"
+"X-EVOLUTION-ASSISTANT:\n"
+"NICKNAME:\n"
+"X-EVOLUTION-SPOUSE:\n"
+"NOTE:\n"
+"FN:test\n"
+"N:;test;;;\n"
+"X-EVOLUTION-BLOG-URL:test.wordpress.com\n"
+"CALURI:\n"
+"FBURL:\n"
+"X-EVOLUTION-VIDEO-URL:\n"
+"X-MOZILLA-HTML:FALSE\n"
+"X-EVOLUTION-FILE-AS:test\n"
+"EMAIL;X-EVOLUTION-UI-SLOT=1;TYPE=WORK:test localhost com\n"
+"EMAIL;X-EVOLUTION-UI-SLOT=2;TYPE=HOME:test localhome com\n"
+"UID:pas-id-4DCB9FF200000000\n"
+"REV:2011-05-12T08:53:06Z\n"
+"END:VCARD";
+
+static void
+quit_tests (void)
+{
+
+ if (error != NULL) {
+ g_print ("Tests failed: %s - %s \n", op, error->message);
+ g_clear_error (&error);
+ }
+
+ g_main_loop_quit (main_loop);
+}
+
+static void
+add_contacts (EBookBackendSqliteDB *ebsdb)
+{
+ GSList *contacts = NULL;
+ EContact *con;
+
+ g_print ("Adding contact \n");
+ op = "add contact";
+
+ con = e_contact_new_from_vcard (vcard_str);
+ contacts = g_slist_append (contacts, con);
+ e_book_backend_sqlitedb_add_contacts (ebsdb, folderid, contacts, FALSE, &error);
+
+ g_object_unref (con);
+}
+
+static void
+search_db (EBookBackendSqliteDB *ebsdb, const gchar *type, const gchar *sexp)
+{
+ GSList *vcards;
+ EbSdbSearchData *s_data;
+
+ g_print ("%s - query: %s \n", type, sexp);
+ op = type;
+ vcards = e_book_backend_sqlitedb_search (ebsdb, folderid, sexp, NULL, &error);
+ if (error)
+ return;
+
+ s_data = vcards->data;
+ g_print ("Result: %s \n", s_data->vcard);
+ e_book_backend_sqlitedb_search_data_free (s_data);
+}
+
+static gboolean
+start_tests (gpointer data)
+{
+ EBookBackendSqliteDB *ebsdb;
+ gboolean populated = FALSE;
+ gchar *vcard_str = NULL, *sexp;
+ EBookQuery *q;
+ GSList *uids = NULL;
+ gboolean store_vcard = FALSE;
+
+ g_print ("Creating the sqlitedb \n");
+ op = "create sqlitedb";
+ ebsdb = e_book_backend_sqlitedb_new
+ (cache_path, email, folderid, folder_name,
+ store_vcard, &error);
+ if (error)
+ goto exit;
+
+ add_contacts (ebsdb);
+ if (error)
+ goto exit;
+
+ g_print ("Getting is_populated \n");
+ op = "set is_populated";
+ e_book_backend_sqlitedb_set_is_populated (ebsdb, folderid, TRUE, &error);
+ if (error)
+ goto exit;
+
+ g_print ("Setting is_populated \n");
+ op = "set is_populated";
+ populated = e_book_backend_sqlitedb_get_is_populated (ebsdb, folderid, &error);
+ if (error)
+ goto exit;
+ g_print ("Populated: %d \n", populated);
+
+ g_print ("Setting key value \n");
+ op = "set key/value";
+ e_book_backend_sqlitedb_set_key_value (ebsdb, folderid, "customkey", "stored", &error);
+ if (error)
+ goto exit;
+
+ g_print ("Get Vcard string \n");
+ op = "get vcard string";
+ vcard_str = e_book_backend_sqlitedb_get_vcard_string (ebsdb, folderid, uid, &error);
+ if (error)
+ goto exit;
+ g_print ("VCard: %s \n", vcard_str);
+ g_free (vcard_str);
+
+ q = e_book_query_field_test (E_CONTACT_FULL_NAME, E_BOOK_QUERY_CONTAINS, "test");
+ sexp = e_book_query_to_string (q);
+ search_db (ebsdb, "summary query", sexp);
+ e_book_query_unref (q);
+ g_free (sexp);
+ if (error)
+ goto exit;
+
+ if (store_vcard) {
+ q = e_book_query_any_field_contains ("word");
+ sexp = e_book_query_to_string (q);
+ search_db (ebsdb, "full_search query", sexp);
+ e_book_query_unref (q);
+ g_free (sexp);
+ if (error)
+ goto exit;
+ }
+
+ g_print ("Delete contact \n");
+ op = "delete contact";
+ uids = g_slist_append (uids, (gchar *) uid);
+ e_book_backend_sqlitedb_remove_contacts (ebsdb, folderid, uids, &error);
+ g_slist_free (uids);
+ if (error)
+ goto exit;
+
+ g_print ("Delete addressbook \n");
+ op = "delete addressbook";
+ e_book_backend_sqlitedb_delete_addressbook (ebsdb, folderid, &error);
+
+exit:
+ g_object_unref (ebsdb);
+ quit_tests ();
+ return FALSE;
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+ g_type_init ();
+ g_thread_init (NULL);
+
+ if (argc != 2) {
+ g_print ("Please enter a path to store the cache \n");
+ return -1;
+ }
+
+ cache_path = argv[1];
+
+ main_loop = g_main_loop_new (NULL, TRUE);
+ g_idle_add ((GSourceFunc) start_tests, NULL);
+ g_main_loop_run (main_loop);
+
+ /* terminate */
+ g_main_loop_unref (main_loop);
+
+ return 0;
+}
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb.c b/addressbook/libedata-book/e-book-backend-sqlitedb.c
new file mode 100644
index 0000000..d2da462
--- /dev/null
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb.c
@@ -0,0 +1,1687 @@
+/*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-book-backend-sqlitedb.c
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * Authors: Chenthill Palanisamy <pchenthill novell com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <sqlite3.h>
+
+#include "libedataserver/e-sexp.h"
+#include "libedata-book/e-book-backend-sexp.h"
+#include "libebackend/e-sqlite3-vfs.h"
+#include "e-book-backend-sqlitedb.h"
+
+#define d(x)
+
+#define DB_FILENAME "contacts.db"
+#define FOLDER_VERSION 1
+
+#define READER_LOCK(ebsdb) g_static_rw_lock_reader_lock (&ebsdb->priv->rwlock)
+#define READER_UNLOCK(ebsdb) g_static_rw_lock_reader_unlock (&ebsdb->priv->rwlock)
+#define WRITER_LOCK(ebssdb) g_static_rw_lock_writer_lock (&ebsdb->priv->rwlock)
+#define WRITER_UNLOCK(ebssdb) g_static_rw_lock_writer_unlock (&ebsdb->priv->rwlock)
+
+struct _EBookBackendSqliteDBPrivate {
+ sqlite3 *db;
+ gchar *path;
+ gchar *hash_key;
+
+ gboolean store_vcard;
+ GStaticRWLock rwlock;
+};
+
+G_DEFINE_TYPE (EBookBackendSqliteDB, e_book_backend_sqlitedb, G_TYPE_OBJECT)
+
+#define E_BOOK_SDB_ERROR \
+ (e_book_backend_sqlitedb_error_quark ())
+
+static GHashTable *db_connections = NULL;
+static GStaticMutex dbcon_lock = G_STATIC_MUTEX_INIT;
+
+static int
+store_data_to_vcard (gpointer ref, gint ncol, gchar **cols, gchar **name);
+
+static GQuark
+e_book_backend_sqlitedb_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0)) {
+ const gchar *string = "e-book-backend-sqlitedb-error-quark";
+ quark = g_quark_from_static_string (string);
+ }
+
+ return quark;
+}
+
+static void
+e_book_backend_sqlitedb_finalize (GObject *object)
+{
+ EBookBackendSqliteDBPrivate *priv;
+
+ priv = E_BOOK_BACKEND_SQLITEDB (object)->priv;
+
+ g_static_rw_lock_free (&priv->rwlock);
+
+ sqlite3_close (priv->db);
+ priv->db = NULL;
+
+ g_free (priv->path);
+ priv->path = NULL;
+
+ g_static_mutex_lock (&dbcon_lock);
+ if (db_connections != NULL) {
+ g_hash_table_remove (db_connections, priv->hash_key);
+
+ if (g_hash_table_size (db_connections) == 0) {
+ g_hash_table_destroy (db_connections);
+ db_connections = NULL;
+ }
+
+ g_free (priv->hash_key);
+ priv->hash_key = NULL;
+ }
+ g_static_mutex_unlock (&dbcon_lock);
+
+ g_free (priv);
+ priv = NULL;
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_book_backend_sqlitedb_parent_class)->finalize (object);
+}
+
+static void
+e_book_backend_sqlitedb_class_init (EBookBackendSqliteDBClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EBookBackendSqliteDBPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = e_book_backend_sqlitedb_finalize;
+}
+
+static void
+e_book_backend_sqlitedb_init (EBookBackendSqliteDB *ebsdb)
+{
+ ebsdb->priv = g_new0 (EBookBackendSqliteDBPrivate, 1) ;
+
+ ebsdb->priv->store_vcard = TRUE;
+ g_static_rw_lock_init (&ebsdb->priv->rwlock);
+}
+
+
+static void
+e_book_sqlitedb_match_func (sqlite3_context *ctx, gint nArgs, sqlite3_value **values)
+{
+ gboolean matches = FALSE;
+ const gchar *what, *where;
+
+ g_return_if_fail (ctx != NULL);
+ g_return_if_fail (nArgs == 2);
+ g_return_if_fail (values != NULL);
+
+ what = (const gchar *) sqlite3_value_text (values[0]);
+ where = (const gchar *) sqlite3_value_text (values[1]);
+
+ if (what && where && !*what) {
+ matches = TRUE;
+ } else if (what && where) {
+ gboolean word = TRUE;
+ gint i, j;
+
+ for (i = 0, j = 0; where[i] && !matches; i++) {
+ gchar c = where[i];
+
+ if (c == ' ') {
+ word = TRUE;
+ j = 0;
+ } else if (word && tolower (c) == tolower (what[j])) {
+ j++;
+ if (what[j] == 0 && (where[i + 1] == 0 || isspace (where[i + 1])))
+ matches = TRUE;
+ } else {
+ word = FALSE;
+ }
+ }
+ }
+
+ sqlite3_result_int (ctx, matches ? 1 : 0);
+}
+
+/**
+ * e_book_sql_exec
+ * @db:
+ * @stmt:
+ * @callback:
+ * @data:
+ * @error:
+ *
+ * Callers should hold the rw lock depending on read or write operation
+ * Returns:
+ **/
+static gint
+book_backend_sql_exec (sqlite3 *db,
+ const gchar *stmt,
+ gint (*callback)(void*,gint,gchar**,gchar**),
+ gpointer data,
+ GError **error)
+{
+ gchar *errmsg = NULL;
+ gint ret = -1;
+
+ ret = sqlite3_exec (db, stmt, callback, data, &errmsg);
+ while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
+ if (errmsg) {
+ sqlite3_free (errmsg);
+ errmsg = NULL;
+ }
+ ret = sqlite3_exec (db, stmt, NULL, NULL, &errmsg);
+ }
+
+ if (ret != SQLITE_OK) {
+ d(g_print ("Error in SQL EXEC statement: %s [%s].\n", stmt, errmsg));
+ g_set_error (
+ error, E_BOOK_SDB_ERROR,
+ 0, "%s", errmsg);
+ sqlite3_free (errmsg);
+ errmsg = NULL;
+ return -1;
+ }
+
+ if (errmsg) {
+ sqlite3_free (errmsg);
+ errmsg = NULL;
+ }
+
+ return 0;
+}
+
+static void
+book_backend_sqlitedb_start_transaction (EBookBackendSqliteDB *ebsdb, GError **error)
+{
+ book_backend_sql_exec (ebsdb->priv->db, "BEGIN", NULL, NULL, error);
+}
+
+static void
+book_backend_sqlitedb_end_transaction (EBookBackendSqliteDB *ebsdb, GError **error)
+{
+ if (!error || !*error)
+ book_backend_sql_exec (ebsdb->priv->db, "COMMIT", NULL, NULL, error);
+ else
+ book_backend_sql_exec (ebsdb->priv->db, "ROLLBACK", NULL, NULL, NULL);
+}
+
+static void
+create_folders_table (EBookBackendSqliteDB *ebsdb,
+ GError **error)
+{
+ GError *err = NULL;
+ /* sync_data points to syncronization data, it could be last_modified time
+ or a sequence number or some text depending on the backend.
+
+ parial_content says whether the contents are partially downloaded for
+ auto-completion or if it has the complete content.
+
+ Have not included a bdata here since the keys table should suffice any
+ additional need that arises.
+ */
+ const gchar *stmt = "CREATE TABLE IF NOT EXISTS folders"
+ "( folder_id TEXT PRIMARY KEY,"
+ " folder_name TEXT,"
+ " sync_data TEXT,"
+ " is_populated INTEGER,"
+ " partial_content INTEGER,"
+ " version INTEGER)";
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err)
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL , &err);
+
+
+ /* Create a child table to store key/value pairs for a folder */
+ if (!err) {
+ stmt = "CREATE TABLE IF NOT EXISTS keys"
+ "( key TEXT PRIMARY KEY, value TEXT,"
+ " folder_id TEXT REFERENCES folders)";
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ }
+
+ if (!err) {
+ stmt = "CREATE INDEX IF NOT EXISTS keysindex ON keys(folder_id)";
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return;
+}
+
+
+static gint
+folder_found_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ gboolean *found = ref;
+
+ *found = TRUE;
+
+ return 0;
+}
+
+static gboolean
+folder_exists (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gchar *stmt;
+ gboolean found = FALSE;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT folder_id FROM folders WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, folder_found_cb , &found, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return found;
+}
+
+static void
+add_folder_into_db (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *folder_name,
+ GError **error)
+{
+ gchar *stmt;
+ GError *err = NULL;
+
+ if (folder_exists (ebsdb, folderid, error))
+ return;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err) {
+ stmt = sqlite3_mprintf ("INSERT OR REPLACE INTO folders VALUES ( %Q, %Q, %Q, %d, %d, %d ) ",
+ folderid, folder_name, NULL, 0, 0, FOLDER_VERSION);
+
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return;
+}
+
+
+/* The column names match the fields used in book-backend-sexp */
+static gint
+create_contacts_table (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gint ret;
+ gchar *stmt, *tmp;
+ GError *err = NULL;
+
+ stmt = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS %Q"
+ "( uid TEXT PRIMARY KEY,"
+ " nickname TEXT, full_name TEXT,"
+ " given_name TEXT, family_name TEXT,"
+ " file_as TEXT,"
+ " email_1 TEXT, email_2 TEXT,"
+ " email_3 TEXT, email_4 TEXT,"
+ " partial_content INTEGER,"
+ " is_list INTEGER, list_show_addresses INTEGER,"
+ " wants_html INTEGER,"
+ " vcard TEXT, bdata TEXT)", folderid);
+
+ WRITER_LOCK (ebsdb);
+ ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL , &err);
+ sqlite3_free (stmt);
+
+
+ /* Create indexes on full_name and email_1 as autocompletion queries would mainly
+ rely on this. Assuming that the frequency of matching on these would be higher than
+ on the other fields like email_2, surname etc. email_1 should be the primary email */
+ if (!err) {
+ tmp = g_strdup_printf("FNINDEX-%s", folderid);
+ stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (full_name)", tmp, folderid);
+ ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ g_free (tmp);
+ sqlite3_free (stmt);
+ }
+
+ if (!err) {
+ tmp = g_strdup_printf("EMINDEX-%s", folderid);
+ stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (email_1)", tmp, folderid);
+ ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ g_free (tmp);
+ sqlite3_free (stmt);
+ }
+
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return ret;
+}
+
+static gboolean
+book_backend_sqlitedb_load (EBookBackendSqliteDB *ebsdb,
+ const gchar *filename,
+ GError **error)
+{
+ EBookBackendSqliteDBPrivate *priv;
+ gint ret;
+
+ priv = ebsdb->priv;
+
+ e_sqlite3_vfs_init ();
+
+ ret = sqlite3_open (filename, &priv->db);
+ if (ret) {
+ if (!priv->db) {
+ g_set_error (
+ error, E_BOOK_SDB_ERROR,
+ 0,
+ _("Insufficient memory"));
+ } else {
+ const gchar *errmsg;
+ errmsg = sqlite3_errmsg (priv->db);
+ d(g_print("Can't open database %s: %s\n", path, errmsg));
+ g_set_error (
+ error, E_BOOK_SDB_ERROR,
+ 0, "%s", errmsg);
+ sqlite3_close (priv->db);
+ }
+ return FALSE;
+ }
+
+ sqlite3_create_function (priv->db, "MATCH", 2, SQLITE_UTF8, NULL, e_book_sqlitedb_match_func, NULL, NULL);
+
+ WRITER_LOCK (ebsdb);
+
+ book_backend_sql_exec (priv->db, "ATTACH DATABASE ':memory:' AS mem", NULL, NULL, NULL);
+ book_backend_sql_exec (priv->db, "PRAGMA foreign_keys = ON", NULL, NULL, NULL);
+
+ WRITER_UNLOCK (ebsdb);
+
+ create_folders_table (ebsdb, error);
+
+ return TRUE;
+}
+
+/**
+ * e_book_backend_sqlitedb_new
+ * @path: location where the db would be created
+ * @emailid: email id of the user
+ * @folderid: folder id of the address-book
+ * @folder_name: name of the address-book
+ * @store_vcard: True if the vcard should be stored inside db, if FALSE only the summary fields would be stored inside db.
+ * @error:
+ *
+ * If the path for multiple addressbooks are same, the contacts from all addressbooks
+ * would be stored in same db in different tables.
+ *
+ * Returns:
+ **/
+EBookBackendSqliteDB *
+e_book_backend_sqlitedb_new (const gchar *path,
+ const gchar *emailid,
+ const gchar *folderid,
+ const gchar *folder_name,
+ gboolean store_vcard,
+ GError **error)
+{
+ EBookBackendSqliteDB *ebsdb;
+ gchar *hash_key, *filename;
+ GError *err = NULL;
+
+ g_static_mutex_lock (&dbcon_lock);
+
+ hash_key = g_strdup_printf ("%s %s", emailid, path);
+ if (db_connections != NULL) {
+ ebsdb = g_hash_table_lookup (db_connections, hash_key);
+
+ if (ebsdb) {
+ g_object_ref (ebsdb);
+ g_static_mutex_unlock (&dbcon_lock);
+ g_free (hash_key);
+ goto exit;
+ }
+ }
+
+ ebsdb = g_object_new (E_TYPE_BOOK_BACKEND_SQLITEDB, NULL);
+ ebsdb->priv->path = g_strdup (path);
+ ebsdb->priv->store_vcard = store_vcard;
+ if (g_mkdir_with_parents (path, 0777) < 0) {
+ g_set_error (error, E_BOOK_SDB_ERROR,
+ 0, "Can not make parent directory: errno %d", errno);
+ return NULL;
+ }
+ filename = g_build_filename (path, DB_FILENAME, NULL);
+
+ book_backend_sqlitedb_load (ebsdb, filename, &err);
+ g_free (filename);
+
+ if (db_connections == NULL)
+ db_connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ g_hash_table_insert (db_connections, hash_key, ebsdb);
+ ebsdb->priv->hash_key = g_strdup (hash_key);
+
+ g_static_mutex_unlock (&dbcon_lock);
+
+exit:
+ if (!err)
+ add_folder_into_db (ebsdb, folderid, folder_name, &err);
+ if (!err)
+ create_contacts_table (ebsdb, folderid, &err);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return ebsdb;
+}
+
+/* Add Contact */
+static gchar *
+insert_stmt_from_contact (EContact *contact,
+ gboolean partial_content,
+ const gchar *folderid,
+ gboolean store_vcard)
+{
+ gchar *stmt = NULL;
+ gchar *id, *nickname, *full_name;
+ gchar *given_name, *surname, *file_as;
+ gchar *email_1, *email_2, *email_3, *email_4;
+ gchar *vcard_str = NULL;
+ gint wants_html, is_list, list_show_addresses;
+
+ id = e_contact_get (contact, E_CONTACT_UID);
+ nickname = e_contact_get (contact, E_CONTACT_NICKNAME);
+ full_name = e_contact_get (contact, E_CONTACT_FULL_NAME);
+ given_name = e_contact_get (contact, E_CONTACT_GIVEN_NAME);
+ surname = e_contact_get (contact, E_CONTACT_FAMILY_NAME);
+ file_as = e_contact_get (contact, E_CONTACT_FILE_AS);
+ email_1 = e_contact_get (contact, E_CONTACT_EMAIL_1);
+ email_2 = e_contact_get (contact, E_CONTACT_EMAIL_2);
+ email_3 = e_contact_get (contact, E_CONTACT_EMAIL_3);
+ email_4 = e_contact_get (contact, E_CONTACT_EMAIL_4);
+ is_list = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST));
+ wants_html = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_WANTS_HTML));
+ list_show_addresses = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_LIST_SHOW_ADDRESSES));
+
+ if (store_vcard)
+ vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+
+ stmt = sqlite3_mprintf ("INSERT or REPLACE INTO %Q VALUES (%Q, %Q, %Q, "
+ "%Q, %Q, %Q, %Q, %Q, %Q, %Q, %d, %d, %d, %d, %Q, %Q)", folderid, id, nickname,
+ full_name, given_name, surname, file_as, email_1,
+ email_2, email_3, email_4, partial_content, is_list, wants_html, list_show_addresses, vcard_str, NULL);
+
+ g_free (id);
+ g_free (nickname);
+ g_free (given_name);
+ g_free (surname);
+ g_free (file_as);
+ g_free (email_1);
+ g_free (email_2);
+ g_free (email_3);
+ g_free (email_4);
+ g_free (vcard_str);
+
+ return stmt;
+}
+
+/**
+ * e_book_backend_sqlitedb_add_contact
+ * @ebsdb:
+ * @folderid: folder id
+ * @contact: EContact to be added
+ * @partial_content: contact does not contain full information. Used when
+ * the backend cache's partial information for auto-completion.
+ * @error:
+ *
+ * This is a convenience wrapper for e_book_backend_sqlitedb_add_contacts,
+ * which is the preferred means to add multiple contacts when possible.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+e_book_backend_sqlitedb_add_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ EContact *contact,
+ gboolean partial_content,
+ GError **error)
+{
+ GSList l;
+ l.data = contact;
+ l.next = NULL;
+ return e_book_backend_sqlitedb_add_contacts (ebsdb, folderid, &l,
+ partial_content, error);
+}
+
+/**
+ * e_book_backend_sqlitedb_add_contacts
+ * @ebsdb:
+ * @folderid: folder id
+ * @contacts: list of EContacts
+ * @partial_content: contact does not contain full information. Used when
+ * the backend cache's partial information for auto-completion.
+ * @error:
+ *
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+e_book_backend_sqlitedb_add_contacts (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GSList *contacts,
+ gboolean partial_content,
+ GError **error)
+{
+ GSList *l;
+ GError *err = NULL;
+ gboolean ret = TRUE;
+ EBookBackendSqliteDBPrivate *priv;
+
+ priv = ebsdb->priv;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ for (l = contacts; !err && l != NULL; l = g_slist_next (l)) {
+ gchar *stmt;
+ EContact *contact = (EContact *) l->data;
+
+ stmt = insert_stmt_from_contact (contact, partial_content, folderid,
+ priv->store_vcard);
+ book_backend_sql_exec (priv->db, stmt, NULL, NULL, &err);
+
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return ret && !err;
+}
+
+gboolean
+e_book_backend_sqlitedb_remove_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error)
+{
+ GSList l;
+ l.data = (char*)uid; /* Won't modify it, I promise :) */
+ l.next = NULL;
+ return e_book_backend_sqlitedb_remove_contacts (ebsdb, folderid, &l,
+ error);
+}
+
+gboolean
+e_book_backend_sqlitedb_remove_contacts (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GSList *uids,
+ GError **error)
+{
+ GSList *l;
+ GError *err = NULL;
+ GString *str;
+ gchar *tmp;
+ EBookBackendSqliteDBPrivate *priv;
+
+ priv = ebsdb->priv;
+ str = g_string_new ("DELETE FROM ");
+
+ tmp = sqlite3_mprintf ("%Q WHERE uid IN (", folderid);
+ g_string_append (str, tmp);
+ sqlite3_free (tmp);
+
+ for (l = uids; l != NULL; l = g_slist_next (l)) {
+ gchar *uid = (gchar *) uids->data;
+
+ tmp = sqlite3_mprintf ("%Q", uid);
+ g_string_append_printf (str, " %s ,", tmp);
+ sqlite3_free (tmp);
+ }
+
+ /* remove the last comma */
+ g_string_truncate (str, str->len - 1);
+ g_string_append (str, ")");
+
+ WRITER_LOCK (ebsdb);
+
+ if (!err)
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err)
+ book_backend_sql_exec (priv->db, str->str, NULL, NULL, &err);
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ g_string_free (str, TRUE);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+struct _contact_info {
+ gboolean exists;
+ gboolean partial_content;
+};
+
+static gint
+contact_found_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ struct _contact_info *cinfo = ref;
+
+ cinfo->exists = TRUE;
+ cinfo->partial_content = cols [0] ? strtoul (cols [0], NULL, 10) : 0;
+
+ return 0;
+}
+
+gboolean
+e_book_backend_sqlitedb_has_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ gboolean *partial_content,
+ GError **error)
+{
+ GError *err = NULL;
+ gchar *stmt;
+ struct _contact_info cinfo;
+
+ cinfo.exists = FALSE;
+ cinfo.partial_content = FALSE;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT partial_content FROM %Q WHERE uid = %Q", folderid, uid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, contact_found_cb , &cinfo, &err);
+ sqlite3_free (stmt);
+
+ if (!err)
+ *partial_content = cinfo.partial_content;
+ else
+ g_propagate_error (error, err);
+
+ READER_UNLOCK (ebsdb);
+
+ return cinfo.exists;
+}
+
+static gint
+get_vcard_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ gchar **vcard_str = ref;
+
+ if (cols [0])
+ *vcard_str = g_strdup (cols [0]);
+
+ return 0;
+}
+
+EContact *
+e_book_backend_sqlitedb_get_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error)
+{
+ GError *err = NULL;
+ EContact *contact = NULL;
+ gchar *vcard = e_book_backend_sqlitedb_get_vcard_string (ebsdb, folderid, uid, &err);
+ if (!err) {
+ contact = e_contact_new_from_vcard(vcard);
+ g_free (vcard);
+ } else
+ g_propagate_error (error, err);
+
+ return contact;
+}
+
+gchar *
+e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error)
+{
+ gchar *stmt;
+ gchar *vcard_str = NULL;
+
+ READER_LOCK (ebsdb);
+
+ if (!ebsdb->priv->store_vcard) {
+ GSList *vcards = NULL;
+
+ stmt = sqlite3_mprintf ("SELECT uid, nickname, full_name, given_name, family_name, file_as, email_1, email_2, "
+ "email_3, is_list, list_show_addresses, wants_html FROM %Q WHERE uid = %Q", folderid, uid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, store_data_to_vcard, &vcards, error);
+ sqlite3_free (stmt);
+
+ if (vcards) {
+ EbSdbSearchData *s_data = (EbSdbSearchData *) vcards->data;
+
+ vcard_str = s_data->vcard;
+
+ g_free (s_data->uid);
+ g_free (s_data->bdata);
+ g_free (s_data);
+ g_slist_free (vcards);
+ vcards = NULL;
+ }
+ } else {
+ stmt = sqlite3_mprintf ("SELECT vcard FROM %Q WHERE uid = %Q", folderid, uid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, get_vcard_cb , &vcard_str, error);
+ sqlite3_free (stmt);
+ }
+
+ READER_UNLOCK (ebsdb);
+
+ return vcard_str;
+}
+
+static ESExpResult *
+func_check (struct _ESExp *f, gint argc, struct _ESExpResult **argv, gpointer data)
+{
+ ESExpResult *r;
+ gint truth = FALSE;
+
+ if (argc == 2
+ && argv[0]->type == ESEXP_RES_STRING
+ && argv[1]->type == ESEXP_RES_STRING) {
+ gchar *query_name = argv[0]->value.string;
+
+ if (!strcmp (query_name, "nickname") ||
+ !strcmp (query_name, "full_name") ||
+ !strcmp (query_name, "file_as") ||
+ !strcmp (query_name, "email")) {
+ truth = TRUE;
+ }
+ }
+
+ r = e_sexp_result_new (f, ESEXP_RES_BOOL);
+ r->value.boolean = truth;
+
+ return r;
+}
+
+/* 'builtin' functions */
+static const struct {
+ const gchar *name;
+ ESExpFunc *func;
+ gint type; /* set to 1 if a function can perform shortcut evaluation, or
+ doesn't execute everything, 0 otherwise */
+} check_symbols[] = {
+ { "contains", func_check, 0 },
+ { "is", func_check, 0 },
+ { "beginswith", func_check, 0 },
+ { "endswith", func_check, 0 },
+ { "exists", func_check, 0 }
+};
+
+static gboolean
+book_backend_sqlitedb_is_summary_query (const gchar *query)
+{
+ ESExp *sexp;
+ ESExpResult *r;
+ gboolean retval;
+ gint i;
+ gint esexp_error;
+
+ sexp = e_sexp_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (check_symbols); i++) {
+ if (check_symbols[i].type == 1) {
+ e_sexp_add_ifunction (sexp, 0, check_symbols[i].name,
+ (ESExpIFunc *)check_symbols[i].func, NULL);
+ } else {
+ e_sexp_add_function (sexp, 0, check_symbols[i].name,
+ check_symbols[i].func, NULL);
+ }
+ }
+
+ e_sexp_input_text (sexp, query, strlen (query));
+ esexp_error = e_sexp_parse (sexp);
+
+ if (esexp_error == -1) {
+ return FALSE;
+ }
+
+ r = e_sexp_eval (sexp);
+
+ retval = (r && r->type == ESEXP_RES_BOOL && r->value.boolean);
+
+ e_sexp_result_free (sexp, r);
+
+ e_sexp_unref (sexp);
+
+ return retval;
+}
+
+static ESExpResult *
+func_or (ESExp *f, gint argc, struct _ESExpTerm **argv, gpointer data)
+{
+ ESExpResult *r, *r1;
+ GString *string;
+ gint i;
+
+ string = g_string_new("( ");
+ for (i = 0; i < argc; i++) {
+ r1 = e_sexp_term_eval (f, argv[i]);
+
+ if (r1->type != ESEXP_RES_STRING) {
+ e_sexp_result_free (f, r1);
+ continue;
+ }
+ g_string_append_printf(string, "%s%s", r1->value.string, ((argc>1) && (i != argc-1)) ? " OR ":"");
+ e_sexp_result_free (f, r1);
+ }
+ g_string_append(string, " )");
+
+ r = e_sexp_result_new (f, ESEXP_RES_STRING);
+ r->value.string = string->str;
+ g_string_free (string, FALSE);
+ return r;
+}
+
+
+typedef enum {
+ MATCH_CONTAINS,
+ MATCH_IS,
+ MATCH_BEGINS_WITH,
+ MATCH_ENDS_WITH
+} match_type;
+
+static ESExpResult *
+convert_match_exp (struct _ESExp *f, gint argc, struct _ESExpResult **argv, gpointer data, match_type match)
+{
+ ESExpResult *r;
+ gchar *str=NULL;
+
+ /* are we inside a match-all? */
+ if (argc>1 && argv[0]->type == ESEXP_RES_STRING) {
+ const gchar *field;
+
+ /* only a subset of headers are supported .. */
+ field = argv[0]->value.string;
+
+ if (argv[1]->type == ESEXP_RES_STRING && argv[1]->value.string [0] != 0) {
+ gchar *value=NULL;
+
+ if (match == MATCH_CONTAINS) {
+ value = g_strdup_printf ("'%%%s%%'", argv[1]->value.string);
+ } else if (match == MATCH_ENDS_WITH) {
+ value = g_strdup_printf ("'%%%s'", argv[1]->value.string);
+ } else if (match == MATCH_BEGINS_WITH) {
+ value = g_strdup_printf ("'%s%%'", argv[1]->value.string);
+ } else if (match == MATCH_IS) {
+ value = g_strdup_printf ("'%%%s%%'", argv[1]->value.string);
+ }
+
+ if (!strcmp (field, "full_name")) {
+ gchar *full, *sur, *given, *nick;
+
+ full = g_strdup_printf("(full_name IS NOT NULL AND full_name LIKE %s)",value);
+ sur = g_strdup_printf("(family_name IS NOT NULL AND family_name LIKE %s)",value);
+ given = g_strdup_printf("(given_name IS NOT NULL AND given_name LIKE %s)",value);
+ nick = g_strdup_printf("(nickname IS NOT NULL AND nickname LIKE %s)",value);
+
+ str = g_strdup_printf (" %s OR %s OR %s OR %s ", full, sur, given, nick);
+
+ g_free (full);
+ g_free (sur);
+ g_free (given);
+ g_free (nick);
+ } else if (!strcmp (field, "email")) {
+ gint i;
+ GString *emails = g_string_new (NULL);
+
+ for (i = 1; i < 4; i++) {
+ g_string_append_printf (emails, "(email_%d IS NOT NULL AND email_%d LIKE %s)", i, i, value);
+ g_string_append (emails, " OR ");
+ }
+ g_string_append_printf (emails, "(email_4 IS NOT NULL AND email_4 LIKE %s)", value);
+
+ str = emails->str;
+ g_string_free (emails, FALSE);
+ } else
+ str = g_strdup_printf("(%s IS NOT NULL AND %s LIKE %s)", field, field, value);
+ g_free (value);
+ }
+ }
+
+ r = e_sexp_result_new (f, ESEXP_RES_STRING);
+ r->value.string = str;
+
+ return r;
+}
+
+static ESExpResult *
+func_contains (struct _ESExp *f, gint argc, struct _ESExpResult **argv, gpointer data)
+{
+ return convert_match_exp (f, argc, argv, data, MATCH_CONTAINS);
+}
+
+static ESExpResult *
+func_is (struct _ESExp *f, gint argc, struct _ESExpResult **argv, gpointer data)
+{
+ return convert_match_exp (f, argc, argv, data, MATCH_IS);
+}
+
+static ESExpResult *
+func_beginswith (struct _ESExp *f, gint argc, struct _ESExpResult **argv, gpointer data)
+{
+ return convert_match_exp (f, argc, argv, data, MATCH_BEGINS_WITH);
+}
+
+static ESExpResult *
+func_endswith (struct _ESExp *f, gint argc, struct _ESExpResult **argv, gpointer data)
+{
+ return convert_match_exp (f, argc, argv, data, MATCH_ENDS_WITH);
+}
+
+/* 'builtin' functions */
+static struct {
+ const gchar *name;
+ ESExpFunc *func;
+ guint immediate :1;
+} symbols[] = {
+ { "or", (ESExpFunc *) func_or, 1},
+
+ { "contains", func_contains, 0 },
+ { "is", func_is, 0 },
+ { "beginswith", func_beginswith, 0 },
+ { "endswith", func_endswith, 0 },
+};
+
+static char *
+sexp_to_sql_query (const gchar *query)
+{
+ ESExp *sexp;
+ ESExpResult *r;
+ gint i;
+ gchar *res;
+
+ sexp = e_sexp_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
+ if (symbols[i].immediate)
+ e_sexp_add_ifunction (sexp, 0, symbols[i].name,
+ (ESExpIFunc *) symbols[i].func, NULL);
+ else
+ e_sexp_add_function (sexp, 0, symbols[i].name,
+ symbols[i].func, NULL);
+ }
+
+ e_sexp_input_text (sexp, query, strlen (query));
+ e_sexp_parse (sexp);
+
+ r = e_sexp_eval (sexp);
+ if (!r)
+ return NULL;
+ if (r->type == ESEXP_RES_STRING) {
+ res = g_strdup (r->value.string);
+ } else
+ g_assert (0);
+
+ e_sexp_result_free (sexp, r);
+ e_sexp_unref (sexp);
+ return res;
+}
+
+static gint
+addto_vcard_list_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ GSList **vcard_data = ref;
+ EbSdbSearchData *s_data = g_new0 (EbSdbSearchData, 1);
+
+ if (cols [0])
+ s_data->uid = g_strdup (cols [0]);
+
+ if (cols [1])
+ s_data->vcard = g_strdup (cols [1]);
+
+ if (cols [2])
+ s_data->bdata = g_strdup (cols [1]);
+
+ *vcard_data = g_slist_prepend (*vcard_data, s_data);
+
+ return 0;
+}
+
+static gint
+addto_slist_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ GSList **uids = ref;
+
+ if (cols [0])
+ *uids = g_slist_prepend (*uids, g_strdup (cols [0]));
+
+ return 0;
+}
+
+static int
+store_data_to_vcard (gpointer ref, gint ncol, gchar **cols, gchar **name)
+{
+ GSList **vcard_data = ref;
+ EbSdbSearchData *search_data = g_new0 (EbSdbSearchData, 1);
+ EContact *contact = e_contact_new ();
+ gchar *vcard;
+ gint i;
+
+ /* parse through cols, this will be useful if the api starts supporting field restrictions */
+ for (i = 0; i < ncol; i++)
+ {
+ if (!name[i] || !cols[i])
+ continue;
+
+ if (!strcmp (name [i], "uid")) {
+ e_contact_set (contact, E_CONTACT_UID, cols [i]);
+
+ search_data->uid = g_strdup (cols [i]);
+ } else if (!strcmp (name [i], "nickname"))
+ e_contact_set (contact, E_CONTACT_NICKNAME, cols [i]);
+ else if (!strcmp (name [i], "full_name"))
+ e_contact_set (contact, E_CONTACT_FULL_NAME, cols [i]);
+ else if (!strcmp (name [i], "given_name"))
+ e_contact_set (contact, E_CONTACT_GIVEN_NAME, cols [i]);
+ else if (!strcmp (name [i], "family_name"))
+ e_contact_set (contact, E_CONTACT_FAMILY_NAME, cols [i]);
+ else if (!strcmp (name [i], "file_as"))
+ e_contact_set (contact, E_CONTACT_FILE_AS, cols [i]);
+ else if (!strcmp (name [i], "email_1"))
+ e_contact_set (contact, E_CONTACT_EMAIL_1, cols [i]);
+ else if (!strcmp (name [i], "email_2"))
+ e_contact_set (contact, E_CONTACT_EMAIL_2, cols [i]);
+ else if (!strcmp (name [i], "email_3"))
+ e_contact_set (contact, E_CONTACT_EMAIL_3, cols [i]);
+ else if (!strcmp (name [i], "is_list")) {
+ gint val = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
+ e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (val));
+ } else if (!strcmp (name [i], "list_show_addresses")) {
+ gint val = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
+ e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (val));
+ } else if (!strcmp (name [i], "wants_html")) {
+ gint val = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
+ e_contact_set (contact, E_CONTACT_WANTS_HTML, GINT_TO_POINTER (val));
+ } else if (!strcmp (name [i], "bdata")) {
+ search_data->bdata = g_strdup (cols [i]);
+ }
+
+ }
+
+ vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+ search_data->vcard = vcard;
+ *vcard_data = g_slist_prepend (*vcard_data, search_data);
+
+ g_object_unref (contact);
+ return 0;
+}
+
+static GSList *
+book_backend_sqlitedb_search_query (EBookBackendSqliteDB *ebsdb,
+ const gchar *sql,
+ const gchar *folderid,
+ GSList *fields_of_interest,
+ GError **error)
+{
+ GError *err = NULL;
+ GSList *vcard_data = NULL;
+ gchar *stmt;
+
+ READER_LOCK (ebsdb);
+
+ /* TODO enable return just the requested fields. */
+ if (!ebsdb->priv->store_vcard || fields_of_interest) {
+ stmt = sqlite3_mprintf ("SELECT uid, nickname, full_name, given_name, family_name, file_as, email_1, email_2, "
+ "email_3, is_list, list_show_addresses, wants_html FROM %Q WHERE %s", folderid, sql);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, store_data_to_vcard, &vcard_data, &err);
+ sqlite3_free (stmt);
+ } else {
+ stmt = sqlite3_mprintf ("SELECT uid vcard bdata FROM %Q WHERE %s", folderid, sql);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, addto_vcard_list_cb , &vcard_data, &err);
+ sqlite3_free (stmt);
+ }
+
+ READER_UNLOCK (ebsdb);
+
+ if (vcard_data)
+ vcard_data = g_slist_reverse (vcard_data);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return vcard_data;
+}
+
+static GSList *
+book_backend_sqlitedb_search_full (EBookBackendSqliteDB *ebsdb, const gchar *sexp, const gchar *folderid, gboolean return_uids, GError **error)
+{
+ GError *err = NULL;
+ GSList *r_list = NULL, *all = NULL, *l;
+ EBookBackendSExp *bsexp = NULL;
+ gchar *stmt;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT uid vcard bdata FROM %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, addto_vcard_list_cb , &all, &err);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ if (!err) {
+ bsexp = e_book_backend_sexp_new (sexp);
+
+ for (l = all; l != NULL; l = g_slist_next (l)) {
+ EbSdbSearchData *s_data = (EbSdbSearchData *) l->data;
+
+ if (e_book_backend_sexp_match_vcard (bsexp, s_data->vcard)) {
+ if (!return_uids)
+ r_list = g_slist_prepend (r_list, s_data);
+ else {
+ r_list = g_slist_prepend (r_list, g_strdup (s_data->uid));
+ e_book_backend_sqlitedb_search_data_free (s_data);
+ }
+ } else
+ e_book_backend_sqlitedb_search_data_free (s_data);
+ }
+
+ g_object_unref (bsexp);
+ }
+
+ g_slist_free (all);
+
+ return r_list;
+}
+
+/**
+ * e_book_backend_sqlitedb_search
+ * @ebsdb:
+ * @folderid:
+ * @sexp: search expression.
+ * &fields_of_interest: a #GList containing the names of fields to return, or NULL for all.
+ * At the moment if this is non-null, the vcard will be populated with summary fields, else it would return the
+ * whole vcard if its stored in the db. [not implemented fully]
+ * @error:
+ * Search on summary fields is always supported. Search expression containing
+ * any other field is supported only if backend chooses to store the vcard inside the db.
+ *
+ * Summary fields - uid, nickname, given_name, family_name, file_as email_1, email_2, email_3, email_4, is_list,
+ * list_show_addresses, wants_html
+ *
+ * Returns: List of EbSdbSearchData.
+ **/
+GSList *
+e_book_backend_sqlitedb_search (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *sexp,
+ GSList *fields_of_interest,
+ GError **error)
+{
+ GSList *search_contacts = NULL;
+
+ if (book_backend_sqlitedb_is_summary_query (sexp)) {
+ gchar *sql_query;
+
+ sql_query = sexp_to_sql_query (sexp);
+ search_contacts = book_backend_sqlitedb_search_query (ebsdb, sql_query, folderid, fields_of_interest, error);
+ g_free (sql_query);
+ } else if (ebsdb->priv->store_vcard)
+ search_contacts = book_backend_sqlitedb_search_full (ebsdb, sexp, folderid, FALSE, error);
+ else {
+ g_set_error (error, E_BOOK_SDB_ERROR,
+ 0, "Full search_contacts are not stored in cache. Hence only summary query is supported.");
+ }
+
+ return search_contacts;
+}
+
+GSList *
+e_book_backend_sqlitedb_search_uids (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *sexp,
+ GError **error)
+{
+ GSList *uids = NULL;
+
+ if (book_backend_sqlitedb_is_summary_query (sexp)) {
+ gchar *stmt;
+ gchar *sql_query = sexp_to_sql_query (sexp);
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT uid FROM %Q WHERE %s", folderid, sql_query);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ g_free (sql_query);
+ } else if (ebsdb->priv->store_vcard)
+ uids = book_backend_sqlitedb_search_full (ebsdb, sexp, folderid, TRUE, error);
+ else {
+ g_set_error (error, E_BOOK_SDB_ERROR,
+ 0, "Full vcards are not stored in cache. Hence only summary query is supported.");
+ }
+
+ return uids;
+}
+
+static gint
+get_bool_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ gboolean *ret = ref;
+
+ *ret = cols [0] ? strtoul (cols [0], NULL, 10) : 0;
+
+ return 0;
+}
+
+gboolean
+e_book_backend_sqlitedb_get_is_populated (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gchar *stmt;
+ gboolean ret = FALSE;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT is_populated FROM folders WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, get_bool_cb , &ret, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return ret;
+
+}
+
+
+gboolean
+e_book_backend_sqlitedb_set_is_populated (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ gboolean populated,
+ GError **error)
+{
+ gchar *stmt = NULL;
+ GError *err = NULL;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err) {
+ stmt = sqlite3_mprintf ("UPDATE folders SET is_populated = %d WHERE folder_id = %Q",
+ populated, folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+/**
+ * e_book_backend_sqlitedb_get_has_partial_content
+ * @ebsdb:
+ * @folderid:
+ * @error:
+ *
+ *
+ * Returns: TRUE if the vcards stored in the db were downloaded partially. It is to indicate
+ * the stored vcards does not contain the full data.
+ **/
+gboolean
+e_book_backend_sqlitedb_get_has_partial_content (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gchar *stmt;
+ gboolean ret = FALSE;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT partial_content FROM folders WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, get_bool_cb , &ret, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return ret;
+}
+
+gboolean
+e_book_backend_sqlitedb_set_has_partial_content (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ gboolean partial_content,
+ GError **error)
+{
+ gchar *stmt = NULL;
+ GError *err = NULL;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err) {
+ stmt = sqlite3_mprintf ("UPDATE folders SET partial_content = %d WHERE folder_id = %Q",
+ partial_content, folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+static int
+get_string_cb (gpointer ref, gint col, gchar **cols, gchar **name)
+{
+ gchar **ret = ref;
+
+ *ret = g_strdup (cols [0]);
+
+ return 0;
+}
+
+
+gchar *
+e_book_backend_sqlitedb_get_contact_bdata (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error)
+{
+ gchar *stmt, *ret = NULL;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT bdata FROM %Q WHERE uid = %Q", folderid, uid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb , &ret, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return ret;
+}
+
+gboolean
+e_book_backend_sqlitedb_set_contact_bdata (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ const gchar *value,
+ GError **error)
+{
+ gchar *stmt = NULL;
+ GError *err = NULL;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err) {
+ stmt = sqlite3_mprintf ("UPDATE %Q SET bdata = %Q WHERE uid = %Q", folderid,
+ value, uid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+gchar *
+e_book_backend_sqlitedb_get_sync_data (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gchar *stmt, *ret = NULL;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT sync_data FROM folders WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb , &ret, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return ret;
+}
+
+gboolean
+e_book_backend_sqlitedb_set_sync_data (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *sync_data,
+ GError **error)
+{
+ gchar *stmt = NULL;
+ GError *err = NULL;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err) {
+ stmt = sqlite3_mprintf ("UPDATE folders SET sync_data = %Q WHERE folder_id = %Q",
+ sync_data, folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+gchar *
+e_book_backend_sqlitedb_get_key_value (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *key,
+ GError **error)
+{
+ gchar *stmt, *ret = NULL;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT value FROM keys WHERE folder_id = %Q AND key = %Q",
+ folderid, key);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb , &ret, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return ret;
+}
+
+gboolean
+e_book_backend_sqlitedb_set_key_value (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *key,
+ const gchar *value,
+ GError **error)
+{
+ gchar *stmt = NULL;
+ GError *err = NULL;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ if (!err) {
+ stmt = sqlite3_mprintf ("INSERT or REPLACE INTO keys (key, value, folder_id) \
+ values (%Q, %Q, %Q)", key, value, folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+GSList *
+e_book_backend_sqlitedb_get_partially_cached_ids (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gchar *stmt;
+ GSList *uids = NULL;
+
+ READER_LOCK (ebsdb);
+
+ stmt = sqlite3_mprintf ("SELECT uid FROM %Q WHERE partial_content = 1",
+ folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
+ sqlite3_free (stmt);
+
+ READER_UNLOCK (ebsdb);
+
+ return uids;
+}
+
+gboolean
+e_book_backend_sqlitedb_delete_addressbook (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
+{
+ gchar *stmt;
+ GError *err = NULL;
+
+ WRITER_LOCK (ebsdb);
+ book_backend_sqlitedb_start_transaction (ebsdb, &err);
+
+ /* delete the contacts table */
+ if (!err) {
+ stmt = sqlite3_mprintf ("DROP TABLE %Q ", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ /* delete the key/value pairs corresponding to this table */
+ if (!err) {
+ stmt = sqlite3_mprintf ("DELETE FROM keys WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ /* delete the folder from the folders table */
+ if (!err) {
+ stmt = sqlite3_mprintf ("DELETE FROM folders WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ sqlite3_free (stmt);
+ }
+
+ book_backend_sqlitedb_end_transaction (ebsdb, &err);
+ WRITER_UNLOCK (ebsdb);
+
+ if (err)
+ g_propagate_error (error, err);
+
+ return !err;
+}
+
+void
+e_book_backend_sqlitedb_search_data_free (EbSdbSearchData *s_data)
+{
+ if (s_data) {
+ g_free (s_data->uid);
+ g_free (s_data->vcard);
+ g_free (s_data->bdata);
+ g_free (s_data);
+ }
+}
+
+gboolean
+e_book_backend_sqlitedb_remove (EBookBackendSqliteDB *ebsdb,
+ GError **error)
+{
+ EBookBackendSqliteDBPrivate *priv;
+ gchar *filename;
+ gint ret;
+
+ priv = ebsdb->priv;
+
+ WRITER_LOCK (ebsdb);
+
+ sqlite3_close (priv->db);
+ filename = g_build_filename (priv->path, DB_FILENAME, NULL);
+ ret = g_unlink (filename);
+
+ WRITER_UNLOCK (ebsdb);
+
+ g_free (filename);
+ if (ret == -1) {
+ g_set_error (error, E_BOOK_SDB_ERROR,
+ 0, "Unable to remove the db file: errno %d", errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb.h b/addressbook/libedata-book/e-book-backend-sqlitedb.h
new file mode 100644
index 0000000..49099b7
--- /dev/null
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb.h
@@ -0,0 +1,190 @@
+/*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-book-backend-sqlitedb.h
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * Authors: Chenthill Palanisamy <pchenthill novell com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef E_BOOK_BACKEND_SQLITEDB_H
+#define E_BOOK_BACKEND_SQLITEDB_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libebook/e-contact.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_BACKEND_SQLITEDB \
+ (e_book_backend_sqlitedb_get_type ())
+#define E_BOOK_BACKEND_SQLITEDB(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_BOOK_BACKEND_SQLITEDB, EBookBackendSqliteDB))
+#define E_BOOK_BACKEND_SQLITEDB_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_BOOK_BACKEND_SQLITEDB, EBookBackendSqliteDBClass))
+#define E_IS_BOOK_BACKEND_SQLITEDB(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_BOOK_BACKEND_SQLITEDB))
+#define E_IS_BOOK_BACKEND_SQLITEDB_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_BOOK_BACKEND_SQLITEDB))
+#define E_BOOK_BACKEND_SQLITEDB_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_BOOK_BACKEND_SQLITEDB, EBookBackendSqliteDBClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookBackendSqliteDB EBookBackendSqliteDB;
+typedef struct _EBookBackendSqliteDBClass EBookBackendSqliteDBClass;
+typedef struct _EBookBackendSqliteDBPrivate EBookBackendSqliteDBPrivate;
+
+struct _EBookBackendSqliteDB {
+ GObject parent;
+ EBookBackendSqliteDBPrivate *priv;
+};
+
+struct _EBookBackendSqliteDBClass {
+ GObjectClass parent_class;
+
+ /* virtual methods */
+};
+
+typedef struct {
+ gchar *vcard;
+ gchar *uid;
+ gchar *bdata;
+} EbSdbSearchData;
+
+GType e_book_backend_sqlitedb_get_type (void);
+
+EBookBackendSqliteDB *
+ e_book_backend_sqlitedb_new (const gchar *path,
+ const gchar *email_id,
+ const gchar *folderid,
+ const gchar *folder_name,
+ gboolean store_vcard,
+ GError **error);
+gboolean e_book_backend_sqlitedb_add_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ EContact *contact,
+ gboolean partial_content,
+ GError **error);
+gboolean e_book_backend_sqlitedb_add_contacts (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GSList *contacts,
+ gboolean partial_content,
+ GError **error);
+gboolean e_book_backend_sqlitedb_remove_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error);
+gboolean e_book_backend_sqlitedb_remove_contacts (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GSList *uids,
+ GError **error);
+gboolean e_book_backend_sqlitedb_has_contact (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ gboolean *partial_content,
+ GError **error);
+EContact * e_book_backend_sqlitedb_get_contact
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error);
+gchar * e_book_backend_sqlitedb_get_vcard_string
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error);
+GSList * e_book_backend_sqlitedb_search (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *sexp,
+ GSList *fields_of_interest,
+ GError **error);
+GSList * e_book_backend_sqlitedb_search_uids
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *sexp,
+ GError **error);
+gboolean e_book_backend_sqlitedb_get_is_populated
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error);
+
+gboolean e_book_backend_sqlitedb_set_is_populated
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ gboolean populated,
+ GError **error);
+gchar * e_book_backend_sqlitedb_get_sync_data
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error);
+gboolean e_book_backend_sqlitedb_set_sync_data
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *sync_data,
+ GError **error);
+gchar * e_book_backend_sqlitedb_get_key_value
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *key,
+ GError **error);
+gboolean e_book_backend_sqlitedb_set_key_value
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *key,
+ const gchar *value,
+ GError **error);
+
+gchar * e_book_backend_sqlitedb_get_contact_bdata
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ GError **error);
+gboolean e_book_backend_sqlitedb_set_contact_bdata
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ const gchar *uid,
+ const gchar *value,
+ GError **error);
+
+gboolean e_book_backend_sqlitedb_get_has_partial_content
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error);
+gboolean e_book_backend_sqlitedb_set_has_partial_content
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ gboolean partial_content,
+ GError **error);
+GSList * e_book_backend_sqlitedb_get_partially_cached_ids
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error);
+gboolean e_book_backend_sqlitedb_delete_addressbook
+ (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error);
+gboolean e_book_backend_sqlitedb_remove (EBookBackendSqliteDB *ebsdb,
+ GError **error);
+void e_book_backend_sqlitedb_search_data_free
+ (EbSdbSearchData *s_data);
+
+G_END_DECLS
+
+#endif /* E_BOOK_BACKEND_SQLITEDB_H */
diff --git a/configure.ac b/configure.ac
index ca21388..540f5e5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1189,6 +1189,11 @@ AC_DEFUN([EVO_SET_COMPILE_FLAGS], [
])
dnl ******************************
+dnl sqlite3 flags
+dnl ******************************
+PKG_CHECK_MODULES(SQLITE3, [sqlite3 >= sqlite_minimum_version])
+
+dnl ******************************
dnl libedataserver flags
dnl ******************************
E_DATA_SERVER_DEPS="gio-2.0 libxml-2.0 libsoup-2.4 gconf-2.0 $mozilla_nspr"
diff --git a/libebackend/Makefile.am b/libebackend/Makefile.am
index 424e6db..5228869 100644
--- a/libebackend/Makefile.am
+++ b/libebackend/Makefile.am
@@ -5,6 +5,7 @@ libebackend_1_2_la_CPPFLAGS = \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"e-data-server\" \
$(DB_CFLAGS) \
+ $(SQLITE3_CFLAGS) \
$(E_BACKEND_CFLAGS)
libebackend_1_2_la_SOURCES = \
@@ -12,11 +13,13 @@ libebackend_1_2_la_SOURCES = \
e-offline-listener.c \
e-dbhash.c \
e-db3-utils.c \
+ e-sqlite3-vfs.c \
e-file-cache.c
libebackend_1_2_la_LIBADD = \
$(top_builddir)/libedataserver/libedataserver-1.2.la \
$(E_BACKEND_LIBS) \
+ $(SQLITE3_LIBS) \
$(DB_LIBS)
libebackend_1_2_la_LDFLAGS = \
@@ -29,6 +32,7 @@ libebackendinclude_HEADERS = \
e-offline-listener.h \
e-db3-utils.h \
e-dbhash.h \
+ e-sqlite3-vfs.h \
e-file-cache.h
%-$(API_VERSION).pc: %.pc
diff --git a/libebackend/e-sqlite3-vfs.c b/libebackend/e-sqlite3-vfs.c
new file mode 100644
index 0000000..6d2868d
--- /dev/null
+++ b/libebackend/e-sqlite3-vfs.c
@@ -0,0 +1,338 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * Copyright (C) 1999-2011 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include <sqlite3.h>
+#include <glib.h>
+#include <config.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+#include <libedataserver/e-flag.h>
+
+#include "e-sqlite3-vfs.h"
+
+#define SYNC_TIMEOUT_SECONDS 5
+
+static sqlite3_vfs *old_vfs = NULL;
+static GThreadPool *sync_pool = NULL;
+
+typedef struct {
+ sqlite3_file parent;
+ sqlite3_file *old_vfs_file; /* pointer to old_vfs' file */
+ GStaticRecMutex sync_mutex;
+ guint timeout_id;
+ gint flags;
+} ESqlite3File;
+
+static gint
+call_old_file_Sync (ESqlite3File *cFile, gint flags)
+{
+ g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+ g_return_val_if_fail (cFile != NULL, SQLITE_ERROR);
+
+ g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR);
+ return cFile->old_vfs_file->pMethods->xSync (cFile->old_vfs_file, flags);
+}
+
+struct SyncRequestData
+{
+ ESqlite3File *cFile;
+ guint32 flags;
+ EFlag *sync_op; /* not NULL when waiting for a finish; will be freed by the caller */
+};
+
+static void
+sync_request_thread_cb (gpointer task_data, gpointer null_data)
+{
+ struct SyncRequestData *sync_data = task_data;
+ EFlag *sync_op;
+
+ g_return_if_fail (sync_data != NULL);
+ g_return_if_fail (sync_data->cFile != NULL);
+
+ call_old_file_Sync (sync_data->cFile, sync_data->flags);
+
+ sync_op = sync_data->sync_op;
+ g_free (sync_data);
+
+ if (sync_op)
+ e_flag_set (sync_op);
+}
+
+static void
+sync_push_request (ESqlite3File *cFile, gboolean wait_for_finish)
+{
+ struct SyncRequestData *data;
+ EFlag *sync_op = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (cFile != NULL);
+ g_return_if_fail (sync_pool != NULL);
+
+ g_static_rec_mutex_lock (&cFile->sync_mutex);
+
+ if (wait_for_finish)
+ sync_op = e_flag_new ();
+
+ data = g_new0 (struct SyncRequestData, 1);
+ data->cFile = cFile;
+ data->flags = cFile->flags;
+ data->sync_op = sync_op;
+
+ cFile->flags = 0;
+
+ g_static_rec_mutex_unlock (&cFile->sync_mutex);
+
+ g_thread_pool_push (sync_pool, data, &error);
+
+ if (error) {
+ g_warning ("%s: Failed to push to thread pool: %s\n", G_STRFUNC, error->message);
+ g_error_free (error);
+
+ if (sync_op)
+ e_flag_free (sync_op);
+
+ return;
+ }
+
+ if (sync_op) {
+ e_flag_wait (sync_op);
+ e_flag_free (sync_op);
+ }
+}
+
+static gboolean
+sync_push_request_timeout (ESqlite3File *cFile)
+{
+ g_static_rec_mutex_lock (&cFile->sync_mutex);
+
+ if (cFile->timeout_id != 0) {
+ sync_push_request (cFile, FALSE);
+ cFile->timeout_id = 0;
+ }
+
+ g_static_rec_mutex_unlock (&cFile->sync_mutex);
+
+ return FALSE;
+}
+
+#define def_subclassed(_nm, _params, _call) \
+static gint \
+e_sqlite3_file_ ## _nm _params \
+{ \
+ ESqlite3File *cFile; \
+ \
+ g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR); \
+ g_return_val_if_fail (pFile != NULL, SQLITE_ERROR); \
+ \
+ cFile = (ESqlite3File *) pFile; \
+ g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR); \
+ return cFile->old_vfs_file->pMethods->_nm _call; \
+}
+
+def_subclassed (xRead, (sqlite3_file *pFile, gpointer pBuf, gint iAmt, sqlite3_int64 iOfst), (cFile->old_vfs_file, pBuf, iAmt, iOfst))
+def_subclassed (xWrite, (sqlite3_file *pFile, gconstpointer pBuf, gint iAmt, sqlite3_int64 iOfst), (cFile->old_vfs_file, pBuf, iAmt, iOfst))
+def_subclassed (xTruncate, (sqlite3_file *pFile, sqlite3_int64 size), (cFile->old_vfs_file, size))
+def_subclassed (xFileSize, (sqlite3_file *pFile, sqlite3_int64 *pSize), (cFile->old_vfs_file, pSize))
+def_subclassed (xLock, (sqlite3_file *pFile, gint lockType), (cFile->old_vfs_file, lockType))
+def_subclassed (xUnlock, (sqlite3_file *pFile, gint lockType), (cFile->old_vfs_file, lockType))
+def_subclassed (xFileControl, (sqlite3_file *pFile, gint op, gpointer pArg), (cFile->old_vfs_file, op, pArg))
+def_subclassed (xSectorSize, (sqlite3_file *pFile), (cFile->old_vfs_file))
+def_subclassed (xDeviceCharacteristics, (sqlite3_file *pFile), (cFile->old_vfs_file))
+
+#undef def_subclassed
+
+static gint
+e_sqlite3_file_xCheckReservedLock (sqlite3_file *pFile, gint *pResOut)
+{
+ ESqlite3File *cFile;
+
+ g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+ g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
+
+ cFile = (ESqlite3File *) pFile;
+ g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR);
+
+ /* check version in runtime */
+ if (sqlite3_libversion_number () < 3006000)
+ return ((gint (*)(sqlite3_file *)) (cFile->old_vfs_file->pMethods->xCheckReservedLock)) (cFile->old_vfs_file);
+ else
+ return ((gint (*)(sqlite3_file *, gint *)) (cFile->old_vfs_file->pMethods->xCheckReservedLock)) (cFile->old_vfs_file, pResOut);
+}
+
+static gint
+e_sqlite3_file_xClose (sqlite3_file *pFile)
+{
+ ESqlite3File *cFile;
+ gint res;
+
+ g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+ g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
+
+ cFile = (ESqlite3File *) pFile;
+
+ g_static_rec_mutex_lock (&cFile->sync_mutex);
+
+ /* Cancel any pending sync requests. */
+ if (cFile->timeout_id > 0) {
+ g_source_remove (cFile->timeout_id);
+ cFile->timeout_id = 0;
+ }
+
+ g_static_rec_mutex_unlock (&cFile->sync_mutex);
+
+ /* Make the last sync. */
+ sync_push_request (cFile, TRUE);
+
+ if (cFile->old_vfs_file->pMethods)
+ res = cFile->old_vfs_file->pMethods->xClose (cFile->old_vfs_file);
+ else
+ res = SQLITE_OK;
+
+ g_free (cFile->old_vfs_file);
+ cFile->old_vfs_file = NULL;
+
+ g_static_rec_mutex_free (&cFile->sync_mutex);
+
+ return res;
+}
+
+static gint
+e_sqlite3_file_xSync (sqlite3_file *pFile, gint flags)
+{
+ ESqlite3File *cFile;
+
+ g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+ g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
+
+ cFile = (ESqlite3File *) pFile;
+
+ g_static_rec_mutex_lock (&cFile->sync_mutex);
+
+ /* If a sync request is already scheduled, accumulate flags. */
+ cFile->flags |= flags;
+
+ /* Cancel any pending sync requests. */
+ if (cFile->timeout_id > 0)
+ g_source_remove (cFile->timeout_id);
+
+ /* Wait SYNC_TIMEOUT_SECONDS before we actually sync. */
+ cFile->timeout_id = g_timeout_add_seconds (
+ SYNC_TIMEOUT_SECONDS, (GSourceFunc)
+ sync_push_request_timeout, cFile);
+
+ g_static_rec_mutex_unlock (&cFile->sync_mutex);
+
+ return SQLITE_OK;
+}
+
+static gint
+e_sqlite3_vfs_xOpen (sqlite3_vfs *pVfs, const gchar *zPath, sqlite3_file *pFile, gint flags, gint *pOutFlags)
+{
+ static GStaticRecMutex only_once_lock = G_STATIC_REC_MUTEX_INIT;
+ static sqlite3_io_methods io_methods = {0};
+ ESqlite3File *cFile;
+ gint res;
+
+ g_return_val_if_fail (old_vfs != NULL, -1);
+ g_return_val_if_fail (pFile != NULL, -1);
+
+ cFile = (ESqlite3File *)pFile;
+ cFile->old_vfs_file = g_malloc0 (old_vfs->szOsFile);
+
+ res = old_vfs->xOpen (old_vfs, zPath, cFile->old_vfs_file, flags, pOutFlags);
+ if (res != SQLITE_OK) {
+ g_free (cFile->old_vfs_file);
+ return res;
+ }
+
+ g_static_rec_mutex_init (&cFile->sync_mutex);
+
+ g_static_rec_mutex_lock (&only_once_lock);
+
+ if (!sync_pool)
+ sync_pool = g_thread_pool_new (sync_request_thread_cb, NULL, 2, FALSE, NULL);
+
+ /* cFile->old_vfs_file->pMethods is NULL when open failed for some reason,
+ thus do not initialize our structure when do not know the version */
+ if (io_methods.xClose == NULL && cFile->old_vfs_file->pMethods) {
+ /* initialize our subclass function only once */
+ io_methods.iVersion = cFile->old_vfs_file->pMethods->iVersion;
+
+ /* check version in compile time */
+ #if SQLITE_VERSION_NUMBER < 3006000
+ io_methods.xCheckReservedLock = (gint (*)(sqlite3_file *)) e_sqlite3_file_xCheckReservedLock;
+ #else
+ io_methods.xCheckReservedLock = e_sqlite3_file_xCheckReservedLock;
+ #endif
+
+ #define use_subclassed(x) io_methods.x = e_sqlite3_file_ ## x
+ use_subclassed (xClose);
+ use_subclassed (xRead);
+ use_subclassed (xWrite);
+ use_subclassed (xTruncate);
+ use_subclassed (xSync);
+ use_subclassed (xFileSize);
+ use_subclassed (xLock);
+ use_subclassed (xUnlock);
+ use_subclassed (xFileControl);
+ use_subclassed (xSectorSize);
+ use_subclassed (xDeviceCharacteristics);
+ #undef use_subclassed
+ }
+
+ g_static_rec_mutex_unlock (&only_once_lock);
+
+ cFile->parent.pMethods = &io_methods;
+
+ return res;
+}
+
+static gpointer
+init_sqlite_vfs (void)
+{
+ static sqlite3_vfs vfs = { 0 };
+
+ old_vfs = sqlite3_vfs_find (NULL);
+ g_return_val_if_fail (old_vfs != NULL, NULL);
+
+ memcpy (&vfs, old_vfs, sizeof (sqlite3_vfs));
+
+ vfs.szOsFile = sizeof (ESqlite3File);
+ vfs.zName = "e_sqlite3_vfs";
+ vfs.xOpen = e_sqlite3_vfs_xOpen;
+
+ sqlite3_vfs_register (&vfs, 1);
+
+ return NULL;
+}
+
+void
+e_sqlite3_vfs_init (void)
+{
+ static GOnce vfs_once = G_ONCE_INIT;
+
+ g_once (&vfs_once, (GThreadFunc) init_sqlite_vfs, NULL);
+
+ return;
+}
diff --git a/libebackend/e-sqlite3-vfs.h b/libebackend/e-sqlite3-vfs.h
new file mode 100644
index 0000000..22835ad
--- /dev/null
+++ b/libebackend/e-sqlite3-vfs.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * Copyright (C) 1999-2011 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef E_SQLITE3_VFS_H
+#define E_SQLITE3_VFS_H
+
+void e_sqlite3_vfs_init (void);
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]