From b2d599baa6719d9475976a36637690b50607cf9d Mon Sep 17 00:00:00 2001
From: Mateusz Polrola <mateuszx.potrola@intel.com>
Date: Tue, 22 Apr 2014 16:44:25 +0200
Subject: [PATCH] Added sqlite database changelog

---
 addressbook/backends/file/e-book-backend-file.c    |   4 +-
 addressbook/libedata-book/Makefile.am              |   3 +
 .../libedata-book/e-book-sqlite-changelog.c        |  97 +++++++
 .../libedata-book/e-book-sqlite-changelog.h        | 109 ++++++++
 addressbook/libedata-book/e-book-sqlite.c          | 280 ++++++++++++++++++++-
 addressbook/libedata-book/e-book-sqlite.h          |  12 +-
 addressbook/libedata-book/libedata-book.h          |   1 +
 configure.ac                                       |   3 +
 tests/libedata-book/data-test-utils.c              |   2 +
 9 files changed, 493 insertions(+), 18 deletions(-)
 create mode 100644 addressbook/libedata-book/e-book-sqlite-changelog.c
 create mode 100644 addressbook/libedata-book/e-book-sqlite-changelog.h

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index d33ef8b..228a7fc 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -1836,7 +1836,7 @@ book_backend_file_initable_init (GInitable *initable,
 	/* The old BDB exists, lets migrate that to sqlite right away. */
 	if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
 		priv->sqlitedb = e_book_sqlite_new_full (
-			fullpath, setup_extension,
+			fullpath, source, setup_extension,
 			NULL,
 			book_backend_file_vcard_changed,
 			initable, NULL, cancellable, error);
@@ -1881,7 +1881,7 @@ book_backend_file_initable_init (GInitable *initable,
 
 		/* Create the sqlitedb. */
 		priv->sqlitedb = e_book_sqlite_new_full (
-			fullpath, setup_extension,
+			fullpath, source, setup_extension,
 			NULL,
 			book_backend_file_vcard_changed,
 			initable, NULL, cancellable, error);
diff --git a/addressbook/libedata-book/Makefile.am b/addressbook/libedata-book/Makefile.am
index 791ec26..633c633 100644
--- a/addressbook/libedata-book/Makefile.am
+++ b/addressbook/libedata-book/Makefile.am
@@ -6,6 +6,7 @@ libedata_book_1_2_la_CPPFLAGS = \
 	-DLIBEDATA_BOOK_COMPILATION \
 	-DG_LOG_DOMAIN=\"libedata-book\" \
 	-DBACKENDDIR=\"$(ebook_backenddir)\" \
+	-DSQLITEMODULEDIR=\"$(sqlite_moduledir)\" \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/addressbook \
 	-I$(top_srcdir)/addressbook/libegdbus \
@@ -36,6 +37,7 @@ libedata_book_1_2_la_SOURCES = \
 	e-data-book-direct.c \
 	e-data-book-factory.c \
 	e-data-book-view.c \
+	e-book-sqlite-changelog.c \
 	ximian-vcard.h
 
 libedata_book_1_2_la_LIBADD = \
@@ -72,6 +74,7 @@ libedata_bookinclude_HEADERS = \
 	e-book-backend-sqlitedb.h \
 	e-book-backend-db-cache.h \
 	e-book-sqlite.h \
+	e-book-sqlite-changelog.h \
 	$(NULL)
 
 factorydir = $(libexecdir)
diff --git a/addressbook/libedata-book/e-book-sqlite-changelog.c b/addressbook/libedata-book/e-book-sqlite-changelog.c
new file mode 100644
index 0000000..02325fc
--- /dev/null
+++ b/addressbook/libedata-book/e-book-sqlite-changelog.c
@@ -0,0 +1,97 @@
+/*
+ * e-backend-sqlite-changelog.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <addressbook/libedata-book/libedata-book.h>
+#include <config.h>
+
+G_DEFINE_ABSTRACT_TYPE (EBookSqliteChangelog, e_book_sqlite_changelog, E_TYPE_EXTENSION)
+
+gboolean
+e_book_sqlite_changelog_is_enabled (EBookSqliteChangelog *changelog)
+{
+	g_return_val_if_fail (E_IS_BOOK_SQLITE_CHANGELOG (changelog), FALSE);
+
+	return (* E_BOOK_SQLITE_CHANGELOG_GET_CLASS (changelog)->is_enabled) (changelog);
+}
+
+gboolean
+e_book_sqlite_changelog_setup (EBookSqliteChangelog *changelog,
+			       gpointer backend,
+			       gpointer context,
+			       GError **error)
+{
+	g_return_val_if_fail (E_IS_BOOK_SQLITE_CHANGELOG (changelog), FALSE);
+	g_return_val_if_fail (E_IS_BOOK_SQLITE (backend), FALSE);
+	
+	return (* E_BOOK_SQLITE_CHANGELOG_GET_CLASS (changelog)->setup) (changelog, backend, context, error);
+}
+
+gboolean
+e_book_sqlite_changelog_before_insert_contact (EBookSqliteChangelog *changelog, 
+					       EContact *contact, 
+					       const gchar *extra,
+					       EContact *old_contact,
+					       const gchar *old_extra,
+					       gboolean replace,
+					       GCancellable *cancellable,
+					       GError **error)
+{	
+	g_return_val_if_fail (E_IS_BOOK_SQLITE_CHANGELOG (changelog), FALSE);
+	g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
+
+	return (* E_BOOK_SQLITE_CHANGELOG_GET_CLASS (changelog)->before_insert_contact) 
+								(changelog,
+								 contact,
+								 extra,
+								 old_contact,
+								 old_extra,
+								 replace,
+								 cancellable,
+								 error);
+}
+
+gboolean
+e_book_sqlite_changelog_before_remove_contact (EBookSqliteChangelog *changelog,
+					       EContact *contact,
+					       const gchar *extra,
+					       GCancellable *cancellable,
+					       GError **error)
+{
+	g_return_val_if_fail (E_IS_BOOK_SQLITE_CHANGELOG (changelog), FALSE);
+
+	return (* E_BOOK_SQLITE_CHANGELOG_GET_CLASS (changelog)->before_remove_contact) 
+								(changelog,
+								 contact,
+								 extra,
+								 cancellable,
+								 error);
+}
+
+static void
+e_book_sqlite_changelog_class_init (EBookSqliteChangelogClass *class)
+{
+	EExtensionClass *extension_class;
+
+	extension_class = E_EXTENSION_CLASS (class);
+	extension_class->extensible_type = E_TYPE_BOOK_SQLITE;
+}
+
+static void
+e_book_sqlite_changelog_init (EBookSqliteChangelog *changelog)
+{
+}
diff --git a/addressbook/libedata-book/e-book-sqlite-changelog.h b/addressbook/libedata-book/e-book-sqlite-changelog.h
new file mode 100644
index 0000000..866e83c
--- /dev/null
+++ b/addressbook/libedata-book/e-book-sqlite-changelog.h
@@ -0,0 +1,109 @@
+/*
+ * e-book-sqlite-changelog.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__LIBEDATA_BOOK_H_INSIDE__) && !defined (LIBEDATA_BOOK_COMPILATION)
+#error "Only <libedata-book/libedata-book.h> should be included directly."
+#endif
+
+#ifndef E_BOOK_SQLITE_CHANGELOG_H
+#define E_BOOK_SQLITE_CHANGELOG_H
+
+#include <libebackend/libebackend.h>
+#include <libebook-contacts/libebook-contacts.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_SQLITE_CHANGELOG \
+	(e_book_sqlite_changelog_get_type ())
+#define E_BOOK_SQLITE_CHANGELOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_BOOK_SQLITE_CHANGELOG, EBookSqliteChangelog))
+#define E_BOOK_SQLITE_CHANGELOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_BOOK_SQLITE_CHANGELOG, EBookSqliteChangelogClass))
+#define E_IS_BOOK_SQLITE_CHANGELOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_BOOK_SQLITE_CHANGELOG))
+#define E_IS_BOOK_SQLITE_CHANGELOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_BOOK_SQLITE_CHANGELOG))
+#define E_BOOK_SQLITE_CHANGELOG_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_BOOK_SQLITE_CHANGELOG, EBookSqliteChangelogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookSqliteChangelog EBookSqliteChangelog;
+typedef struct _EBookSqliteChangelogClass EBookSqliteChangelogClass;
+typedef struct _EBookSqliteChangelogPrivate EBookSqliteChangelogPrivate;
+
+struct _EBookSqliteChangelog {
+	EExtension parent;
+	EBookSqliteChangelogPrivate *priv;
+};
+
+struct _EBookSqliteChangelogClass {
+	EExtensionClass parent_class;
+	
+	gboolean	 (*is_enabled) 			(EBookSqliteChangelog *changelog);
+	gboolean	 (*setup) 			(EBookSqliteChangelog *changelog, 
+		       				 	 gpointer backend, 
+		       				 	 gpointer context,
+							 GError **error);
+	gboolean	 (*before_insert_contact)	(EBookSqliteChangelog *changelog, 
+					   	 	 EContact *contact,
+							 const gchar *extra,
+							 EContact *old_contact,
+							 const gchar *old_extra,
+					   	 	 gboolean replace,
+							 GCancellable *cancellable,
+							 GError **error);
+	gboolean	 (*before_remove_contact) 	(EBookSqliteChangelog *changelog,
+							 EContact *contact,
+							 const gchar *extra,
+							 GCancellable *cancellable,
+							 GError **error);
+};
+
+GType		e_book_sqlite_changelog_get_type	(void) G_GNUC_CONST;
+
+gboolean	e_book_sqlite_changelog_is_enabled 	(EBookSqliteChangelog *changelog);
+
+gboolean	e_book_sqlite_changelog_setup 		(EBookSqliteChangelog *changelog, 
+					       		 gpointer backend,
+					       		 gpointer context,
+							 GError **error);
+
+gboolean	e_book_sqlite_changelog_before_insert_contact 
+							(EBookSqliteChangelog *changelog, 
+							 EContact *contact,
+							 const gchar *extra,
+							 EContact *old_contact,
+							 const gchar *old_extra,
+							 gboolean replace,
+							 GCancellable *cancellable,
+							 GError **error);
+
+gboolean	e_book_sqlite_changelog_before_remove_contact 
+							(EBookSqliteChangelog *changelog,
+							 EContact *contact,
+							 const gchar *extra,
+							 GCancellable *cancellable,
+							 GError **error);
+G_END_DECLS
+
+#endif /* E_BOOK_SQLITE_CHANGELOG_H */
diff --git a/addressbook/libedata-book/e-book-sqlite.c b/addressbook/libedata-book/e-book-sqlite.c
index a57089b..7bba8a1 100644
--- a/addressbook/libedata-book/e-book-sqlite.c
+++ b/addressbook/libedata-book/e-book-sqlite.c
@@ -36,6 +36,7 @@
 
 #include "e-book-backend-sexp.h"
 
+#include "e-book-sqlite-changelog.h"
 /******************************************************
  *                 Debugging Macros                   *
  ******************************************************
@@ -340,9 +341,13 @@ struct _EBookSqlitePrivate {
 	sqlite3_stmt   *replace_stmt;    /* Replace statement for main summary table */
 	GHashTable     *multi_deletes;   /* Delete statement for each auxiliary table */
 	GHashTable     *multi_inserts;   /* Insert statement for each auxiliary table */
+
+	ESource	       *source;
+	EBookSqliteChangelog *changelog;
 };
 
-G_DEFINE_TYPE (EBookSqlite, e_book_sqlite, G_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_CODE (EBookSqlite, e_book_sqlite, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
 G_DEFINE_QUARK (e-book-backend-sqlite-error-quark,
 		e_book_sqlite_error)
 
@@ -2691,8 +2696,37 @@ ebsql_init_locale (EBookSqlite *ebsql,
 	return success;
 }
 
+static gboolean
+ebsql_load_changelog_extension (EBookSqlite *ebsql, GError **error)
+{
+	GList *extensions = NULL;
+	GList *extension = NULL;
+	gboolean success = TRUE;
+
+	e_extensible_load_extensions (E_EXTENSIBLE (ebsql));
+
+	extensions = e_extensible_list_extensions (E_EXTENSIBLE (ebsql), 
+						   E_TYPE_BOOK_SQLITE_CHANGELOG);
+	
+	extension = g_list_first (extensions);
+
+	if (extension) {
+		ebsql->priv->changelog = g_object_ref (E_BOOK_SQLITE_CHANGELOG (extension->data));
+		
+		success = e_book_sqlite_changelog_setup (ebsql->priv->changelog,
+							 ebsql,
+							 (gpointer) ebsql->priv->db,
+							 error);
+	}
+
+	g_list_free (extensions);
+
+	return success;
+}
+
 static EBookSqlite *
 ebsql_new_internal (const gchar *path,
+		    ESource *source,
 		    EbSqlVCardCallback vcard_callback,
 		    EbSqlChangeCallback change_callback,
 		    gpointer user_data,
@@ -2731,6 +2765,7 @@ ebsql_new_internal (const gchar *path,
 	ebsql->priv->change_callback = change_callback;
 	ebsql->priv->user_data = user_data;
 	ebsql->priv->user_data_destroy = user_data_destroy;
+	ebsql->priv->source = source;
 
 	EBSQL_NOTE (REF_COUNTS, g_printerr ("EBookSqlite initially created\n"));
 
@@ -2846,6 +2881,9 @@ ebsql_new_internal (const gchar *path,
 					     already_exists, error);
 
 	if (success)
+		success = ebsql_load_changelog_extension (ebsql, error);
+
+	if (success)
 		success = ebsql_commit_transaction (ebsql, error);
 	else
 		/* The GError is already set. */
@@ -5998,6 +6036,7 @@ e_book_sqlite_finalize (GObject *object)
 	g_free (priv->path);
 	g_free (priv->locale);
 	g_free (priv->region_code);
+	g_object_unref (priv->changelog);
 
 	if (priv->collator)
 		e_collator_unref (priv->collator);
@@ -6020,7 +6059,7 @@ e_book_sqlite_finalize (GObject *object)
 	sqlite3_finalize (priv->insert_stmt);
 	sqlite3_finalize (priv->replace_stmt);
 	sqlite3_close (priv->db);
-
+	
 	EBSQL_NOTE (REF_COUNTS, g_printerr ("EBookSqlite finalized\n"));
 
 	/* Chain up to parent's finalize() method. */
@@ -6031,6 +6070,9 @@ static void
 e_book_sqlite_class_init (EBookSqliteClass *class)
 {
 	GObjectClass *object_class;
+	GList *modules_list;
+	gchar *modules_directory = g_strdup (SQLITEMODULEDIR);
+	const gchar *modules_directory_env;
 
 	g_type_class_add_private (class, sizeof (EBookSqlitePrivate));
 
@@ -6040,6 +6082,16 @@ e_book_sqlite_class_init (EBookSqliteClass *class)
 
 	/* Parse the EBSQL_DEBUG environment variable */
 	ebsql_init_debug ();
+
+	modules_directory_env = g_getenv (EDS_SQLITE_MODULES);
+	if (modules_directory_env &&
+	    g_file_test (modules_directory_env, G_FILE_TEST_IS_DIR))
+		modules_directory = g_strdup (modules_directory_env);
+
+	modules_list = e_module_load_all_in_directory (modules_directory);
+	g_list_free_full (modules_list, (GDestroyNotify) g_type_module_unuse);
+
+	g_free (modules_directory);
 }
 
 static void
@@ -6053,6 +6105,8 @@ e_book_sqlite_init (EBookSqlite *ebsql)
 	g_mutex_init (&ebsql->priv->updates_lock);
 
 	ebsql->priv->transliterator = e_transliterator_new ("Any-Latin");
+
+	ebsql->priv->changelog = NULL;
 }
 
 /**********************************************************
@@ -6060,6 +6114,7 @@ e_book_sqlite_init (EBookSqlite *ebsql)
  **********************************************************/
 static EBookSqlite *
 ebsql_new_default (const gchar *path,
+		   ESource *source,
 		   EbSqlVCardCallback vcard_callback,
 		   EbSqlChangeCallback change_callback,
 		   gpointer user_data,
@@ -6084,7 +6139,8 @@ ebsql_new_default (const gchar *path,
 		G_N_ELEMENTS (default_indexed_fields));
 
 	ebsql = ebsql_new_internal (
-		path, vcard_callback, change_callback,
+		path, source,
+		vcard_callback, change_callback,
 		user_data, user_data_destroy,
 		(SummaryField *) summary_fields->data,
 		summary_fields->len,
@@ -6129,7 +6185,7 @@ e_book_sqlite_new (const gchar *path,
 {
 	g_return_val_if_fail (path && path[0], NULL);
 
-	return ebsql_new_default (path, NULL, NULL, NULL, NULL, cancellable, error);
+	return ebsql_new_default (path, NULL, NULL, NULL, NULL, NULL, cancellable, error);
 }
 
 /**
@@ -6174,6 +6230,7 @@ e_book_sqlite_new (const gchar *path,
  **/
 EBookSqlite *
 e_book_sqlite_new_full (const gchar *path,
+			ESource *source,
 			ESourceBackendSummarySetup *setup,
 			EbSqlVCardCallback vcard_callback,
 			EbSqlChangeCallback change_callback,
@@ -6195,6 +6252,7 @@ e_book_sqlite_new_full (const gchar *path,
 
 	if (!setup)
 		return ebsql_new_default (path,
+					  source,
 					  vcard_callback,
 					  change_callback,
 					  user_data,
@@ -6212,6 +6270,7 @@ e_book_sqlite_new_full (const gchar *path,
 				   EBSQL_MAX_SUMMARY_FIELDS);
 
 		ebsql = ebsql_new_default (path,
+					   source,
 					   vcard_callback,
 					   change_callback,
 					   user_data,
@@ -6261,7 +6320,8 @@ e_book_sqlite_new_full (const gchar *path,
 		summary_fields, indexed_fields, index_types, n_indexed_fields);
 
 	ebsql = ebsql_new_internal (
-		path, vcard_callback, change_callback,
+		path, source,
+		vcard_callback, change_callback,
 		user_data, user_data_destroy, 
 		(SummaryField *) summary_fields->data,
 		summary_fields->len,
@@ -6409,6 +6469,21 @@ e_book_sqlite_ref_collator (EBookSqlite *ebsql)
 	return e_collator_ref (ebsql->priv->collator);
 }
 
+ESource *
+e_book_sqlite_get_source (EBookSqlite *ebsql)
+{
+	g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), NULL);
+	
+	return ebsql->priv->source;
+}
+
+EBookSqliteChangelog *
+e_book_sqlite_ref_changelog (EBookSqlite *ebsql)
+{
+	g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), NULL);
+	
+	return g_object_ref (ebsql->priv->changelog);
+}
 /**
  * e_book_sqlitedb_add_contact:
  * @ebsql: An #EBookSqlite
@@ -6448,6 +6523,24 @@ e_book_sqlite_add_contact (EBookSqlite *ebsql,
 	return e_book_sqlite_add_contacts (ebsql, &l, &el, replace, cancellable, error);
 }
 
+
+gboolean
+ebsql_get_contact_non_locking (EBookSqlite *ebsql,
+			       const gchar *uid,
+			       gboolean meta_contact,
+			       EContact **contact,
+			       GError **error);
+gboolean
+ebsql_get_vcard_non_locking (EBookSqlite *ebsql,
+			     const gchar *uid,
+			     gboolean meta_contact,
+			     gchar **ret_vcard,
+			     GError **error);
+gboolean
+ebsql_get_contact_extra_non_locking (EBookSqlite *ebsql,
+				     const gchar *uid,
+				     gchar **ret_extra,
+				     GError **error);
 /**
  * e_book_sqlite_new_contacts:
  * @ebsql: An #EBookSqlite
@@ -6500,6 +6593,40 @@ e_book_sqlite_add_contacts (EBookSqlite *ebsql,
 
 		if (ll)
 			extra_data = (const gchar *)ll->data;
+	
+		if (e_book_sqlite_changelog_is_enabled (ebsql->priv->changelog)) {
+			EContact *old_contact = NULL;
+			gchar *old_extra_data = NULL;
+			const gchar *contact_uid = NULL;
+			if (replace) {
+				contact_uid = e_contact_get (contact, E_CONTACT_UID);
+				success = ebsql_get_contact_non_locking (ebsql,	
+									 contact_uid,
+									 FALSE,
+									 &old_contact,
+									 error);
+				if(success)
+					success = ebsql_get_contact_extra_non_locking (ebsql,
+										       contact_uid,
+										       &old_extra_data,
+										       error);
+				if(!success)
+					break;
+			}
+			success = e_book_sqlite_changelog_before_insert_contact (ebsql->priv->changelog,
+									  	 contact,
+										 extra_data,
+										 old_contact,
+										 old_extra_data,
+										 replace,
+										 cancellable,
+										 error);
+			g_object_unref (old_contact);
+			g_free (old_extra_data);
+		
+			if(!success)
+				break;
+		}
 
 		success = ebsql_insert_contact (ebsql,
 						EBSQL_CHANGE_CONTACT_ADDED,
@@ -6606,16 +6733,47 @@ e_book_sqlite_remove_contacts (EBookSqlite *ebsql,
 		return FALSE;
 	}
 
+	if (e_book_sqlite_changelog_is_enabled (ebsql->priv->changelog)) {
+		GSList *l;
+		EContact *old_contact = NULL;
+		gchar *old_extra_data = NULL;
+		const gchar *contact_uid;
+		for (l = uids; success && l; l = l->next) {
+			contact_uid = (const gchar*) l->data;
+			success = ebsql_get_contact_non_locking (ebsql,	
+								 contact_uid,
+								 FALSE,
+								 &old_contact,
+								 error);
+			if(success)
+				success = ebsql_get_contact_extra_non_locking (ebsql,
+									       contact_uid,
+									       &old_extra_data,
+									       error);
+			if(success)
+				success = e_book_sqlite_changelog_before_remove_contact
+									 (ebsql->priv->changelog,
+									  old_contact,
+									  old_extra_data,
+									  cancellable,
+									  error);
+			g_object_unref (old_contact);
+			g_free (old_extra_data);
+		}
+	}
+
 	/* Delete data from the auxiliary tables first */
-	for (i = 0; success && i < ebsql->priv->n_summary_fields; i++) {
-		SummaryField *field = &(ebsql->priv->summary_fields[i]);
+	if (success) {
+		for (i = 0; success && i < ebsql->priv->n_summary_fields; i++) {
+			SummaryField *field = &(ebsql->priv->summary_fields[i]);
 
-		if (field->type != E_TYPE_CONTACT_ATTR_LIST)
-			continue;
+			if (field->type != E_TYPE_CONTACT_ATTR_LIST)
+				continue;
 
-		stmt = generate_delete_stmt (field->aux_table, uids);
-		success = ebsql_exec (ebsql, stmt, NULL, NULL, NULL, error);
-		g_free (stmt);
+			stmt = generate_delete_stmt (field->aux_table, uids);
+			success = ebsql_exec (ebsql, stmt, NULL, NULL, NULL, error);
+			g_free (stmt);
+		}
 	}
 
 	/* Now delete the entry from the main contacts */
@@ -6792,6 +6950,85 @@ e_book_sqlite_get_vcard (EBookSqlite *ebsql,
 	return success;
 }
 
+gboolean
+ebsql_get_vcard_non_locking (EBookSqlite *ebsql,
+		 	     const gchar *uid,
+		 	     gboolean meta_contact,	
+			     gchar **ret_vcard,
+			     GError **error)
+{
+	gboolean success = FALSE;
+	gchar *vcard = NULL;
+
+	g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
+	g_return_val_if_fail (uid != NULL, FALSE);
+	g_return_val_if_fail (ret_vcard != NULL && *ret_vcard == NULL, FALSE);
+
+	/* Try constructing contacts from only UID/REV first if that's requested */
+	if (meta_contact) {
+		GSList *vcards = NULL;
+
+		success = ebsql_exec_printf (
+			ebsql, "SELECT summary.uid, summary.Rev FROM %Q AS summary WHERE uid = %Q",
+			collect_lean_results_cb, &vcards, NULL, error,
+			ebsql->priv->folderid, uid);
+
+		if (vcards) {
+			EbSqlSearchData *search_data = (EbSqlSearchData *) vcards->data;
+
+			vcard = search_data->vcard;
+			search_data->vcard = NULL;
+
+			g_slist_free_full (vcards, (GDestroyNotify)e_book_sqlite_search_data_free);
+			vcards = NULL;
+		}
+
+	} else {
+		success = ebsql_exec_printf (
+			ebsql, "SELECT %s FROM %Q AS summary WHERE summary.uid = %Q",
+			get_string_cb, &vcard, NULL, error, 
+			EBSQL_VCARD_FRAGMENT (ebsql), ebsql->priv->folderid, uid);
+	}
+
+	*ret_vcard = vcard;
+
+	if (success && !vcard) {
+		EBSQL_SET_ERROR (error,
+				 E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND,
+				 _("Contact '%s' not found"), uid);
+		success = FALSE;
+	}
+
+	return success;
+}
+
+gboolean
+ebsql_get_contact_non_locking (EBookSqlite *ebsql,
+			       const gchar *uid,
+			       gboolean meta_contact,	
+			       EContact **contact,
+			       GError **error)
+{
+	gboolean success = FALSE;
+	gchar *vcard = NULL;
+
+	g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
+	g_return_val_if_fail (uid != NULL, FALSE);
+	g_return_val_if_fail (contact != NULL && *contact == NULL, FALSE);
+
+	success = ebsql_get_vcard_non_locking (ebsql,
+				   	       uid,
+				  	       meta_contact,	
+				  	       &vcard,
+				   	       error);
+
+	if (success && vcard) {
+		*contact = e_contact_new_from_vcard_with_uid (vcard, uid);
+		g_free (vcard);
+	}
+
+	return success;
+}
 /**
  * e_book_sqlite_set_contact_extra:
  * @ebsql: An #EBookSqlite
@@ -6862,6 +7099,25 @@ e_book_sqlite_get_contact_extra (EBookSqlite *ebsql,
 	return success;
 }
 
+gboolean
+ebsql_get_contact_extra_non_locking (EBookSqlite *ebsql,
+				     const gchar *uid,
+				     gchar **ret_extra,
+				     GError **error)
+{
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
+	g_return_val_if_fail (uid != NULL, FALSE);
+	g_return_val_if_fail (ret_extra != NULL && *ret_extra == NULL, FALSE);
+
+	success = ebsql_exec_printf (
+		ebsql, "SELECT bdata FROM %Q WHERE uid = %Q",
+		get_string_cb, ret_extra, NULL, error, 
+		ebsql->priv->folderid, uid);
+
+	return success;
+}
 /**
  * e_book_sqlite_search:
  * @ebsql: An #EBookSqlite
diff --git a/addressbook/libedata-book/e-book-sqlite.h b/addressbook/libedata-book/e-book-sqlite.h
index 64c8706..f05f173 100644
--- a/addressbook/libedata-book/e-book-sqlite.h
+++ b/addressbook/libedata-book/e-book-sqlite.h
@@ -26,9 +26,8 @@
 
 #ifndef E_BOOK_SQLITE_H
 #define E_BOOK_SQLITE_H
-
 #include <libebook-contacts/libebook-contacts.h>
-
+#include "e-book-sqlite-changelog.h"
 /* Standard GObject macros */
 #define E_TYPE_BOOK_SQLITE            (e_book_sqlite_get_type ())
 #define E_BOOK_SQLITE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_BOOK_SQLITE, EBookSqlite))
@@ -58,7 +57,7 @@
  * Since: 3.12
  **/
 #define E_BOOK_SQL_IS_POPULATED_KEY "eds-reserved-namespace-is-populated"
-
+#define EDS_SQLITE_MODULES "EDS_SQLITE_MODULES"
 G_BEGIN_DECLS
 
 typedef struct _EBookSqlite EBookSqlite;
@@ -287,6 +286,7 @@ EBookSqlite *e_book_sqlite_new		        (const gchar *path,
 						 GCancellable *cancellable,
 						 GError **error);
 EBookSqlite *e_book_sqlite_new_full             (const gchar *path,
+						 ESource *source,
 						 ESourceBackendSummarySetup *setup,
 						 EbSqlVCardCallback vcard_callback,
 						 EbSqlChangeCallback change_callback,
@@ -311,6 +311,11 @@ gboolean     e_book_sqlite_get_locale           (EBookSqlite *ebsql,
 
 ECollator   *e_book_sqlite_ref_collator         (EBookSqlite *ebsql);
 
+ESource	    *e_book_sqlite_get_source		(EBookSqlite *ebsql);
+
+EBookSqliteChangelog *
+	     e_book_sqlite_ref_changelog 	(EBookSqlite *ebsql);
+
 /* Adding / Removing / Searching contacts */
 gboolean     e_book_sqlite_add_contact          (EBookSqlite *ebsql,
 						 EContact *contact,
@@ -365,7 +370,6 @@ gboolean     e_book_sqlite_search_uids          (EBookSqlite *ebsql,
 						 GSList **ret_list,
 						 GCancellable *cancellable,
 						 GError **error);
-
 /* Key / Value convenience API */
 gboolean     e_book_sqlite_get_key_value	(EBookSqlite *ebsql,
 						 const gchar *key,
diff --git a/addressbook/libedata-book/libedata-book.h b/addressbook/libedata-book/libedata-book.h
index 8c52b60..e2376d0 100644
--- a/addressbook/libedata-book/libedata-book.h
+++ b/addressbook/libedata-book/libedata-book.h
@@ -33,6 +33,7 @@
 #include <libedata-book/e-book-backend-sync.h>
 #include <libedata-book/e-book-backend.h>
 #include <libedata-book/e-book-sqlite.h>
+#include <libedata-book/e-book-sqlite-changelog.h>
 #include <libedata-book/e-data-book-cursor.h>
 #include <libedata-book/e-data-book-cursor-sqlite.h>
 #include <libedata-book/e-data-book-direct.h>
diff --git a/configure.ac b/configure.ac
index b2778bb..d81c7da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1642,6 +1642,9 @@ AC_SUBST(moduledir)
 ebook_backenddir='${privlibdir}'/addressbook-backends
 AC_SUBST(ebook_backenddir)
 
+sqlite_moduledir='${privlibdir}'/sqlite-modules
+AC_SUBST(sqlite_moduledir)
+
 ecal_backenddir='${privlibdir}'/calendar-backends
 AC_SUBST(ecal_backenddir)
 
diff --git a/tests/libedata-book/data-test-utils.c b/tests/libedata-book/data-test-utils.c
index 92bd9a9..f656bec 100644
--- a/tests/libedata-book/data-test-utils.c
+++ b/tests/libedata-book/data-test-utils.c
@@ -199,6 +199,7 @@ e_sqlite_fixture_setup (EbSqlFixture  *fixture,
 
 	if (closure->without_vcards)
 		fixture->ebsql = e_book_sqlite_new_full (filename,
+							 NULL,
 							 setup,
 							 fetch_vcard_from_hash,
 							 contact_change_cb,
@@ -207,6 +208,7 @@ e_sqlite_fixture_setup (EbSqlFixture  *fixture,
 							 &error);
 	else
 		fixture->ebsql = e_book_sqlite_new_full (filename,
+							 NULL,
 							 setup,
 							 NULL,
 							 contact_change_cb,
-- 
1.7.11.7

