[evolution-data-server] EBookBackendSqliteDB, e-sqlite3-vfs.[ch]: Add sqlitedb cache for addressbook. It can be used as summ



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]