[evolution-data-server/sqlite-refactor: 2/6] EBookBackendSqliteDB: Refactoring work in progress.



commit 3a0af29802b492335f0df244aa13b9d236361557
Author: Tristan Van Berkom <tristanvb openismus com>
Date:   Thu Nov 14 17:43:11 2013 +0900

    EBookBackendSqliteDB: Refactoring work in progress.

 .../libebook-contacts/e-book-contacts-types.h      |    6 +-
 .../libedata-book/e-book-backend-sqlitedb.c        | 5356 +++++++++++---------
 2 files changed, 2928 insertions(+), 2434 deletions(-)
---
diff --git a/addressbook/libebook-contacts/e-book-contacts-types.h 
b/addressbook/libebook-contacts/e-book-contacts-types.h
index 01a6dfa..ca6b952 100644
--- a/addressbook/libebook-contacts/e-book-contacts-types.h
+++ b/addressbook/libebook-contacts/e-book-contacts-types.h
@@ -123,16 +123,18 @@ typedef struct {
  * @E_BOOK_INDEX_PREFIX: An index suitable for searching contacts with a prefix pattern
  * @E_BOOK_INDEX_SUFFIX: An index suitable for searching contacts with a suffix pattern
  * @E_BOOK_INDEX_PHONE: An index suitable for searching contacts for phone numbers.
- * <note><para>that phone numbers must be convertible into FQTN according to E.164 to be
+ * <note><para>Phone numbers must be convertible into FQTN according to E.164 to be
  * stored in this index. The number "+9999999" for instance won't be stored because
  * the country calling code "+999" currently is not assigned.</para></note>
+ * @E_BOOK_INDEX_SORT_KEY: Indicates that a given #EContactField should be usable as a sort key.
  *
  * The type of index defined by e_source_backend_summary_setup_set_indexed_fields()
  */
 typedef enum {
        E_BOOK_INDEX_PREFIX = 0,
        E_BOOK_INDEX_SUFFIX,
-       E_BOOK_INDEX_PHONE
+       E_BOOK_INDEX_PHONE,
+       E_BOOK_INDEX_SORT_KEY
 } EBookIndexType;
 
 /**
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb.c 
b/addressbook/libedata-book/e-book-backend-sqlitedb.c
index fb21912..ef41b2e 100644
--- a/addressbook/libedata-book/e-book-backend-sqlitedb.c
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb.c
@@ -36,25 +36,39 @@
 
 #include "e-book-backend-sexp.h"
 
-#define E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_BOOK_BACKEND_SQLITEDB, EBookBackendSqliteDBPrivate))
+
+/* Some forward declarations */
+static sqlite3_stmt *book_backend_sqlitedb_prepare_insert (EBookBackendSqliteDB *ebsdb,
+                                                          const gchar *folderid,
+                                                          gboolean replace_existing,
+                                                          GError **error);
+
+static gboolean  book_backend_sqlitedb_insert_contact (EBookBackendSqliteDB *ebsdb,
+                                                      EContact *contact,
+                                                      const gchar *folderid,
+                                                      gboolean replace_existing,
+                                                      const gchar *default_region,
+                                                      GError **error);
+
+static gboolean book_backend_sqlitedb_set_locale_internal (EBookBackendSqliteDB *ebsdb,
+                                                          const gchar          *locale,
+                                                          GError              **error);
 
 #define d(x)
 
 #if d(1)+0
 #  define LOCK_MUTEX(mutex)                                    \
        G_STMT_START {                                          \
-               g_message ("%s: DB Locking ", G_STRFUNC);       \
+               g_printerr ("%s: DB Locking\n", G_STRFUNC);     \
                g_mutex_lock (mutex);                           \
-               g_message ("%s: DB Locked ", G_STRFUNC);        \
+               g_printerr ("%s: DB Locked\n", G_STRFUNC);      \
        } G_STMT_END
 
 #  define UNLOCK_MUTEX(mutex)                                  \
        G_STMT_START {                                          \
-               g_message ("%s: Unlocking ", G_STRFUNC);        \
+               g_printerr ("%s: Unlocking\n", G_STRFUNC);      \
                g_mutex_unlock (mutex);                         \
-               g_message ("%s: DB Unlocked ", G_STRFUNC);      \
+               g_printerr ("%s: DB Unlocked\n", G_STRFUNC);    \
        } G_STMT_END
 #else
 #  define LOCK_MUTEX(mutex)   g_mutex_lock (mutex)
@@ -62,21 +76,38 @@
 #endif
 
 #define DB_FILENAME "contacts.db"
-#define FOLDER_VERSION 7
+#define FOLDER_VERSION 8
 
-typedef enum {
-       INDEX_PREFIX = (1 << 0),
-       INDEX_SUFFIX = (1 << 1),
-       INDEX_PHONE  = (1 << 2)
-} IndexFlags;
+#define INDEX_FLAG(type)  (1 << E_BOOK_INDEX_##type)
+
+#define INSERT_MULTI_STMT_BYTES 128
+#define MAIN_TABLE_ENTRY_BYTES  32
+
+typedef gint (* EbSdbSqliteCallback) (gpointer ref,
+                                     gint     n_cols,
+                                     gchar  **cols,
+                                     gchar  **names);
 
 typedef struct {
        EContactField field;   /* The EContact field */
        GType         type;    /* The GType (only support string or gboolean) */
        const gchar  *dbname;  /* The key for this field in the sqlite3 table */
-       IndexFlags    index;   /* Whether this summary field should have an index in the SQLite DB */
+       gint          index;   /* Types of searches this field should support (see EBookIndexType) */
 } SummaryField;
 
+/* We have one of these per 'folderid' */
+typedef struct {
+
+       sqlite3_stmt   *insert_stmt;
+       sqlite3_stmt   *replace_stmt;
+
+       /* One delete & insert statement per 
+        * multivalued attribute in the summary
+        */
+       GHashTable     *multi_deletes;
+       GHashTable     *multi_inserts;
+} PreparedStatements;
+
 struct _EBookBackendSqliteDBPrivate {
        sqlite3 *db;
        gchar *path;
@@ -91,16 +122,41 @@ struct _EBookBackendSqliteDBPrivate {
        SummaryField   *summary_fields;
        gint            n_summary_fields;
        guint           have_attr_list : 1;
-       IndexFlags      attr_list_indexes;
+       gint            attr_list_indexes;
 
-       ECollator      *collator; /* The ECollator to create sort keys for all fields */
+       ECollator      *collator; /* The ECollator to create sort keys for any sortable fields */
        gchar          *locale;   /* The current locale */
+
+       GHashTable     *prepared_stmts;
 };
 
 G_DEFINE_TYPE (EBookBackendSqliteDB, e_book_backend_sqlitedb, G_TYPE_OBJECT)
+G_DEFINE_QUARK (e-book-backend-sqlitedb-error-quark,
+               e_book_backend_sqlitedb_error)
 
-static GHashTable *db_connections = NULL;
-static GMutex dbcon_lock;
+/* The ColumnInfo struct is used to constant data
+ * and dynamically allocated data, the 'type' and
+ * 'extra' members are however always constant.
+ */
+typedef struct {
+       gchar       *name;
+       const gchar *type;
+       const gchar *extra;
+       gchar       *index;
+} ColumnInfo;
+
+static ColumnInfo main_table_columns[] = {
+       { (gchar *) "folder_id",       "TEXT",      "PRIMARY KEY", NULL },
+       { (gchar *) "folder_name",     "TEXT",       NULL,         NULL },
+       { (gchar *) "sync_data",       "TEXT",       NULL,         NULL },
+       { (gchar *) "is_populated",    "INTEGER",   "DEFAULT 0",   NULL },
+       { (gchar *) "partial_content", "INTEGER",   "DEFAULT 0",   NULL },
+       { (gchar *) "version",         "INTEGER",    NULL,         NULL },
+       { (gchar *) "revision",        "TEXT",       NULL,         NULL },
+       { (gchar *) "multivalues",     "TEXT",       NULL,         NULL },
+       { (gchar *) "lc_collate",      "TEXT",       NULL,         NULL },
+       { (gchar *) "countrycode",     "VARCHAR(2)", NULL,         NULL },
+};
 
 static EContactField default_summary_fields[] = {
        E_CONTACT_UID,
@@ -121,211 +177,332 @@ static EContactField default_summary_fields[] = {
  */
 static EContactField default_indexed_fields[] = {
        E_CONTACT_FULL_NAME,
-       E_CONTACT_EMAIL
+       E_CONTACT_EMAIL,
+       E_CONTACT_FULL_NAME,
+       E_CONTACT_GIVEN_NAME,
+       E_CONTACT_FAMILY_NAME
 };
 
 static EBookIndexType default_index_types[] = {
        E_BOOK_INDEX_PREFIX,
-       E_BOOK_INDEX_PREFIX
+       E_BOOK_INDEX_PREFIX,
+       E_BOOK_INDEX_SORT_KEY,
+       E_BOOK_INDEX_SORT_KEY,
+       E_BOOK_INDEX_SORT_KEY
 };
 
+/******************************************************
+ *      Sharing EBookBackendSqliteDB instances        *
+ ******************************************************/
+static GHashTable *db_connections = NULL;
+static GMutex dbcon_lock;
+
+static EBookBackendSqliteDB *
+book_backend_sqlitedb_ref_from_hash (const gchar *path,
+                                    const gchar *emailid)
+{
+       EBookBackendSqliteDB *ebsdb = NULL;
+       gchar *hash_key;
+
+       hash_key = g_strdup_printf ("%s %s", emailid, path);
+       if (db_connections != NULL) {
+               ebsdb = g_hash_table_lookup (db_connections, hash_key);
+       }
+       g_free (hash_key);
+
+       if (ebsdb)
+               return g_object_ref (ebsdb);
+
+       return NULL;
+}
+
 static void
-destroy_search_data (gpointer data)
+book_backend_sqlitedb_register_to_hash (EBookBackendSqliteDB *ebsdb,
+                                       const gchar *path,
+                                       const gchar *emailid)
 {
-       e_book_backend_sqlitedb_search_data_free (data);
+       gchar *hash_key = g_strdup_printf ("%s %s", path, emailid);
+
+       if (db_connections == NULL)
+               db_connections = g_hash_table_new_full (
+                       (GHashFunc) g_str_hash,
+                       (GEqualFunc) g_str_equal,
+                       (GDestroyNotify) g_free,
+                       (GDestroyNotify) NULL);
+       g_hash_table_insert (db_connections, hash_key, ebsdb);
+
+       /* Hold on to the key, just a pointer reference to the same key in the hash table */
+       ebsdb->priv->hash_key = hash_key;
 }
 
-static SummaryField * append_summary_field (GArray         *array,
-                                           EContactField   field,
-                                           gboolean       *have_attr_list,
-                                           GError        **error);
+static void
+book_backend_sqlitedb_unregister_from_hash (EBookBackendSqliteDB *ebsdb)
+{
+       EBookBackendSqliteDBPrivate *priv = ebsdb->priv;
 
-static gboolean upgrade_contacts_table (EBookBackendSqliteDB *ebsdb,
-                                       const gchar          *folderid,
-                                       const gchar          *region,
-                                       const gchar          *lc_collate,
-                                       GError              **error);
-static gboolean sqlitedb_set_locale_internal (EBookBackendSqliteDB *ebsdb,
-                                             const gchar          *locale,
-                                             GError              **error);
+       g_mutex_lock (&dbcon_lock);
+       if (db_connections != NULL) {
+               if (priv->hash_key != NULL) {
+                       g_hash_table_remove (db_connections, priv->hash_key);
 
-static const gchar *
-summary_dbname_from_field (EBookBackendSqliteDB *ebsdb,
-                           EContactField field)
+                       if (g_hash_table_size (db_connections) == 0) {
+                               g_hash_table_destroy (db_connections);
+                               db_connections = NULL;
+                       }
+
+                       /* No need to free the key,
+                        * it was freed by g_hash_table_remove()
+                        */
+                       priv->hash_key = NULL;
+               }
+       }
+       g_mutex_unlock (&dbcon_lock);
+}
+
+/************************************************************
+ *    Executing SQLite statements and collecting results    *
+ ************************************************************
+ *
+ * Lock must be held at all times when executing statements
+ */
+/* For debugging statements with BOOKSQL_DEBUG environment variable */
+static gint
+print_debug_cb (gpointer ref,
+                gint n_cols,
+                gchar **cols,
+                gchar **name)
 {
        gint i;
 
-       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
-               if (ebsdb->priv->summary_fields[i].field == field)
-                       return ebsdb->priv->summary_fields[i].dbname;
-       }
+       g_printerr ("  DEBUG BEGIN:\n");
 
-       return NULL;
+       for (i = 0; i < n_cols; i++)
+               g_printerr ("    NAME: '%s' VALUE: %s\n", name[i], cols[i]);
+
+       g_printerr ("  DEBUG END\n");
+
+       return 0;
 }
 
+/* Collect a GList of column names in the main summary table */
 static gint
-summary_index_from_field (EBookBackendSqliteDB *ebsdb,
-                         EContactField         field)
+get_columns_cb (gpointer ref,
+               gint col,
+               gchar **cols,
+               gchar **name)
 {
+       GSList **columns = (GSList **) ref;
        gint i;
 
-       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
-               if (ebsdb->priv->summary_fields[i].field == field)
-                       return i;
-       }
+       for (i = 0; i < col; i++) {
+               if (strcmp (name[i], "name") == 0) {
+                       if (strcmp (cols[i], "vcard") != 0 &&
+                           strcmp (cols[i], "bdata") != 0) {
+                               gchar *column = g_strdup (cols[i]);
 
-       return -1;
+                               *columns = g_slist_prepend (*columns, column);
+                       }
+                       break;
+               }
+       }
+       return 0;
 }
 
+/* Collect the first string result */
 static gint
-summary_index_from_field_name (EBookBackendSqliteDB *ebsdb,
-                               const gchar *field_name)
+get_string_cb (gpointer ref,
+               gint col,
+               gchar **cols,
+               gchar **name)
 {
-       EContactField field;
+       gchar **ret = ref;
 
-       field = e_contact_field_id (field_name);
+       *ret = g_strdup (cols [0]);
 
-       return summary_index_from_field (ebsdb, field);
+       return 0;
 }
 
-typedef struct {
-       EBookBackendSqliteDB *ebsdb;
-       GSList *list;
-} StoreVCardData;
+/* Collect the first boolean result */
+static gint
+get_bool_cb (gpointer ref,
+             gint col,
+             gchar **cols,
+             gchar **name)
+{
+       gboolean *ret = ref;
 
-G_DEFINE_QUARK (
-       e-book-backend-sqlitedb-error-quark,
-       e_book_backend_sqlitedb_error)
+       *ret = cols [0] ? g_ascii_strtoull (cols[0], NULL, 10) : 0;
 
-static void
-e_book_backend_sqlitedb_dispose (GObject *object)
+       return 0;
+}
+
+/* Collect the first integer result */
+static gint
+get_int_cb (gpointer ref,
+           gint col,
+           gchar **cols,
+           gchar **name)
 {
-       EBookBackendSqliteDBPrivate *priv;
+       gint *ret = ref;
 
-       priv = E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE (object);
+       *ret = cols [0] ? g_ascii_strtoll (cols[0], NULL, 10) : 0;
 
-       g_mutex_lock (&dbcon_lock);
-       if (db_connections != NULL) {
-               if (priv->hash_key != NULL) {
-                       g_hash_table_remove (db_connections, priv->hash_key);
+       return 0;
+}
 
-                       if (g_hash_table_size (db_connections) == 0) {
-                               g_hash_table_destroy (db_connections);
-                               db_connections = NULL;
-                       }
+/* Collect the result of a SELECT count(*) statement */
+static gint
+get_count_cb (gpointer ref,
+              gint n_cols,
+              gchar **cols,
+              gchar **name)
+{
+       gint64 count = 0;
+       gint *ret = ref;
+       gint i;
 
-                       g_free (priv->hash_key);
-                       priv->hash_key = NULL;
+       for (i = 0; i < n_cols; i++) {
+               if (name[i] && strncmp (name[i], "count", 5) == 0) {
+                       count = g_ascii_strtoll (cols[i], NULL, 10);
+
+                       break;
                }
        }
-       g_mutex_unlock (&dbcon_lock);
 
-       /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (e_book_backend_sqlitedb_parent_class)->dispose (object);
+       *ret = count;
+
+       return 0;
 }
 
-static void
-e_book_backend_sqlitedb_finalize (GObject *object)
+/* Report if there was at least one result */
+static gint
+get_exists_cb (gpointer ref,
+              gint col,
+              gchar **cols,
+              gchar **name)
 {
-       EBookBackendSqliteDBPrivate *priv;
+       gboolean *exists = ref;
 
-       priv = E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE (object);
+       *exists = TRUE;
 
-       sqlite3_close (priv->db);
+       return 0;
+}
 
-       g_free (priv->path);
-       g_free (priv->summary_fields);
-       g_free (priv->locale);
+static EbSdbSearchData *
+search_data_from_results (gchar **cols)
+{
+       EbSdbSearchData *data = g_slice_new0 (EbSdbSearchData);
 
-       if (priv->collator)
-               e_collator_unref (priv->collator);
+       if (cols[0])
+               data->uid = g_strdup (cols[0]);
 
-       g_mutex_clear (&priv->lock);
-       g_mutex_clear (&priv->updates_lock);
+       if (cols[1])
+               data->vcard = g_strdup (cols[1]);
 
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_book_backend_sqlitedb_parent_class)->finalize (object);
+       if (cols[2])
+               data->bdata = g_strdup (cols[2]);
+
+       return data;
 }
 
-static void
-e_book_backend_sqlitedb_class_init (EBookBackendSqliteDBClass *class)
+static gint
+collect_full_results_cb (gpointer ref,
+                        gint col,
+                        gchar **cols,
+                        gchar **name)
 {
-       GObjectClass *object_class;
+       EbSdbSearchData *data;
+       GSList **vcard_data = ref;
 
-       g_type_class_add_private (class, sizeof (EBookBackendSqliteDBPrivate));
+       data = search_data_from_results (cols);
 
-       object_class = G_OBJECT_CLASS (class);
-       object_class->dispose = e_book_backend_sqlitedb_dispose;
-       object_class->finalize = e_book_backend_sqlitedb_finalize;
+       *vcard_data = g_slist_prepend (*vcard_data, data);
+
+       return 0;
 }
 
-static void
-e_book_backend_sqlitedb_init (EBookBackendSqliteDB *ebsdb)
+static gint
+collect_uid_results_cb (gpointer ref,
+                       gint col,
+                       gchar **cols,
+                       gchar **name)
 {
-       ebsdb->priv = E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE (ebsdb);
+       GSList **uids = ref;
 
-       ebsdb->priv->store_vcard = TRUE;
+       if (cols[0])
+               *uids = g_slist_prepend (*uids, g_strdup (cols [0]));
 
-       ebsdb->priv->in_transaction = 0;
-       g_mutex_init (&ebsdb->priv->lock);
-       g_mutex_init (&ebsdb->priv->updates_lock);
+       return 0;
 }
 
 static gint
-get_string_cb (gpointer ref,
-               gint col,
-               gchar **cols,
-               gchar **name)
+collect_lean_results_cb (gpointer ref,
+                        gint ncol,
+                        gchar **cols,
+                        gchar **name)
 {
-       gchar **ret = ref;
+       GSList **vcard_data = ref;
+       EbSdbSearchData *search_data = g_slice_new0 (EbSdbSearchData);
+       EContact *contact = e_contact_new ();
+       gchar *vcard;
+       gint i;
 
-       *ret = g_strdup (cols [0]);
+       /* 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;
+
+               /* Only UID & REV can be used to create contacts from the summary columns */
+               if (!g_ascii_strcasecmp (name[i], "uid")) {
+                       e_contact_set (contact, E_CONTACT_UID, cols[i]);
+                       search_data->uid = g_strdup (cols[i]);
+               } else if (!g_ascii_strcasecmp (name[i], "Rev")) {
+                       e_contact_set (contact, E_CONTACT_REV, cols[i]);
+               } else if (!g_ascii_strcasecmp (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 gint
-get_bool_cb (gpointer ref,
-             gint col,
-             gchar **cols,
-             gchar **name)
+collect_uids_and_rev_cb (gpointer user_data,
+                        gint col,
+                        gchar **cols,
+                        gchar **name)
 {
-       gboolean *ret = ref;
+       GHashTable *uids_and_rev = user_data;
 
-       *ret = cols [0] ? strtoul (cols [0], NULL, 10) : 0;
+       if (col == 2 && cols[0])
+               g_hash_table_insert (uids_and_rev, g_strdup (cols[0]), g_strdup (cols[1] ? cols[1] : ""));
 
        return 0;
 }
 
-/**
- * e_book_sql_exec
- * @db:
- * @stmt:
- * @callback:
- * @data:
- * @error:
- *
- * Callers should hold the rw lock depending on read or write operation
- * Returns:
- **/
 static gboolean
-book_backend_sql_exec_real (sqlite3 *db,
-                            const gchar *stmt,
-                            gint (*callback)(gpointer ,gint,gchar **,gchar **),
-                            gpointer data,
-                            GError **error)
+book_backend_sqlitedb_exec_real (EBookBackendSqliteDB *ebsdb,
+                                const gchar *stmt,
+                                EbSdbSqliteCallback callback,
+                                gpointer data,
+                                GError **error)
 {
        gchar *errmsg = NULL;
        gint ret = -1;
 
-       ret = sqlite3_exec (db, stmt, callback, data, &errmsg);
+       ret = sqlite3_exec (ebsdb->priv->db, stmt, callback, data, &errmsg);
        while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
                if (errmsg) {
                        sqlite3_free (errmsg);
                        errmsg = NULL;
                }
                g_thread_yield ();
-               ret = sqlite3_exec (db, stmt, callback, data, &errmsg);
+               ret = sqlite3_exec (ebsdb->priv->db, stmt, callback, data, &errmsg);
        }
 
        if (ret != SQLITE_OK) {
@@ -348,57 +525,43 @@ book_backend_sql_exec_real (sqlite3 *db,
        return TRUE;
 }
 
-static gint
-print_debug_cb (gpointer ref,
-                gint n_cols,
-                gchar **cols,
-                gchar **name)
-{
-       gint i;
-
-       g_printerr ("  DEBUG BEGIN:\n");
-
-       for (i = 0; i < n_cols; i++)
-               g_printerr ("    NAME: '%s' VALUE: %s\n", name[i], cols[i]);
-
-       g_printerr ("  DEBUG END\n");
-
-       return 0;
-}
-
 static gint G_GNUC_CONST
-booksql_debug (void)
+book_backend_sqlitedb_debug_level (void)
 {
        static gint booksql_debug = -1;
 
        if (booksql_debug == -1) {
                const gchar *const tmp = g_getenv ("BOOKSQL_DEBUG");
-               booksql_debug = (tmp != NULL ? MAX (0, atoi (tmp)) : 0);
+
+               if (tmp)
+                       booksql_debug = g_ascii_strtoll (tmp, NULL, 10);
+               else
+                       booksql_debug = 0;
        }
 
        return booksql_debug;
 }
 
 static void
-book_backend_sql_debug (sqlite3 *db,
-                        const gchar *stmt,
-                        gint (*callback)(gpointer ,gint,gchar **,gchar **),
-                        gpointer data,
-                        GError **error)
+book_backend_sqlitedb_debug (EBookBackendSqliteDB *ebsdb,
+                            const gchar *stmt,
+                            EbSdbSqliteCallback callback,
+                            gpointer data,
+                            GError **error)
 {
        GError *local_error = NULL;
 
        g_printerr ("DEBUG STATEMENT: %s\n", stmt);
 
-       if (booksql_debug () > 1) {
+       if (book_backend_sqlitedb_debug_level () > 1) {
                gchar *debug = g_strconcat ("EXPLAIN QUERY PLAN ", stmt, NULL);
-               book_backend_sql_exec_real (db, debug, print_debug_cb, NULL, &local_error);
+               book_backend_sqlitedb_exec_real (ebsdb, debug, print_debug_cb, NULL, &local_error);
                g_free (debug);
        }
 
        if (local_error) {
                g_printerr ("DEBUG STATEMENT END: Error: %s\n", local_error->message);
-       } else if (booksql_debug () > 1) {
+       } else if (book_backend_sqlitedb_debug_level () > 1) {
                g_printerr ("DEBUG STATEMENT END: Success\n");
        }
 
@@ -406,19 +569,54 @@ book_backend_sql_debug (sqlite3 *db,
 }
 
 static gboolean
-book_backend_sql_exec (sqlite3 *db,
-                       const gchar *stmt,
-                       gint (*callback)(gpointer ,gint,gchar **,gchar **),
-                       gpointer data,
-                       GError **error)
+book_backend_sqlitedb_exec (EBookBackendSqliteDB *ebsdb,
+                           const gchar *stmt,
+                           EbSdbSqliteCallback callback,
+                           gpointer data,
+                           GError **error)
 {
-       if (booksql_debug ())
-               book_backend_sql_debug (db, stmt, callback, data, error);
+       if (book_backend_sqlitedb_debug_level () > 0)
+               book_backend_sqlitedb_debug (ebsdb, stmt, callback, data, error);
 
-       return book_backend_sql_exec_real (db, stmt, callback, data, error);
+       return book_backend_sqlitedb_exec_real (ebsdb, stmt, callback, data, error);
+}
+
+static gboolean
+book_backend_sqlitedb_exec_vprintf (EBookBackendSqliteDB *ebsdb,
+                                   const gchar *fmt,
+                                   EbSdbSqliteCallback callback,
+                                   gpointer data,
+                                   GError **error,
+                                   va_list args)
+{
+       gboolean success;
+       gchar *stmt;
+
+       stmt = sqlite3_vmprintf (fmt, args);
+       success = book_backend_sqlitedb_exec (ebsdb, stmt, callback, data, error);
+       sqlite3_free (stmt);
+
+       return success;
+}
+
+static gboolean
+book_backend_sqlitedb_exec_printf (EBookBackendSqliteDB *ebsdb,
+                                  const gchar *fmt,
+                                  EbSdbSqliteCallback callback,
+                                  gpointer data,
+                                  GError **error,
+                                  ...)
+{
+       gboolean success;
+       va_list args;
+
+       va_start (args, error);
+       success = book_backend_sqlitedb_exec_vprintf (ebsdb, fmt, callback, data, error, args);
+       va_end (args);
+
+       return success;
 }
 
-/* This function must always be called with the priv->lock held */
 static gboolean
 book_backend_sqlitedb_start_transaction (EBookBackendSqliteDB *ebsdb,
                                          GError **error)
@@ -434,14 +632,13 @@ book_backend_sqlitedb_start_transaction (EBookBackendSqliteDB *ebsdb,
 
        if (ebsdb->priv->in_transaction == 1) {
 
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, "BEGIN", NULL, NULL, error);
+               success = book_backend_sqlitedb_exec (
+                       ebsdb, "BEGIN", NULL, NULL, error);
        }
 
        return success;
 }
 
-/* This function must always be called with the priv->lock held */
 static gboolean
 book_backend_sqlitedb_commit_transaction (EBookBackendSqliteDB *ebsdb,
                                           GError **error)
@@ -457,14 +654,13 @@ book_backend_sqlitedb_commit_transaction (EBookBackendSqliteDB *ebsdb,
        ebsdb->priv->in_transaction--;
 
        if (ebsdb->priv->in_transaction == 0) {
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, "COMMIT", NULL, NULL, error);
+               success = book_backend_sqlitedb_exec (
+                       ebsdb, "COMMIT", NULL, NULL, error);
        }
 
        return success;
 }
 
-/* This function must always be called with the priv->lock held */
 static gboolean
 book_backend_sqlitedb_rollback_transaction (EBookBackendSqliteDB *ebsdb,
                                             GError **error)
@@ -480,776 +676,295 @@ book_backend_sqlitedb_rollback_transaction (EBookBackendSqliteDB *ebsdb,
        ebsdb->priv->in_transaction--;
 
        if (ebsdb->priv->in_transaction == 0) {
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, "ROLLBACK", NULL, NULL, error);
+               success = book_backend_sqlitedb_exec (
+                       ebsdb, "ROLLBACK", NULL, NULL, error);
 
        }
        return success;
 }
 
-static gint
-collect_versions_cb (gpointer ref,
-                     gint col,
-                     gchar **cols,
-                     gchar **name)
+static sqlite3_stmt *
+book_backend_sqlitedb_prepare_statement (EBookBackendSqliteDB *ebsdb,
+                                        const gchar *stmt_str,
+                                        GError **error)
 {
-       gint *ret = ref;
-
-       /* Just collect the first result, all folders
-        * should always have the same DB version. */
-       *ret = cols [0] ? strtoul (cols [0], NULL, 10) : 0;
-
-       return 0;
-}
-
-typedef struct {
-       gboolean has_countrycode;
-       gboolean has_lc_collate;
-} LocaleColumns;
+       sqlite3_stmt *stmt;
+       const gchar *stmt_tail = NULL;
+       gint ret;
 
-static gint
-find_locale_columns (gpointer data,
-                    gint n_cols,
-                    gchar **cols,
-                    gchar **name)
-{
-       LocaleColumns *columns = (LocaleColumns *)data;
-       gint i;
+       ret = sqlite3_prepare_v2 (ebsdb->priv->db, stmt_str, strlen (stmt_str), &stmt, &stmt_tail);
 
-       for (i = 0; i < n_cols; i++) {
-               if (g_strcmp0 (cols[i], "countrycode") == 0)
-                       columns->has_countrycode = TRUE;
-               else if (g_strcmp0 (cols[i], "lc_collate") == 0)
-                       columns->has_lc_collate = TRUE;
+       if (ret != SQLITE_OK) {
+               const gchar *errmsg = sqlite3_errmsg (ebsdb->priv->db);
+               g_set_error_literal (error,
+                                    E_BOOK_SDB_ERROR,
+                                    E_BOOK_SDB_ERROR_OTHER,
+                                    errmsg);
+       } else if (stmt == NULL) {
+               g_set_error_literal (error,
+                                    E_BOOK_SDB_ERROR,
+                                    E_BOOK_SDB_ERROR_OTHER,
+                                    "Unknown error preparing SQL statement");
        }
 
-       return 0;
+       if (stmt_tail && stmt_tail[0])
+               g_warning ("Part of this statement was not parsed: %s", stmt_tail);
+
+       return stmt;
 }
 
+/* Convenience for running statements. After successfully
+ * binding all parameters, just return with this.
+ */
 static gboolean
-create_folders_table (EBookBackendSqliteDB *ebsdb,
-                      gint *previous_schema,
-                      GError **error)
+book_backend_sqlitedb_complete_statement (EBookBackendSqliteDB *ebsdb,
+                                         sqlite3_stmt *stmt,
+                                         gint ret,
+                                         GError **error)
 {
-       gboolean success;
-       gint version = 0;
-       LocaleColumns locale_columns = { FALSE, FALSE };
-
-       /* sync_data points to syncronization data, it could be last_modified
-        * time or a sequence number or some text depending on the backend.
-        *
-        * partial_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 DEFAULT 0,"
-               "  partial_content INTEGER DEFAULT 0,"
-               " version INTEGER,"
-               "  revision TEXT,"
-               " multivalues TEXT )";
-
-       if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
-               return FALSE;
-
-       if (!book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error))
-               goto rollback;
-
-       /* Create a child table to store key/value pairs for a folder. */
-       stmt =  "CREATE TABLE IF NOT EXISTS keys"
-               "( key TEXT PRIMARY KEY, value TEXT,"
-               " folder_id TEXT REFERENCES folders)";
-       if (!book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error))
-               goto rollback;
-
-       stmt = "CREATE INDEX IF NOT EXISTS keysindex ON keys(folder_id)";
-       if (!book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error))
-               goto rollback;
-
-       /* Fetch the version, it should be the
-        * same for all folders (hence the LIMIT). */
-       stmt = "SELECT version FROM folders LIMIT 1";
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, collect_versions_cb, &version, error);
-
-       if (!success)
-               goto rollback;
-
-       /* Upgrade DB to version 2, add revision column
-        *
-        * (version = 0 indicates that it did not exist and we just
-        * created the table)
-        */
-       if (version >= 1 && version < 2) {
-               stmt = "ALTER TABLE folders ADD COLUMN revision TEXT";
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, stmt, NULL, NULL, error);
-
-               if (!success)
-                       goto rollback;
-       }
-
-       /* Upgrade DB to version 3, add multivalues introspection columns
-        */
-       if (version >= 1 && version < 3) {
-               stmt = "ALTER TABLE folders ADD COLUMN multivalues TEXT";
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, stmt, NULL, NULL, error);
-
-               if (!success)
-                       goto rollback;
-       }
-
-       /* Upgrade DB to version 4: Nothing to do. The country-code column it
-        * added got redundant already.
-        */
-
-       /* Upgrade DB to version 5: Drop the reverse_multivalues column, but
-        * wait with converting phone summary values to new format until
-        * create_contacts_table() as we need introspection details for doing
-        * that.
-        */
-       if (version >= 3 && version < 5) {
-               stmt = "UPDATE folders SET "
-                               "multivalues = REPLACE(RTRIM(REPLACE("
-                                       "multivalues || ':', ':', "
-                                       "CASE reverse_multivalues "
-                                               "WHEN 0 THEN ';prefix ' "
-                                               "ELSE ';prefix;suffix ' "
-                                       "END)), ' ', ':'), "
-                               "reverse_multivalues = NULL";
-
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, stmt, NULL, NULL, error);
-
-               if (!success)
-                       goto rollback;
-       }
-
-       /* Finish the eventual upgrade by storing the current schema version.
-        */
-       if (version >= 1 && version < FOLDER_VERSION) {
-               gchar *version_update_stmt =
-                       sqlite3_mprintf ("UPDATE folders SET version = %d", FOLDER_VERSION);
-
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, version_update_stmt, NULL, NULL, error);
-
-               sqlite3_free (version_update_stmt);
-       }
-
-       if (!success)
-               goto rollback;
-
-       /* Ensure countrycode column exists to track the addressbook's country code,
-        * these statements are safe regardless of the previous schema version.
-        */
-       stmt = "PRAGMA table_info(folders)";
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, find_locale_columns, &locale_columns, error);
-
-       if (!success)
-               goto rollback;
-
-       if (!locale_columns.has_countrycode) {
-               stmt = "ALTER TABLE folders ADD COLUMN countrycode VARCHAR(2)";
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-
-               if (!success)
-                       goto rollback;
-
-       }
-
-       if (!locale_columns.has_lc_collate) {
-               stmt = "ALTER TABLE folders ADD COLUMN lc_collate TEXT";
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
+       if (ret == SQLITE_OK) {
+               ret = sqlite3_step (stmt);
 
-               if (!success)
-                       goto rollback;
+               if (ret == SQLITE_DONE)
+                       ret = SQLITE_OK;
        }
 
-       /* Remember the schema version for later use and finish the transaction. */
-       *previous_schema = version;
-       return book_backend_sqlitedb_commit_transaction (ebsdb, error);
+       if (ret != SQLITE_OK) {
+               const gchar *errmsg = sqlite3_errmsg (ebsdb->priv->db);
+               g_set_error_literal (error,
+                                    E_BOOK_SDB_ERROR,
+                                    ret == SQLITE_CONSTRAINT ?
+                                    E_BOOK_SDB_ERROR_CONSTRAINT : E_BOOK_SDB_ERROR_OTHER,
+                                    errmsg);
+       } 
 
-rollback:
-       /* The GError is already set. */
-       book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+       /* Reset / Clear at the end, regardless of error state */
+       sqlite3_reset (stmt);
+       sqlite3_clear_bindings (stmt);
 
-       *previous_schema = 0;
-       return FALSE;
+       return (ret == SQLITE_OK);
 }
 
-static gchar *
-format_multivalues (EBookBackendSqliteDB *ebsdb)
+/******************************************************
+ *                  Summary Fields                    *
+ ******************************************************/
+static gint
+summary_field_array_index (GArray *array,
+                          EContactField field)
 {
        gint i;
-       GString *string;
-       gboolean first = TRUE;
-
-       string = g_string_new (NULL);
-
-       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
-               if (ebsdb->priv->summary_fields[i].type == E_TYPE_CONTACT_ATTR_LIST) {
-                       if (first)
-                               first = FALSE;
-                       else
-                               g_string_append_c (string, ':');
-
-                       g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
 
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_PREFIX) != 0)
-                               g_string_append (string, ";prefix");
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0)
-                               g_string_append (string, ";suffix");
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0)
-                               g_string_append (string, ";phone");
-               }
+       for (i = 0; i < array->len; i++) {
+               SummaryField *iter = &g_array_index (array, SummaryField, i);
+               if (field == iter->field)
+                       return i;
        }
 
-       return g_string_free (string, FALSE);
+       return -1;
 }
 
-static gint
-get_count_cb (gpointer ref,
-              gint n_cols,
-              gchar **cols,
-              gchar **name)
+static SummaryField *
+summary_field_append (GArray *array,
+                      EContactField field,
+                      gboolean *have_attr_list,
+                      GError **error)
 {
-       gint64 count = 0;
-       gint *ret = ref;
-       gint i;
+       const gchar *dbname = NULL;
+       GType        type = G_TYPE_INVALID;
+       gint         index;
+       SummaryField new_field = { 0, };
 
-       for (i = 0; i < n_cols; i++) {
-               if (name[i] && strncmp (name[i], "count", 5) == 0) {
-                       count = g_ascii_strtoll (cols[i], NULL, 10);
-               }
+       if (field < 1 || field >= E_CONTACT_FIELD_LAST) {
+               g_set_error (
+                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+                       _("Invalid contact field '%d' specified in summary"), field);
+               return NULL;
        }
 
-       *ret = count;
+       /* Avoid including the same field twice in the summary */
+       index = summary_field_array_index (array, field);
+       if (index >= 0)
+               return &g_array_index (array, SummaryField, index);
 
-       return 0;
-}
+       /* Resolve some exceptions, we store these
+        * specific contact fields with different names
+        * than those found in the EContactField table
+        */
+       switch (field) {
+       case E_CONTACT_UID:
+               dbname = "uid";
+               break;
+       case E_CONTACT_IS_LIST:
+               dbname = "is_list";
+               break;
+       default:
+               dbname = e_contact_field_name (field);
+               break;
+       }
 
-static gboolean
-check_folderid_exists (EBookBackendSqliteDB *ebsdb,
-                       const gchar *folderid,
-                       gboolean *exists,
-                       GError **error)
-{
-       gboolean success;
-       gint count = 0;
-       gchar *stmt;
+       type = e_contact_field_type (field);
 
-       stmt = sqlite3_mprintf ("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=%Q;", 
folderid);
+       if (type != G_TYPE_STRING &&
+           type != G_TYPE_BOOLEAN &&
+           type != E_TYPE_CONTACT_ATTR_LIST) {
+               g_set_error (
+                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+                       _("Contact field '%s' of type '%s' specified in summary, "
+                       "but only boolean, string and string list field types are supported"),
+                       e_contact_pretty_name (field), g_type_name (type));
+               return NULL;
+       }
 
-       success = book_backend_sql_exec (ebsdb->priv->db, stmt, get_count_cb, &count, error);
-       sqlite3_free (stmt);
+       if (type == E_TYPE_CONTACT_ATTR_LIST && have_attr_list)
+               *have_attr_list = TRUE;
 
-       *exists = (count > 0);
+       new_field.field  = field;
+       new_field.dbname = dbname;
+       new_field.type   = type;
+       new_field.index  = 0;
+       g_array_append_val (array, new_field);
 
-       return success;
+       return &g_array_index (array, SummaryField, array->len - 1);
 }
 
 static gboolean
-add_folder_into_db (EBookBackendSqliteDB *ebsdb,
-                    const gchar *folderid,
-                    const gchar *folder_name,
-                   gboolean *already_exists,
-                    GError **error)
+summary_field_remove (GArray *array,
+                      EContactField field)
 {
-       gchar *stmt;
-       gboolean success;
-       gchar *multivalues;
-       gboolean exists = FALSE;
+       gint index;
 
-       if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
+       index = summary_field_array_index (array, field);
+       if (index < 0)
                return FALSE;
 
-       success = check_folderid_exists (ebsdb, folderid, &exists, error);
-       if (!success)
-               goto rollback;
-
-       if (!exists) {
-               const gchar *lc_collate;
-
-               multivalues = format_multivalues (ebsdb);
-
-               lc_collate = setlocale (LC_COLLATE, NULL);
-
-               stmt = sqlite3_mprintf (
-                       "INSERT OR IGNORE INTO "
-                       "folders ( folder_id, folder_name, version, multivalues, lc_collate ) "
-                       "VALUES ( %Q, %Q, %d, %Q, %Q ) ",
-                       folderid, folder_name, FOLDER_VERSION, multivalues, lc_collate);
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, stmt, NULL, NULL, error);
-               sqlite3_free (stmt);
-               g_free (multivalues);
-
-               if (!success)
-                       goto rollback;
-       }
-
-       if (already_exists)
-               *already_exists = exists;
-
-       return book_backend_sqlitedb_commit_transaction (ebsdb, error);
-
-rollback:
-       book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
-
-       return FALSE;
+       g_array_remove_index_fast (array, index);
+       return TRUE;
 }
 
-static gint
-collect_columns_cb (gpointer ref,
-                    gint col,
-                    gchar **cols,
-                    gchar **name)
+static void
+summary_fields_add_indexes (GArray *array,
+                            EContactField *indexes,
+                            EBookIndexType *index_types,
+                            gint n_indexes,
+                            gint *attr_list_indexes)
 {
-       GList **columns = (GList **) ref;
-       gint i;
-
-       for (i = 0; i < col; i++) {
-
-               if (strcmp (name[i], "name") == 0) {
+       gint i, j;
 
-                       if (strcmp (cols[i], "vcard") != 0 &&
-                           strcmp (cols[i], "bdata") != 0) {
+       for (i = 0; i < array->len; i++) {
+               SummaryField *sfield = &g_array_index (array, SummaryField, i);
 
-                               gchar *column = g_strdup (cols[i]);
+               for (j = 0; j < n_indexes; j++) {
+                       if (sfield->field == indexes[j]) {
+                               sfield->index |= (1 << index_types[j]);
 
-                               *columns = g_list_prepend (*columns, column);
+                               if (sfield->type == E_TYPE_CONTACT_ATTR_LIST)
+                                       *attr_list_indexes |= (1 << index_types[j]);
                        }
-
-                       break;
                }
        }
-
-       return 0;
 }
 
-static gboolean
-introspect_summary (EBookBackendSqliteDB *ebsdb,
-                    const gchar *folderid,
-                    GError **error)
+static SummaryField *
+summary_field_get (EBookBackendSqliteDB *ebsdb,
+                  EContactField         field)
 {
-       gboolean success, have_attr_list;
-       gchar *stmt;
-       GList *summary_columns = NULL, *l;
-       GArray *summary_fields = NULL;
-       gchar *multivalues = NULL;
-       gint i, j;
-
-       stmt = sqlite3_mprintf ("PRAGMA table_info (%Q);", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, collect_columns_cb, &summary_columns, error);
-       sqlite3_free (stmt);
-
-       if (!success)
-               goto introspect_summary_finish;
-
-       summary_columns = g_list_reverse (summary_columns);
-       summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
-
-       /* Introspect the normal summary fields */
-       for (l = summary_columns; l; l = l->next) {
-               EContactField field;
-               gchar *col = l->data;
-               gchar *p;
-               IndexFlags computed = 0;
-
-               /* Ignore the 'localized' columns */
-               if (g_str_has_suffix (col, "_localized"))
-                       continue;
-
-               /* Check if we're parsing a reverse field */
-               if ((p = strstr (col, "_reverse")) != NULL) {
-                       computed = INDEX_SUFFIX;
-                       *p = '\0';
-               } else  if ((p = strstr (col, "_phone")) != NULL) {
-                       computed = INDEX_PHONE;
-                       *p = '\0';
-               }
-
-               /* First check exception fields */
-               if (g_ascii_strcasecmp (col, "uid") == 0)
-                       field = E_CONTACT_UID;
-               else if (g_ascii_strcasecmp (col, "is_list") == 0)
-                       field = E_CONTACT_IS_LIST;
-               else
-                       field = e_contact_field_id (col);
-
-               /* Check for parse error */
-               if (field == 0) {
-                       g_set_error (
-                               error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
-                               _("Error introspecting unknown summary field '%s'"), col);
-                       success = FALSE;
-                       break;
-               }
-
-               /* Computed columns are always declared after the normal columns,
-                * if a reverse field is encountered we need to set the suffix
-                * index on the coresponding summary field
-                */
-               if (computed) {
-                       for (i = 0; i < summary_fields->len; i++) {
-                               SummaryField *iter = &g_array_index (summary_fields, SummaryField, i);
-
-                               if (iter->field == field) {
-                                       iter->index |= computed;
-                                       break;
-                               }
-                       }
-               } else {
-                       append_summary_field (summary_fields, field, NULL, NULL);
-               }
-       }
-
-       if (!success)
-               goto introspect_summary_finish;
-
-       /* Introspect the multivalied summary fields */
-       stmt = sqlite3_mprintf (
-               "SELECT multivalues FROM folders WHERE folder_id = %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, get_string_cb, &multivalues, error);
-       sqlite3_free (stmt);
-
-       if (!success)
-               goto introspect_summary_finish;
-
-       ebsdb->priv->attr_list_indexes = 0;
-       ebsdb->priv->have_attr_list = have_attr_list = FALSE;
-
-       if (multivalues) {
-               gchar **fields = g_strsplit (multivalues, ":", 0);
-
-               for (i = 0; fields[i] != NULL; i++) {
-                       EContactField field;
-                       SummaryField *iter;
-                       gchar **params;
-
-                       params = g_strsplit (fields[i], ";", 0);
-                       field = e_contact_field_id (params[0]);
-                       iter = append_summary_field (summary_fields, field, &have_attr_list, NULL);
-
-                       if (iter) {
-                               for (j = 1; params[j]; ++j) {
-                                       if (strcmp (params[j], "prefix") == 0) {
-                                               iter->index |= INDEX_PREFIX;
-                                       } else if (strcmp (params[j], "suffix") == 0) {
-                                               iter->index |= INDEX_SUFFIX;
-                                       } else if (strcmp (params[j], "phone") == 0) {
-                                               iter->index |= INDEX_PHONE;
-                                       }
-                               }
-
-                               ebsdb->priv->attr_list_indexes |= iter->index;
-                       }
-
-                       g_strfreev (params);
-               }
-
-               ebsdb->priv->have_attr_list = have_attr_list;
-
-               g_strfreev (fields);
-       }
-
- introspect_summary_finish:
-
-       g_list_free_full (summary_columns, (GDestroyNotify) g_free);
-       g_free (multivalues);
+       gint i;
 
-       /* Apply the introspected summary fields */
-       if (success) {
-               g_free (ebsdb->priv->summary_fields);
-               ebsdb->priv->n_summary_fields = summary_fields->len;
-               ebsdb->priv->summary_fields = (SummaryField *) g_array_free (summary_fields, FALSE);
-       } else if (summary_fields) {
-               g_array_free (summary_fields, TRUE);
+       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+               if (ebsdb->priv->summary_fields[i].field == field)
+                       return &(ebsdb->priv->summary_fields[i]);
        }
 
-       return success;
+       return NULL;
 }
 
-/* The column names match the fields used in book-backend-sexp */
-static gboolean
-create_contacts_table (EBookBackendSqliteDB *ebsdb,
-                       const gchar *folderid,
-                       gint previous_schema,
-                      gboolean already_exists,
-                       GError **error)
+static SummaryField *
+summary_field_get_by_name (EBookBackendSqliteDB *ebsdb,
+                          const gchar          *field_name)
 {
-       gint i;
-       gboolean success = TRUE;
-       gchar *stmt, *tmp;
-       GString *string;
-       gboolean relocalized = FALSE;
-       gchar *current_region = NULL;
-       const gchar *lc_collate = NULL;
-       gchar *stored_lc_collate = NULL;
-
-       if (e_phone_number_is_supported ()) {
-               current_region = e_phone_number_get_default_region (error);
-
-               if (current_region == NULL)
-                       return FALSE;
-       }
+       EContactField field;
 
-       /* Introspect the summary if the table already exists */
-       if (already_exists) {
-               success = introspect_summary (ebsdb, folderid, error);
+       field = e_contact_field_id (field_name);
 
-               if (!success)
-                       return FALSE;
-       }
+       return summary_field_get (ebsdb, field);
+}
 
-       string = g_string_new (
-               "CREATE TABLE IF NOT EXISTS %Q ( uid TEXT PRIMARY KEY, ");
+static GSList *
+summary_field_list_main_columns (SummaryField *field,
+                                const gchar  *folderid)
+{
+       GSList *columns = NULL;
+       ColumnInfo *info;
 
-       /* Add column creation statements for each summary field.
-        *
-        * Start looping over the summary fields only starting with the second element,
-        * the first element (which is always the UID), is already specified in the
-        * CREATE TABLE statement which we are building.
+       /* Only strings and booleans are supported in the main
+        * summary, string lists are supported in the auxiliary tables
         */
-       for (i = 1; i < ebsdb->priv->n_summary_fields; i++) {
-               if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
-                       g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
-                       g_string_append (string, " TEXT, ");
-
-                       /* For any string columns (not multivalued columns), also create a localized
-                        * data column for sort ordering
-                        */
-                       if (ebsdb->priv->summary_fields[i].field != E_CONTACT_REV) {
-                               g_string_append  (string, ebsdb->priv->summary_fields[i].dbname);
-                               g_string_append  (string, "_localized TEXT, ");
-                       }
-
-               } else if (ebsdb->priv->summary_fields[i].type == G_TYPE_BOOLEAN) {
-                       g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
-                       g_string_append (string, " INTEGER, ");
-               } else if (ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST)
-                       g_warn_if_reached ();
-
-               /* Additional columns holding normalized reverse values for suffix matching */
-               if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
-                       if (ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) {
-                               g_string_append  (string, ebsdb->priv->summary_fields[i].dbname);
-                               g_string_append  (string, "_reverse TEXT, ");
-                       }
-
-                       if (ebsdb->priv->summary_fields[i].index & INDEX_PHONE) {
-                               g_string_append  (string, ebsdb->priv->summary_fields[i].dbname);
-                               g_string_append  (string, "_phone TEXT, ");
-                       }
-               }
-       }
-       g_string_append (string, "vcard TEXT, bdata TEXT)");
-
-       stmt = sqlite3_mprintf (string->str, folderid);
-       g_string_free (string, TRUE);
-
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL , error);
-
-       sqlite3_free (stmt);
-
-       /* Now, if we're upgrading from < version 7, we need to add the _localized columns */
-       if (success && previous_schema >= 1 && previous_schema < 7) {
-
-               tmp = sqlite3_mprintf ("ALTER TABLE %Q ADD COLUMN ", folderid);
-
-               /* UID and REV are always the first two summary fields, in this
-                * case we want to localize all strings EXCEPT these two, so
-                * we start iterating with the third element.
-                */
-               for (i = 2; i < ebsdb->priv->n_summary_fields && success; i++) {
-
-                       if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
-                               string = g_string_new (tmp);
-
-                               g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
-                               g_string_append (string, "_localized TEXT");
-
-                               success = book_backend_sql_exec (
-                                       ebsdb->priv->db, string->str, NULL, NULL , error);
-
-                               g_string_free (string, TRUE);
-                       }
-               }
-               sqlite3_free (tmp);
-       }
-
-       /* Construct the create statement from the attribute list summary table */
-       if (success && ebsdb->priv->have_attr_list) {
-               string = g_string_new ("CREATE TABLE IF NOT EXISTS %Q ( uid TEXT NOT NULL REFERENCES %Q(uid), 
"
-                       "field TEXT, value TEXT");
-
-               if ((ebsdb->priv->attr_list_indexes & INDEX_SUFFIX) != 0)
-                       g_string_append (string, ", value_reverse TEXT");
-               if ((ebsdb->priv->attr_list_indexes & INDEX_PHONE) != 0)
-                       g_string_append (string, ", value_phone TEXT");
-
-               g_string_append_c (string, ')');
-
-               tmp = g_strdup_printf ("%s_lists", folderid);
-               stmt = sqlite3_mprintf (string->str, tmp, folderid);
-               g_string_free (string, TRUE);
+       if (field->type != G_TYPE_STRING &&
+           field->type != G_TYPE_BOOLEAN)
+               return NULL;
 
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-               sqlite3_free (stmt);
+       /* Normal / default column */
+       info = g_slice_new0 (ColumnInfo);
+       info->name = g_strdup (field->dbname);
 
-               /* Give the UID an index in this table, always */
-               stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS LISTINDEX ON %Q (uid)", tmp);
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-               sqlite3_free (stmt);
+       if (field->type == G_TYPE_STRING)
+               info->type = "TEXT";
+       else if (field->type == G_TYPE_BOOLEAN)
+               info->type = "INTEGER";
 
-               /* Create indexes if specified */
-               if (success && (ebsdb->priv->attr_list_indexes & INDEX_PREFIX) != 0) {
-                       stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS VALINDEX ON %Q (value)", tmp);
-                       success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                       sqlite3_free (stmt);
-               }
+       if (field->field == E_CONTACT_UID)
+               info->extra = "PRIMARY KEY";
 
-               if (success && (ebsdb->priv->attr_list_indexes & INDEX_SUFFIX) != 0) {
-                       stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS RVALINDEX ON %Q (value_reverse)", 
tmp);
-                       success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                       sqlite3_free (stmt);
-               }
+       if ((field->index & INDEX_FLAG (PREFIX)) != 0)
+               info->index = g_strconcat ("INDEX_", field->dbname, "_", folderid, NULL);
 
-               if (success && (ebsdb->priv->attr_list_indexes & INDEX_PHONE) != 0) {
-                       stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS PVALINDEX ON %Q (value_phone)", 
tmp);
-                       success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                       sqlite3_free (stmt);
-               }
+       columns = g_slist_prepend (columns, info);
 
-               g_free (tmp);
+       /* Localized column, for storing sort keys */
+       if (field->type == G_TYPE_STRING && (field->index & INDEX_FLAG (SORT_KEY))) {
+               info = g_slice_new0 (ColumnInfo);
+               info->name  = g_strconcat (field->dbname, "_localized", NULL);
+               info->type  = "TEXT";
+               info->index = g_strconcat ("INDEX_", field->dbname, "_localized_", folderid, NULL);
+               columns = g_slist_prepend (columns, info);
        }
 
-       /* Create indexes on the summary fields configured for indexing */
-       for (i = 0; success && i < ebsdb->priv->n_summary_fields; i++) {
-               if ((ebsdb->priv->summary_fields[i].index & INDEX_PREFIX) != 0 &&
-                   ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
-                       /* Derive index name from field & folder */
-                       tmp = g_strdup_printf (
-                               "INDEX_%s_%s",
-                               summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field),
-                               folderid);
-                       stmt = sqlite3_mprintf (
-                               "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)", tmp, folderid,
-                               summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field));
-                       success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                       sqlite3_free (stmt);
-                       g_free (tmp);
-
-                       /* For any indexed column, also index the localized column */
-                       if (ebsdb->priv->summary_fields[i].field != E_CONTACT_REV) {
-                               tmp = g_strdup_printf (
-                                       "INDEX_%s_localized_%s",
-                                       summary_dbname_from_field (ebsdb, 
ebsdb->priv->summary_fields[i].field),
-                                       folderid);
-                               stmt = sqlite3_mprintf (
-                                       "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s_localized)", tmp, folderid,
-                                       summary_dbname_from_field (ebsdb, 
ebsdb->priv->summary_fields[i].field));
-                               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                               sqlite3_free (stmt);
-                               g_free (tmp);
-                       }
-               }
-
-               if (success &&
-                   (ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0 &&
-                   ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
-                       /* Derive index name from field & folder */
-                       tmp = g_strdup_printf (
-                               "RINDEX_%s_%s",
-                               summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field),
-                               folderid);
-                       stmt = sqlite3_mprintf (
-                               "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s_reverse)", tmp, folderid,
-                               summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field));
-                       success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                       sqlite3_free (stmt);
-                       g_free (tmp);
-               }
-
-               if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0 &&
-                   ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
-                       /* Derive index name from field & folder */
-                       tmp = g_strdup_printf (
-                               "PINDEX_%s_%s",
-                               summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field),
-                               folderid);
-                       stmt = sqlite3_mprintf (
-                               "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s_phone)", tmp, folderid,
-                               summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field));
-                       success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-                       sqlite3_free (stmt);
-                       g_free (tmp);
-               }
+       /* Suffix match column */
+       if (field->type == G_TYPE_STRING && (field->index & INDEX_FLAG (SUFFIX)) != 0) {
+               info = g_slice_new0 (ColumnInfo);
+               info->name  = g_strconcat (field->dbname, "_reverse", NULL);
+               info->type  = "TEXT";
+               info->index = g_strconcat ("RINDEX_", field->dbname, "_", folderid, NULL);
+               columns = g_slist_prepend (columns, info);
        }
 
-       /* Get the locale setting for this addressbook */
-       if (success && already_exists) {
-               stmt = sqlite3_mprintf ("SELECT lc_collate FROM folders WHERE folder_id = %Q", folderid);
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb, &stored_lc_collate, 
error);
-               sqlite3_free (stmt);
-
-               lc_collate = stored_lc_collate;
-
+       /* Phone match column */
+       if (field->type == G_TYPE_STRING && (field->index & INDEX_FLAG (PHONE)) != 0) {
+               info = g_slice_new0 (ColumnInfo);
+               info->name  = g_strconcat (field->dbname, "_phone", NULL);
+               info->type  = "TEXT";
+               info->index = g_strconcat ("PINDEX_", field->dbname, "_", folderid, NULL);
+               columns = g_slist_prepend (columns, info);
        }
 
-       if (!lc_collate)
-               /* When creating a new addressbook, or upgrading from a version
-                * where we did not have any locale setting; default to system locale
-                */
-               lc_collate = setlocale (LC_COLLATE, NULL);
-
-       /* Before touching any data, make sure we have a valid ECollator */
-       if (success) {
-               success = sqlitedb_set_locale_internal (ebsdb, lc_collate, error);
-       }
-
-       /* Need to relocalize the whole thing if the schema has been upgraded to version 7 */
-       if (success && previous_schema >= 1 && previous_schema < 7) {
-               success = upgrade_contacts_table (ebsdb, folderid, current_region, lc_collate, error);
-               relocalized = TRUE;
-       }
-
-       /* We may need to relocalize for a country code change */
-       if (success && relocalized == FALSE && e_phone_number_is_supported ()) {
-               gchar *stored_region = NULL;
-
-               stmt = sqlite3_mprintf ("SELECT countrycode FROM folders WHERE folder_id = %Q", folderid);
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb, &stored_region, error);
-               sqlite3_free (stmt);
-
-               if (success && g_strcmp0 (current_region, stored_region) != 0) {
-                       success = upgrade_contacts_table (ebsdb, folderid, current_region, lc_collate, error);
-                       relocalized = TRUE;
-               }
+       return g_slist_reverse (columns);
+}
 
-               g_free (stored_region);
+static void
+column_info_free (ColumnInfo *info)
+{
+       if (info) {
+               g_free (info->name);
+               g_free (info->index);
+               g_slice_free (ColumnInfo, info);
        }
-
-       g_free (current_region);
-       g_free (stored_lc_collate);
-
-       return success;
 }
 
+/******************************************************
+ *          OLD PHONE NUMBER STUFF TO REMOVE          *
+ ******************************************************/
 typedef struct {
-       sqlite3 *db;
+       EBookBackendSqliteDB *ebsdb;
        const gchar *collation;
        const gchar *table;
 } CollationInfo;
@@ -1273,7 +988,7 @@ create_phone_indexes_for_columns (gpointer data,
                        "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s COLLATE %Q)",
                        index_name, info->table, column_name, info->collation);
 
-               if (!book_backend_sql_exec (info->db, stmt, NULL, NULL, &error)) {
+               if (!book_backend_sqlitedb_exec (info->ebsdb, stmt, NULL, NULL, &error)) {
                        g_warning ("%s: %s", G_STRFUNC, error->message);
                        g_error_free (error);
                }
@@ -1298,8 +1013,8 @@ create_phone_indexes_for_tables (gpointer data,
        info->table = cols[0];
        stmt = sqlite3_mprintf ("PRAGMA table_info(%Q)", info->table);
 
-       if (!book_backend_sql_exec (
-               info->db, stmt, create_phone_indexes_for_columns, info, &error)) {
+       if (!book_backend_sqlitedb_exec (
+               info->ebsdb, stmt, create_phone_indexes_for_columns, info, &error)) {
                g_warning ("%s: %s", G_STRFUNC, error->message);
                g_clear_error (&error);
        }
@@ -1309,8 +1024,8 @@ create_phone_indexes_for_tables (gpointer data,
        info->table = tmp = g_strconcat (info->table, "_lists", NULL);
        stmt = sqlite3_mprintf ("PRAGMA table_info(%Q)", info->table);
 
-       if (!book_backend_sql_exec (
-               info->db, stmt, create_phone_indexes_for_columns, info, &error)) {
+       if (!book_backend_sqlitedb_exec (
+               info->ebsdb, stmt, create_phone_indexes_for_columns, info, &error)) {
                g_warning ("%s: %s", G_STRFUNC, error->message);
                g_clear_error (&error);
        }
@@ -1424,7 +1139,7 @@ ixphone_compare_national (gpointer data,
                }
        }
 
-       if (booksql_debug ()) {
+       if (book_backend_sqlitedb_debug_level ()) {
                gchar *const tmp1 = g_strndup (str1, len1);
                gchar *const tmp2 = g_strndup (str2, len2);
 
@@ -1447,6 +1162,7 @@ create_collation (gpointer data,
 {
        gint ret = SQLITE_DONE;
        gint country_code;
+       EBookBackendSqliteDB *ebsdb = (EBookBackendSqliteDB *)data;
 
        g_warn_if_fail (encoding == SQLITE_UTF8);
 
@@ -1464,11 +1180,11 @@ create_collation (gpointer data,
        }
 
        if (ret == SQLITE_OK) {
-               CollationInfo info = { db, name };
+               CollationInfo info = { ebsdb, name };
                GError *error = NULL;
 
-               if (!book_backend_sql_exec (
-                       db, "SELECT folder_id FROM folders",
+               if (!book_backend_sqlitedb_exec (
+                       ebsdb, "SELECT folder_id FROM folders",
                        create_phone_indexes_for_tables, &info, &error)) {
                        g_warning ("%s(%s): %s", G_STRFUNC, name, error->message);
                        g_error_free (error);
@@ -1478,6 +1194,9 @@ create_collation (gpointer data,
        }
 }
 
+/******************************************************
+ *         Implementation for REGEX keyword           *
+ ******************************************************/
 static void
 ebsdb_regexp (sqlite3_context *context,
               gint argc,
@@ -1521,11 +1240,105 @@ ebsdb_regexp (sqlite3_context *context,
        }
 }
 
+/**********************************************************
+ *  Schema Loading / Introspecing / Creating / Upgrating  *
+ **********************************************************
+ *
+ * Sequence of opening the SQLite and creating / upgrading the schema
+ *
+ * NOTE: This plan assumes that the _localized and _translit auxiliary columns
+ * are configurable via new index types in the ESourceBackendSummarySetup API.
+ *
+ * a.) Collect summary fields and index types from the ESourceBackendSummarySetup.
+ *
+ * b.) Open the SQLite.
+ *
+ *     In this stage setup various preferences, case insensitive LIKE statements,
+ *     WAL journaling mode, install REGEX handler, etc.
+ *
+ * c.) Initialize main tables
+ *
+ *     Create main table and add any missing columns which may have been introduced in
+ *     newer versions of the schema
+ *
+ * d.) Introspect current summary.
+ *
+ *     If the summary table exists we know there is already data, at this point
+ *     we override any configuration collected from the ESourceBackendSummarySetup,
+ *     currently we only support setting up the summary configuration once (this might change
+ *     to be more flexible in the future).
+ *
+ *     Furthermore, at this stage we need to collect any auxiliary columns which actually
+ *     exist, we prefer this to upgrading those based on the existing schema.
+ *
+ *     This step will also provide us with the currently set schema version number, if available
+ *
+ * e.) Build / upgrade the schema
+ *
+ *     e1.)
+ *
+ *     If the main table did not previously exist, try to build it in a single statement
+ *     and then create the appropriate indexes in other statements.
+ *
+ *     e2.)
+ *
+ *     Otherwise, if the schema version is found to be less than the current version, perform
+ *     upgrades.
+ *
+ *     On the main table there are a few columns to be added for various version upgrades.
+ *
+ *     Also, identify at this point which summary fields were added to the defaults
+ *     since the last version, these must be added to the SummaryField array and also
+ *     added to the schema.
+ *
+ *     This is specifically important for migration of the email_[1-4] columns as well
+ *     as auxiliary columns required for normal functionality of the cursor (i.e. the _translit column).
+ *
+ * f.) Record any changes that were made to the multi-value summary field record, i.e. save any
+ *     data that will be required for the next summary introspection phase (next time someone opens
+ *     the SQLite for this folder).
+ */
+
+static inline gint
+main_table_index_by_name (const gchar *name)
+{
+       gint i;
+
+       for (i = 0; i < G_N_ELEMENTS (main_table_columns); i++) {
+               if (g_strcmp0 (name, main_table_columns[i].name) == 0)
+                       return i;
+       }
+
+       return -1;
+}
+
+static gint
+check_main_table_columns (gpointer data,
+                         gint n_cols,
+                         gchar **cols,
+                         gchar **name)
+{
+       guint *columns_mask = (guint *)data;
+       gint i;
+
+       for (i = 0; i < n_cols; i++) {
+
+               if (g_strcmp0 (name[i], "name") == 0) {
+                       gint index = main_table_index_by_name (cols[i]);
+
+                       if (index >= 0)
+                               *columns_mask |= (1 << index);
+
+                       break;
+               }
+       }
+       return 0;
+}
+
 static gboolean
-book_backend_sqlitedb_load (EBookBackendSqliteDB *ebsdb,
-                            const gchar *filename,
-                            gint *previous_schema,
-                            GError **error)
+book_backend_sqlitedb_init_sqlite (EBookBackendSqliteDB *ebsdb,
+                                  const gchar *filename,
+                                  GError **error)
 {
        gint ret;
 
@@ -1533,6 +1346,7 @@ book_backend_sqlitedb_load (EBookBackendSqliteDB *ebsdb,
 
        ret = sqlite3_open (filename, &ebsdb->priv->db);
 
+       /* XXX Remove me... old phone number stuff */
        if (ret == SQLITE_OK)
                ret = sqlite3_collation_needed (ebsdb->priv->db, ebsdb, create_collation);
 
@@ -1550,7 +1364,7 @@ book_backend_sqlitedb_load (EBookBackendSqliteDB *ebsdb,
                } else {
                        const gchar *errmsg;
                        errmsg = sqlite3_errmsg (ebsdb->priv->db);
-                       d (g_printerr ("Can't open database %s: %s\n", path, errmsg));
+                       d (g_printerr ("Can't open database %s: %s\n", filename, errmsg));
                        g_set_error_literal (
                                error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER, errmsg);
                        sqlite3_close (ebsdb->priv->db);
@@ -1558,473 +1372,726 @@ book_backend_sqlitedb_load (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       book_backend_sql_exec (
-               ebsdb->priv->db,
-               "ATTACH DATABASE ':memory:' AS mem",
-               NULL, NULL, NULL);
-       book_backend_sql_exec (
-               ebsdb->priv->db,
-               "PRAGMA foreign_keys = ON",
-               NULL, NULL, NULL);
-       book_backend_sql_exec (
-               ebsdb->priv->db,
-               "PRAGMA case_sensitive_like = ON",
-               NULL, NULL, NULL);
+       book_backend_sqlitedb_exec (ebsdb, "ATTACH DATABASE ':memory:' AS mem",
+                                   NULL, NULL, NULL);
+       book_backend_sqlitedb_exec (ebsdb, "PRAGMA foreign_keys = ON",
+                                   NULL, NULL, NULL);
+       book_backend_sqlitedb_exec (ebsdb, "PRAGMA case_sensitive_like = ON",
+                                   NULL, NULL, NULL);
 
-       return create_folders_table (ebsdb, previous_schema, error);
+       return TRUE;
 }
 
-static EBookBackendSqliteDB *
-e_book_backend_sqlitedb_new_internal (const gchar *path,
-                                      const gchar *emailid,
-                                      const gchar *folderid,
-                                      const gchar *folder_name,
-                                      gboolean store_vcard,
-                                      SummaryField *fields,
-                                      gint n_fields,
-                                      gboolean have_attr_list,
-                                      IndexFlags attr_list_indexes,
-                                      GError **error)
+static gboolean
+book_backend_sqlitedb_init_folders (EBookBackendSqliteDB *ebsdb,
+                                   gint *previous_schema,
+                                   GError **error)
 {
-       EBookBackendSqliteDB *ebsdb;
-       gchar *hash_key, *filename;
-       gint previous_schema = 0;
-       gboolean already_exists = FALSE;
+       GString *string;
+       gint version = 0, i;
+       guint existing_columns_mask = 0;
+       gboolean success;
 
-       g_return_val_if_fail (path != NULL, NULL);
-       g_return_val_if_fail (emailid != NULL, NULL);
-       g_return_val_if_fail (folderid != NULL, NULL);
-       g_return_val_if_fail (folder_name != NULL, NULL);
+       if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
+               return FALSE;
 
-       g_mutex_lock (&dbcon_lock);
+       string = g_string_sized_new (MAIN_TABLE_ENTRY_BYTES * G_N_ELEMENTS (main_table_columns));
+       g_string_append (string, "CREATE TABLE IF NOT EXISTS folders (");
+       for (i = 0; i < G_N_ELEMENTS (main_table_columns); i++) {
 
-       hash_key = g_strdup_printf ("%s %s", emailid, path);
-       if (db_connections != NULL) {
-               ebsdb = g_hash_table_lookup (db_connections, hash_key);
+               if (i > 0)
+                       g_string_append (string, ", ");
+
+               g_string_append (string, main_table_columns[i].name);
+               g_string_append_c (string, ' ');
+
+               g_string_append (string, main_table_columns[i].type);
 
-               if (ebsdb) {
-                       g_object_ref (ebsdb);
-                       g_free (hash_key);
-                       goto exit;
+               if (main_table_columns[i].extra) {
+                       g_string_append_c (string, ' ');
+                       g_string_append (string, main_table_columns[i].extra);
                }
        }
+       g_string_append_c (string, ')');
 
-       ebsdb = g_object_new (E_TYPE_BOOK_BACKEND_SQLITEDB, NULL);
-       ebsdb->priv->path = g_strdup (path);
-       ebsdb->priv->summary_fields = fields;
-       ebsdb->priv->n_summary_fields = n_fields;
-       ebsdb->priv->have_attr_list = have_attr_list;
-       ebsdb->priv->attr_list_indexes = attr_list_indexes;
-       ebsdb->priv->store_vcard = store_vcard;
+       /* Create main folders table */
+       success = book_backend_sqlitedb_exec (ebsdb,
+                                             string->str,
+                                             NULL, NULL, error);
+       g_string_free (string, TRUE);
+       if (!success)
+               goto rollback;
 
-       if (g_mkdir_with_parents (path, 0777) < 0) {
-               g_mutex_unlock (&dbcon_lock);
-               g_object_unref (ebsdb);
-               g_set_error (
-                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
-                       "Can not make parent directory: errno %d", errno);
-               return NULL;
-       }
-       filename = g_build_filename (path, DB_FILENAME, NULL);
+       /* Fetch the version, it should be the same for all folders (hence the LIMIT). */
+       if (!book_backend_sqlitedb_exec (
+               ebsdb, "SELECT version FROM folders LIMIT 1",
+               get_int_cb, &version, error))
+               goto rollback;
 
-       if (!book_backend_sqlitedb_load (ebsdb, filename, &previous_schema, error)) {
-               g_mutex_unlock (&dbcon_lock);
-               g_object_unref (ebsdb);
-               g_free (filename);
-               return NULL;
-       }
-       g_free (filename);
+       /* Check which columns in the main table already exist */
+       if (!book_backend_sqlitedb_exec (
+               ebsdb, "PRAGMA table_info (folders)",
+               check_main_table_columns, &existing_columns_mask, error))
+               goto rollback;
 
-       if (db_connections == NULL)
-               db_connections = g_hash_table_new_full (
-                       (GHashFunc) g_str_hash,
-                       (GEqualFunc) g_str_equal,
-                       (GDestroyNotify) g_free,
-                       (GDestroyNotify) NULL);
-       g_hash_table_insert (db_connections, hash_key, ebsdb);
-       ebsdb->priv->hash_key = g_strdup (hash_key);
+       /* Add columns which may be missing */
+       for (i = 0; i < G_N_ELEMENTS (main_table_columns); i++) {
+               ColumnInfo *info = &(main_table_columns[i]);
 
- exit:
-       /* While the global dbcon_lock is held, hold the ebsdb specific lock and
-        * prolong the locking on that instance until the folders are all set up
+               if ((existing_columns_mask & (1 << i)) != 0)
+                       continue;
+
+               if (!book_backend_sqlitedb_exec_printf (
+                       ebsdb, "ALTER TABLE folders ADD COLUMN %s %s %s",
+                       NULL, NULL, error, info->name, info->type,
+                       info->extra ? info->extra : ""))
+                       goto rollback;
+       }
+
+       /* Special case upgrade for schema versions 3 & 4.
+        * 
+        * Drops the reverse_multivalues column.
         */
-       LOCK_MUTEX (&ebsdb->priv->lock);
-       g_mutex_unlock (&dbcon_lock);
+       if (version >= 3 && version < 5) {
 
-       if (!add_folder_into_db (ebsdb, folderid, folder_name,
-                                &already_exists, error)) {
-               UNLOCK_MUTEX (&ebsdb->priv->lock);
-               g_object_unref (ebsdb);
-               return NULL;
+               if (!book_backend_sqlitedb_exec (
+                       ebsdb, 
+                       "UPDATE folders SET "
+                               "multivalues = REPLACE(RTRIM(REPLACE("
+                                       "multivalues || ':', ':', "
+                                       "CASE reverse_multivalues "
+                                               "WHEN 0 THEN ';prefix ' "
+                                               "ELSE ';prefix;suffix ' "
+                                       "END)), ' ', ':'), "
+                               "reverse_multivalues = NULL",
+                       NULL, NULL, error))
+                       goto rollback;
        }
 
-       if (!create_contacts_table (ebsdb, folderid, previous_schema, already_exists, error)) {
-               UNLOCK_MUTEX (&ebsdb->priv->lock);
-               g_object_unref (ebsdb);
-               return NULL;
+       /* Finish the eventual upgrade by storing the current schema version.
+        */
+       if (version >= 1 && version < FOLDER_VERSION) {
+
+               if (!book_backend_sqlitedb_exec_printf (
+                       ebsdb, "UPDATE folders SET version = %d",
+                       NULL, NULL, error, FOLDER_VERSION))
+                       goto rollback;
        }
 
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
+       /* Remember the schema version for later use and finish the transaction. */
+       *previous_schema = version;
+       return book_backend_sqlitedb_commit_transaction (ebsdb, error);
 
-       return ebsdb;
+ rollback:
+       /* The GError is already set. */
+       book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+
+       *previous_schema = 0;
+       return FALSE;
 }
 
-static SummaryField *
-append_summary_field (GArray *array,
-                      EContactField field,
-                      gboolean *have_attr_list,
-                      GError **error)
+static gboolean
+book_backend_sqlitedb_init_keys (EBookBackendSqliteDB *ebsdb,
+                                GError **error)
 {
-       const gchar *dbname = NULL;
-       GType        type = G_TYPE_INVALID;
-       gint         i;
-       SummaryField new_field = { 0, };
+       if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
+               return FALSE;
 
-       if (field < 1 || field >= E_CONTACT_FIELD_LAST) {
-               g_set_error (
-                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
-                       _("Invalid contact field '%d' specified in summary"), field);
-               return NULL;
-       }
+       /* Create a child table to store key/value pairs for a folder. */
+       if (!book_backend_sqlitedb_exec (
+               ebsdb, 
+               "CREATE TABLE IF NOT EXISTS keys ("
+               " key TEXT PRIMARY KEY,"
+               " value TEXT,"
+               " folder_id TEXT REFERENCES folders)",
+               NULL, NULL, error))
+               goto rollback;
 
-       /* Avoid including the same field twice in the summary */
-       for (i = 0; i < array->len; i++) {
-               SummaryField *iter = &g_array_index (array, SummaryField, i);
-               if (field == iter->field)
-                       return iter;
-       }
+       /* Add an index on the keys */
+       if (!book_backend_sqlitedb_exec (
+               ebsdb, 
+               "CREATE INDEX IF NOT EXISTS keysindex ON keys (folder_id)",
+               NULL, NULL, error))
+               goto rollback;
 
-       /* Resolve some exceptions, we store these
-        * specific contact fields with different names
-        * than those found in the EContactField table
-        */
-       switch (field) {
-       case E_CONTACT_UID:
-               dbname = "uid";
-               break;
-       case E_CONTACT_IS_LIST:
-               dbname = "is_list";
-               break;
-       default:
-               dbname = e_contact_field_name (field);
-               break;
+       return book_backend_sqlitedb_commit_transaction (ebsdb, error);
+
+ rollback:
+       /* The GError is already set. */
+       book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+
+       return FALSE;
+}
+
+static gchar *
+format_multivalues (EBookBackendSqliteDB *ebsdb)
+{
+       gint i;
+       GString *string;
+       gboolean first = TRUE;
+
+       string = g_string_new (NULL);
+
+       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+               if (ebsdb->priv->summary_fields[i].type == E_TYPE_CONTACT_ATTR_LIST) {
+                       if (first)
+                               first = FALSE;
+                       else
+                               g_string_append_c (string, ':');
+
+                       g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
+
+                       /* E_BOOK_INDEX_SORT_KEY is not supported in the multivalue fields */
+                       if ((ebsdb->priv->summary_fields[i].index & INDEX_FLAG (PREFIX)) != 0)
+                               g_string_append (string, ";prefix");
+                       if ((ebsdb->priv->summary_fields[i].index & INDEX_FLAG (SUFFIX)) != 0)
+                               g_string_append (string, ";suffix");
+                       if ((ebsdb->priv->summary_fields[i].index & INDEX_FLAG (PHONE)) != 0)
+                               g_string_append (string, ";phone");
+               }
        }
 
-       type = e_contact_field_type (field);
+       return g_string_free (string, FALSE);
+}
 
-       if (type != G_TYPE_STRING &&
-           type != G_TYPE_BOOLEAN &&
-           type != E_TYPE_CONTACT_ATTR_LIST) {
-               g_set_error (
-                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
-                       _("Contact field '%s' of type '%s' specified in summary, "
-                       "but only boolean, string and string list field types are supported"),
-                       e_contact_pretty_name (field), g_type_name (type));
-               return NULL;
+static gboolean
+book_backend_sqlitedb_add_folder (EBookBackendSqliteDB *ebsdb,
+                                 const gchar *folderid,
+                                 const gchar *folder_name,
+                                 gboolean *already_exists,
+                                 GError **error)
+{
+       gboolean success;
+       gchar *multivalues;
+       gint count = 0;
+
+       if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
+               return FALSE;
+
+       /* Check if this folder is already declared in the main folders table */
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, 
+               "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=%Q;",
+               get_count_cb, &count, error, folderid);
+       if (!success)
+               goto rollback;
+
+       if (count == 0) {
+               const gchar *lc_collate;
+
+               multivalues = format_multivalues (ebsdb);
+               lc_collate = setlocale (LC_COLLATE, NULL);
+
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb,
+                       "INSERT OR IGNORE INTO folders"
+                       " ( folder_id, folder_name, version, multivalues, lc_collate ) "
+                       "VALUES ( %Q, %Q, %d, %Q, %Q ) ",
+                       NULL, NULL, error,
+                       folderid, folder_name, FOLDER_VERSION, multivalues, lc_collate);
+
+               g_free (multivalues);
+
+               if (!success)
+                       goto rollback;
        }
 
-       if (type == E_TYPE_CONTACT_ATTR_LIST && have_attr_list)
-               *have_attr_list = TRUE;
+       if (already_exists)
+               *already_exists = (count > 0);
 
-       new_field.field  = field;
-       new_field.dbname = dbname;
-       new_field.type   = type;
-       new_field.index  = 0;
-       g_array_append_val (array, new_field);
+       return book_backend_sqlitedb_commit_transaction (ebsdb, error);
 
-       return &g_array_index (array, SummaryField, array->len - 1);
+rollback:
+       book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+
+       return FALSE;
 }
 
-static void
-summary_fields_add_indexes (GArray *array,
-                            EContactField *indexes,
-                            EBookIndexType *index_types,
-                            gint n_indexes,
-                            IndexFlags *attr_list_indexes)
+static gboolean
+book_backend_sqlitedb_introspect_summary (EBookBackendSqliteDB *ebsdb,
+                                         const gchar *folderid,
+                                         gint previous_schema,
+                                         GSList **introspected_columns,
+                                         GError **error)
 {
+       gboolean success, have_attr_list;
+       GSList *summary_columns = NULL, *l;
+       GArray *summary_fields = NULL;
+       gchar *multivalues = NULL;
        gint i, j;
 
-       for (i = 0; i < array->len; i++) {
-               SummaryField *sfield = &g_array_index (array, SummaryField, i);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "PRAGMA table_info (%Q);",
+               get_columns_cb, &summary_columns, error, folderid);
 
-               for (j = 0; j < n_indexes; j++) {
-                       if (sfield->field == indexes[j]) {
-                               switch (index_types[j]) {
-                               case E_BOOK_INDEX_PREFIX:
-                                       sfield->index |= INDEX_PREFIX;
+       if (!success)
+               goto introspect_summary_finish;
 
-                                       if (sfield->type == E_TYPE_CONTACT_ATTR_LIST)
-                                               *attr_list_indexes |= INDEX_PREFIX;
-                                       break;
-                               case E_BOOK_INDEX_SUFFIX:
-                                       sfield->index |= INDEX_SUFFIX;
+       summary_columns = g_slist_reverse (summary_columns);
+       summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
 
-                                       if (sfield->type == E_TYPE_CONTACT_ATTR_LIST)
-                                               *attr_list_indexes |= INDEX_SUFFIX;
-                                       break;
-                               case E_BOOK_INDEX_PHONE:
-                                       sfield->index |= INDEX_PHONE;
+       /* Introspect the normal summary fields */
+       for (l = summary_columns; l; l = l->next) {
+               EContactField field;
+               const gchar *col = l->data;
+               gchar *p;
+               gint computed = 0;
+               gchar *freeme = NULL;
+
+               /* Note that we don't have any way to introspect
+                * E_BOOK_INDEX_PREFIX, this is not important because if
+                * the prefix index is specified, it will be created
+                * the first time the SQLite tables are created, so
+                * it's not important to ensure prefix indexes after
+                * introspecting the summary.
+                */
 
-                                       if (sfield->type == E_TYPE_CONTACT_ATTR_LIST)
-                                               *attr_list_indexes |= INDEX_PHONE;
-                                       break;
-                               default:
-                                       g_warn_if_reached ();
+               /* Check if we're parsing a reverse field */
+               if ((p = strstr (col, "_reverse")) != NULL) {
+                       computed = INDEX_FLAG (SUFFIX);
+                       freeme   = g_strndup (col, p - col);
+                       col      = freeme;
+               } else if ((p = strstr (col, "_phone")) != NULL) {
+                       computed = INDEX_FLAG (PHONE);
+                       freeme   = g_strndup (col, p - col);
+                       col      = freeme;
+               } else if ((p = strstr (col, "_localized")) != NULL) {
+                       computed = INDEX_FLAG (SORT_KEY);
+                       freeme   = g_strndup (col, p - col);
+                       col      = freeme;
+               }
+
+               /* First check exception fields */
+               if (g_ascii_strcasecmp (col, "uid") == 0)
+                       field = E_CONTACT_UID;
+               else if (g_ascii_strcasecmp (col, "is_list") == 0)
+                       field = E_CONTACT_IS_LIST;
+               else
+                       field = e_contact_field_id (col);
+
+               /* Check for parse error */
+               if (field == 0) {
+                       g_set_error (
+                               error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+                               _("Error introspecting unknown summary field '%s'"), col);
+                       success = FALSE;
+                       g_free (freeme);
+                       break;
+               }
+
+               /* Computed columns are always declared after the normal columns,
+                * if a reverse field is encountered we need to set the suffix
+                * index on the coresponding summary field
+                */
+               if (computed) {
+                       for (i = 0; i < summary_fields->len; i++) {
+                               SummaryField *iter = &g_array_index (summary_fields, SummaryField, i);
+
+                               if (iter->field == field) {
+                                       iter->index |= computed;
                                        break;
                                }
                        }
+               } else {
+                       summary_field_append (summary_fields, field, NULL, NULL);
                }
+
+               g_free (freeme);
        }
-}
 
-/**
- * e_book_backend_sqlitedb_new_full:
- * @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.
- * @setup: an #ESourceBackendSummarySetup describing how the summary should be setup
- * @error: A location to store any error that may have occurred
- *
- * Like e_book_backend_sqlitedb_new(), but allows configuration of which contact fields
- * will be stored for quick reference in the summary. The configuration indicated by
- * @setup will only be taken into account when initially creating the underlying table,
- * further configurations will be ignored.
- *
- * The fields %E_CONTACT_UID and %E_CONTACT_REV are not optional,
- * they will be stored in the summary regardless of this function's parameters
- *
- * <note><para>Only #EContactFields with the type #G_TYPE_STRING, #G_TYPE_BOOLEAN or
- * #E_TYPE_CONTACT_ATTR_LIST are currently supported.</para></note>
- *
- * Returns: (transfer full): The newly created #EBookBackendSqliteDB
- *
- * Since: 3.8
- **/
-EBookBackendSqliteDB *
-e_book_backend_sqlitedb_new_full (const gchar *path,
-                                  const gchar *emailid,
-                                  const gchar *folderid,
-                                  const gchar *folder_name,
-                                  gboolean store_vcard,
-                                  ESourceBackendSummarySetup *setup,
-                                  GError **error)
-{
-       EBookBackendSqliteDB *ebsdb = NULL;
-       EContactField *fields;
-       EContactField *indexed_fields;
-       EBookIndexType *index_types = NULL;
-       gboolean have_attr_list = FALSE;
-       IndexFlags attr_list_indexes = 0;
-       gboolean had_error = FALSE;
-       GArray *summary_fields;
-       gint n_fields = 0, n_indexed_fields = 0, i;
+       if (!success)
+               goto introspect_summary_finish;
 
-       fields         = e_source_backend_summary_setup_get_summary_fields (setup, &n_fields);
-       indexed_fields = e_source_backend_summary_setup_get_indexed_fields (setup, &index_types, 
&n_indexed_fields);
+       /* Introspect the multivalied summary fields */
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb,
+               "SELECT multivalues FROM folders WHERE folder_id = %Q",
+               get_string_cb, &multivalues, error, folderid);
 
-       /* No specified summary fields indicates the default summary configuration should be used */
-       if (n_fields <= 0) {
-               ebsdb = e_book_backend_sqlitedb_new (path, emailid, folderid, folder_name, store_vcard, 
error);
-               g_free (fields);
-               g_free (index_types);
-               g_free (indexed_fields);
+       if (!success)
+               goto introspect_summary_finish;
 
-               return ebsdb;
-       }
+       ebsdb->priv->attr_list_indexes = 0;
+       ebsdb->priv->have_attr_list = have_attr_list = FALSE;
 
-       summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
+       if (multivalues) {
+               gchar **fields = g_strsplit (multivalues, ":", 0);
 
-       /* Ensure the non-optional fields first */
-       append_summary_field (summary_fields, E_CONTACT_UID, &have_attr_list, error);
-       append_summary_field (summary_fields, E_CONTACT_REV, &have_attr_list, error);
+               for (i = 0; fields[i] != NULL; i++) {
+                       EContactField field;
+                       SummaryField *iter;
+                       gchar **params;
 
-       for (i = 0; i < n_fields; i++) {
-               if (!append_summary_field (summary_fields, fields[i], &have_attr_list, error)) {
-                       had_error = TRUE;
-                       break;
+                       params = g_strsplit (fields[i], ";", 0);
+                       field = e_contact_field_id (params[0]);
+                       iter = summary_field_append (summary_fields, field, &have_attr_list, NULL);
+
+                       if (iter) {
+                               for (j = 1; params[j]; ++j) {
+                                       /* Sort keys not supported for multivalued fields */
+                                       if (strcmp (params[j], "prefix") == 0) {
+                                               iter->index |= INDEX_FLAG (PREFIX);
+                                       } else if (strcmp (params[j], "suffix") == 0) {
+                                               iter->index |= INDEX_FLAG (SUFFIX);
+                                       } else if (strcmp (params[j], "phone") == 0) {
+                                               iter->index |= INDEX_FLAG (PHONE);
+                                       }
+                               }
+
+                               ebsdb->priv->attr_list_indexes |= iter->index;
+                       }
+
+                       g_strfreev (params);
                }
+
+               ebsdb->priv->have_attr_list = have_attr_list;
+
+               g_strfreev (fields);
        }
 
-       if (had_error) {
-               g_array_free (summary_fields, TRUE);
-               g_free (fields);
-               g_free (index_types);
-               g_free (indexed_fields);
-               return NULL;
+       /* HARD CODE UP AHEAD
+        *
+        * Now we're finished introspecting, if the summary is from a previous version,
+        * we need to add any summary fields which we're added to the default summary
+        * since the schema version which was introduced here
+        */
+       if (previous_schema >= 1) {
+               SummaryField *summary_field;
+
+               /* XXX We have to take care of the localized columns too now
+                *
+                * By default in schema version 7, we added localized columns for
+                * every G_TYPE_STRING field
+                */
+               if (previous_schema < 8) {
+
+                       /* We used to keep 4 email fields in the summary, before we supported
+                        * the multivaliued E_CONTACT_EMAIL... convert the old summary to use
+                        * the multivaliued field instead.
+                        */
+                       if (summary_field_array_index (summary_fields, E_CONTACT_EMAIL_1) >= 0 &&
+                           summary_field_array_index (summary_fields, E_CONTACT_EMAIL_2) >= 0 &&
+                           summary_field_array_index (summary_fields, E_CONTACT_EMAIL_3) >= 0 &&
+                           summary_field_array_index (summary_fields, E_CONTACT_EMAIL_4) >= 0) {
+
+                               summary_field_remove (summary_fields, E_CONTACT_EMAIL_1);
+                               summary_field_remove (summary_fields, E_CONTACT_EMAIL_2);
+                               summary_field_remove (summary_fields, E_CONTACT_EMAIL_3);
+                               summary_field_remove (summary_fields, E_CONTACT_EMAIL_4);
+
+                               summary_field = summary_field_append (summary_fields, E_CONTACT_EMAIL, 
&have_attr_list, NULL);
+                               summary_field->index |= INDEX_FLAG (PREFIX);
+
+                               ebsdb->priv->have_attr_list = TRUE;
+                               ebsdb->priv->attr_list_indexes |= INDEX_FLAG (PREFIX);
+                       }
+               }
        }
 
-       /* Add the 'indexed' flag to the SummaryField structs */
-       summary_fields_add_indexes (
-               summary_fields, indexed_fields, index_types, n_indexed_fields,
-               &attr_list_indexes);
+ introspect_summary_finish:
 
-       ebsdb = e_book_backend_sqlitedb_new_internal (
-               path, emailid, folderid, folder_name,
-               store_vcard,
-               (SummaryField *) summary_fields->data,
-               summary_fields->len,
-               have_attr_list,
-               attr_list_indexes,
-               error);
+       /* Apply the introspected summary fields */
+       if (success) {
+               g_free (ebsdb->priv->summary_fields);
+               ebsdb->priv->n_summary_fields = summary_fields->len;
+               ebsdb->priv->summary_fields = (SummaryField *) g_array_free (summary_fields, FALSE);
 
-       g_free (fields);
-       g_free (index_types);
-       g_free (indexed_fields);
-       g_array_free (summary_fields, FALSE);
+               *introspected_columns = summary_columns;
+       } else if (summary_fields) {
+               g_array_free (summary_fields, TRUE);
+               g_slist_free_full (summary_columns, (GDestroyNotify) g_free);
+       }
 
-       return ebsdb;
+       g_free (multivalues);
+
+       return success;
 }
 
-/**
- * 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:
- *
- * Since: 3.2
- **/
-EBookBackendSqliteDB *
-e_book_backend_sqlitedb_new (const gchar *path,
-                             const gchar *emailid,
-                             const gchar *folderid,
-                             const gchar *folder_name,
-                             gboolean store_vcard,
-                             GError **error)
+static gboolean
+book_backend_sqlitedb_init_contacts (EBookBackendSqliteDB *ebsdb,
+                                    const gchar *folderid,
+                                    GSList *introspected_columns,
+                                    GError **error)
 {
-       EBookBackendSqliteDB *ebsdb;
-       GArray *summary_fields;
-       gboolean have_attr_list = FALSE;
-       IndexFlags attr_list_indexes = 0;
        gint i;
+       gboolean success = TRUE;
+       GString *string;
+       GSList *summary_columns = NULL, *l;
 
-       /* Create the default summary structs */
-       summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
-       for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++)
-               append_summary_field (summary_fields, default_summary_fields[i], &have_attr_list, NULL);
+       /* Get a list of all columns and indexes which should be present
+        * in the main summary table */
+       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+               SummaryField *field = &(ebsdb->priv->summary_fields[i]);
 
-       /* Add the default index flags */
-       summary_fields_add_indexes (
-               summary_fields,
-               default_indexed_fields,
-               default_index_types,
-               G_N_ELEMENTS (default_indexed_fields),
-               &attr_list_indexes);
+               l = summary_field_list_main_columns (field, folderid);
+               summary_columns = g_slist_concat (summary_columns, l);
+       }
 
-       ebsdb = e_book_backend_sqlitedb_new_internal (
-               path, emailid, folderid, folder_name,
-               store_vcard,
-               (SummaryField *) summary_fields->data,
-               summary_fields->len,
-               have_attr_list,
-               attr_list_indexes,
-               error);
+       /* Create the main contacts table for this folder
+        */
+       string = g_string_sized_new (32 * g_slist_length (summary_columns));
+       g_string_append (string, "CREATE TABLE IF NOT EXISTS %Q (");
 
-       g_array_free (summary_fields, FALSE);
+       for (l = summary_columns; l; l = l->next) {
+               ColumnInfo *info = l->data;
 
-       return ebsdb;
-}
+               if (l != summary_columns)
+                       g_string_append (string, ", ");
 
-gboolean
-e_book_backend_sqlitedb_lock_updates (EBookBackendSqliteDB *ebsdb,
-                                      GError **error)
-{
-       gboolean success;
+               g_string_append (string, info->name);
+               g_string_append_c (string, ' ');
 
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+               g_string_append (string, info->type);
 
-       LOCK_MUTEX (&ebsdb->priv->updates_lock);
+               if (info->extra) {
+                       g_string_append_c (string, ' ');
+                       g_string_append (string, info->extra);
+               }
+       }
+       g_string_append (string, ", vcard TEXT, bdata TEXT)");
 
-       LOCK_MUTEX (&ebsdb->priv->lock);
-       success = book_backend_sqlitedb_start_transaction (ebsdb, error);
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, string->str,
+               NULL, NULL, error, folderid);
+
+       g_string_free (string, TRUE);
+
+       /* If we introspected something, let's first adjust the contacts table
+        * so that it includes the right columns */
+       if (introspected_columns) {
+
+               /* Add any missing columns which are in the summary fields but
+                * not found in the contacts table
+                */
+               for (l = summary_columns; success && l; l = l->next) {
+                       ColumnInfo *info = l->data;
+
+                       if (g_slist_find_custom (introspected_columns,
+                                                info->name, (GCompareFunc)g_ascii_strcasecmp))
+                               continue;
+
+                       success = book_backend_sqlitedb_exec_printf (
+                               ebsdb, "ALTER TABLE %Q ADD COLUMN %s %s %s",
+                               NULL, NULL, error, folderid, info->name, info->type,
+                               info->extra ? info->extra : "");
+               }
+       }
+
+       /* Add indexes to columns in the main contacts table
+        */
+       for (l = summary_columns; success && l; l = l->next) {
+               ColumnInfo *info = l->data;
+
+               if (!info->index)
+                       continue;
+
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb, "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)",
+                       NULL, NULL, error, info->index, folderid, info->name);
+       }
+
+       g_slist_free_full (summary_columns, (GDestroyNotify)column_info_free);
 
        return success;
 }
 
-gboolean
-e_book_backend_sqlitedb_unlock_updates (EBookBackendSqliteDB *ebsdb,
-                                        gboolean do_commit,
-                                        GError **error)
+static gboolean
+book_backend_sqlitedb_init_aux_tables (EBookBackendSqliteDB *ebsdb,
+                                      const gchar *folderid,
+                                      GError **error)
 {
-       gboolean success;
+       GString *string;
+       gboolean success = TRUE;
+       gchar *stmt, *tmp;
 
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+       /* Construct the create statement from the attribute list summary table */
+       if (ebsdb->priv->have_attr_list) {
+               string = g_string_new ("CREATE TABLE IF NOT EXISTS %Q ( uid TEXT NOT NULL REFERENCES %Q(uid), 
"
+                       "field TEXT, value TEXT");
 
-       LOCK_MUTEX (&ebsdb->priv->lock);
-       success = do_commit ?
-               book_backend_sqlitedb_commit_transaction (ebsdb, error) :
-               book_backend_sqlitedb_rollback_transaction (ebsdb, error);
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
+               if ((ebsdb->priv->attr_list_indexes & INDEX_FLAG (SUFFIX)) != 0)
+                       g_string_append (string, ", value_reverse TEXT");
+               if ((ebsdb->priv->attr_list_indexes & INDEX_FLAG (PHONE)) != 0)
+                       g_string_append (string, ", value_phone TEXT");
 
-       UNLOCK_MUTEX (&ebsdb->priv->updates_lock);
+               g_string_append_c (string, ')');
+
+               tmp = g_strdup_printf ("%s_lists", folderid);
+               stmt = sqlite3_mprintf (string->str, tmp, folderid);
+               g_string_free (string, TRUE);
+
+               success = book_backend_sqlitedb_exec (ebsdb, stmt, NULL, NULL, error);
+               sqlite3_free (stmt);
+
+               /* Give the UID an index in this table, always */
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb, "CREATE INDEX IF NOT EXISTS LISTINDEX ON %Q (uid)",
+                       NULL, NULL, error, tmp);
+
+               /* Create indexes if specified */
+               if (success && (ebsdb->priv->attr_list_indexes & INDEX_FLAG (PREFIX)) != 0) {
+                       success = book_backend_sqlitedb_exec_printf (
+                               ebsdb, "CREATE INDEX IF NOT EXISTS VALINDEX ON %Q (value)",
+                               NULL, NULL, error, tmp);
+               }
+
+               if (success && (ebsdb->priv->attr_list_indexes & INDEX_FLAG (SUFFIX)) != 0) {
+                       success = book_backend_sqlitedb_exec_printf (
+                               ebsdb, "CREATE INDEX IF NOT EXISTS RVALINDEX ON %Q (value_reverse)",
+                               NULL, NULL, error, tmp);
+               }
+
+               if (success && (ebsdb->priv->attr_list_indexes & INDEX_FLAG (PHONE)) != 0) {
+                       success = book_backend_sqlitedb_exec_printf (
+                               ebsdb, "CREATE INDEX IF NOT EXISTS PVALINDEX ON %Q (value_phone)",
+                               NULL, NULL, error, tmp);
+               }
+
+               g_free (tmp);
+       }
 
        return success;
 }
 
-/**
- * e_book_backend_sqlitedb_ref_collator:
- * @ebsdb: An #EBookBackendSqliteDB
- *
- * References the currently active #ECollator for @ebsdb,
- * use e_collator_unref() when finished using the returned collator.
- *
- * Note that the active collator will change with the active locale setting.
- *
- * Returns: (transfer full): A reference to the active collator.
- */
-ECollator *
-e_book_backend_sqlitedb_ref_collator(EBookBackendSqliteDB *ebsdb)
+static gboolean
+book_backend_sqlitedb_upgrade (EBookBackendSqliteDB *ebsdb,
+                              const gchar          *folderid,
+                              const gchar          *region,
+                              const gchar          *lc_collate,
+                              GError              **error)
 {
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
+       gboolean success = FALSE;
+       GSList *vcard_data = NULL;
+       GSList *l;
 
-       return e_collator_ref (ebsdb->priv->collator);
-}
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT uid, vcard, NULL FROM %Q",
+               collect_full_results_cb, &vcard_data, error, folderid);
 
-static gchar *
-mprintf_suffix (const gchar *normal)
-{
-       gchar *reverse = normal ? g_utf8_strreverse (normal, -1) : NULL;
-       gchar *stmt = sqlite3_mprintf ("%Q", reverse);
+       for (l = vcard_data; success && l; l = l->next) {
+               EbSdbSearchData *const s_data = l->data;
+               EContact *contact = NULL;
 
-       g_free (reverse);
-       return stmt;
+               /* It can be we're opening a light summary which was created without
+                * storing the vcards, such as was used in EDS versions 3.2 to 3.6.
+                *
+                * In this case we just want to skip the contacts we can't load
+                * and leave them as is in the SQLite, they will be added from
+                * the old BDB in the case of a migration anyway.
+                */
+               if (s_data->vcard)
+                       contact = e_contact_new_from_vcard_with_uid (s_data->vcard, s_data->uid);
+
+               if (contact == NULL)
+                       continue;
+
+               success = book_backend_sqlitedb_insert_contact (ebsdb, contact, folderid, TRUE, region, 
error);
+
+               g_object_unref (contact);
+       }
+
+       g_slist_free_full (vcard_data, (GDestroyNotify)e_book_backend_sqlitedb_search_data_free);
+
+       if (success)
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb, "UPDATE folders SET countrycode = %Q WHERE folder_id = %Q",
+                       NULL, NULL, error, region, folderid);
+
+       if (success)
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb, "UPDATE folders SET lc_collate = %Q WHERE folder_id = %Q",
+                       NULL, NULL, error, lc_collate, folderid);
+
+       return success;
 }
 
-static EPhoneNumber *
-phone_number_from_string (const gchar *normal,
-                          const gchar *default_region)
+static gboolean
+book_backend_sqlitedb_init_locale (EBookBackendSqliteDB *ebsdb,
+                                  const gchar *folderid,
+                                  gint previous_schema,
+                                  gboolean already_exists,
+                                  GError **error)
 {
-       EPhoneNumber *number = NULL;
+       gchar *stored_lc_collate = NULL;
+       gchar *current_region = NULL;
+       const gchar *lc_collate = NULL;
+       gboolean success = TRUE;
+       gboolean relocalized = FALSE;
 
-       /* Don't warn about erronous phone number strings, it's a perfectly normal
-        * use case for users to enter notes instead of phone numbers in the phone
-        * number contact fields, such as "Ask Jenny for Lisa's phone number"
-        */
-       if (normal && e_phone_number_is_supported ())
-               number = e_phone_number_from_string (normal, default_region, NULL);
+       if (e_phone_number_is_supported ()) {
+               current_region = e_phone_number_get_default_region (error);
+
+               if (current_region == NULL)
+                       return FALSE;
+       }
 
-       return number;
+       /* Get the locale setting for this addressbook */
+       if (already_exists) {
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb, "SELECT lc_collate FROM folders WHERE folder_id = %Q",
+                       get_string_cb, &stored_lc_collate, error, folderid);
+
+               lc_collate = stored_lc_collate;
+       }
+
+       if (!lc_collate)
+               /* When creating a new addressbook, or upgrading from a version
+                * where we did not have any locale setting; default to system locale
+                */
+               lc_collate = setlocale (LC_COLLATE, NULL);
+
+       /* Before touching any data, make sure we have a valid ECollator */
+       if (success) {
+               success = book_backend_sqlitedb_set_locale_internal (ebsdb, lc_collate, error);
+       }
+
+       /* Need to relocalize the whole thing if the schema has been upgraded to version 7 */
+       if (success && previous_schema >= 1 && previous_schema < 7) {
+               success = book_backend_sqlitedb_upgrade (ebsdb, folderid, current_region, lc_collate, error);
+               relocalized = TRUE;
+       }
+
+       /* We may need to relocalize for a country code change */
+       if (success && relocalized == FALSE && e_phone_number_is_supported ()) {
+               gchar *stored_region = NULL;
+
+               success = book_backend_sqlitedb_exec_printf (
+                       ebsdb, "SELECT countrycode FROM folders WHERE folder_id = %Q",
+                       get_string_cb, &stored_region, error, folderid);
+
+               if (success && g_strcmp0 (current_region, stored_region) != 0) {
+                       success = book_backend_sqlitedb_upgrade (ebsdb, folderid, current_region, lc_collate, 
error);
+                       relocalized = TRUE;
+               }
+
+               g_free (stored_region);
+       }
+
+       g_free (current_region);
+       g_free (stored_lc_collate);
+
+       return success;
 }
 
+/**********************************************************
+ *                   Inserting Contacts                   *
+ **********************************************************/
 static gchar *
 convert_phone (const gchar *normal,
                const gchar *default_region)
 {
-       EPhoneNumber *number = phone_number_from_string (normal, default_region);
+       EPhoneNumber *number = NULL;
        gchar *indexed_phone_number = NULL;
        gchar *national_number = NULL;
        gint country_code = 0;
 
+       /* Don't warn about erronous phone number strings, it's a perfectly normal
+        * use case for users to enter notes instead of phone numbers in the phone
+        * number contact fields, such as "Ask Jenny for Lisa's phone number"
+        */
+       if (normal && e_phone_number_is_supported ())
+               number = e_phone_number_from_string (normal, default_region, NULL);
+
        if (number) {
                EPhoneNumberCountrySource source;
 
@@ -2047,32 +2114,184 @@ convert_phone (const gchar *normal,
        return indexed_phone_number;
 }
 
-static gchar *
-mprintf_phone (const gchar *normal,
-               const gchar *default_region)
+static sqlite3_stmt *
+book_backend_sqlitedb_prepare_multi_delete (EBookBackendSqliteDB *ebsdb,
+                                           const gchar *folderid,
+                                           SummaryField *field,
+                                           GError **error)
 {
-       gchar *phone = convert_phone (normal, default_region);
-       gchar *stmt = NULL;
+       sqlite3_stmt *stmt = NULL;
+       gchar *list_table = g_strconcat (folderid, "_lists", NULL);
+       gchar *stmt_str;
 
-       if (phone) {
-               stmt = sqlite3_mprintf ("%Q", phone);
-               g_free (phone);
-       }
+       stmt_str = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = :uid", list_table);
+       stmt = book_backend_sqlitedb_prepare_statement (ebsdb, stmt_str, error);
+       sqlite3_free (stmt_str);
 
        return stmt;
 }
 
-/* Add Contact (free the result with g_free() ) */
-static gchar *
-insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
-                          EContact *contact,
-                          const gchar *folderid,
-                          gboolean store_vcard,
-                          gboolean replace_existing,
-                          const gchar *default_region)
+static gboolean
+book_backend_sqlitedb_run_multi_delete (EBookBackendSqliteDB *ebsdb,
+                                       PreparedStatements *stmts,
+                                       EContactField field,
+                                       const gchar *uid,
+                                       GError **error)
+{
+       sqlite3_stmt *stmt;
+       gint ret;
+
+       stmt = g_hash_table_lookup (stmts->multi_deletes, GUINT_TO_POINTER (0));
+
+       /* This can return an error if a previous call to sqlite3_step() had errors,
+        * so let's just ignore any error in this case
+        */
+       sqlite3_reset (stmt);
+
+       /* Clear all previously set values */
+       ret = sqlite3_clear_bindings (stmt);
+
+       /* Set the UID host parameter statically */
+       if (ret == SQLITE_OK)
+               ret = sqlite3_bind_text (stmt, 1, uid, -1, SQLITE_STATIC);
+
+       /* Run the statement */
+       return book_backend_sqlitedb_complete_statement (ebsdb, stmt, ret, error);
+}
+
+static sqlite3_stmt *
+book_backend_sqlitedb_prepare_multi_insert (EBookBackendSqliteDB *ebsdb,
+                                           const gchar *folderid,
+                                           SummaryField *field,
+                                           GError **error)
 {
+       sqlite3_stmt *stmt = NULL;
+       gchar *list_table;
+       gchar *stmt_str;
        GString *string;
-       gchar *str, *vcard_str;
+
+       string = g_string_sized_new (INSERT_MULTI_STMT_BYTES);
+
+       list_table = g_strconcat (folderid, "_lists", NULL);
+       stmt_str = sqlite3_mprintf ("INSERT INTO %Q (uid, field, value", list_table);
+
+       g_string_append (string, stmt_str);
+
+       sqlite3_free (stmt_str);
+       g_free (list_table);
+
+       if ((ebsdb->priv->attr_list_indexes & INDEX_FLAG (SUFFIX)) != 0 &&
+           (field->index & INDEX_FLAG (SUFFIX)) != 0)
+               g_string_append (string, ", value_reverse");
+
+       if ((ebsdb->priv->attr_list_indexes & INDEX_FLAG (PHONE)) != 0 &&
+           (field->index & INDEX_FLAG (PHONE)) != 0)
+               g_string_append (string, ", value_phone");
+
+       g_string_append (string, ") VALUES (:uid, :field, :value");
+
+       if ((ebsdb->priv->attr_list_indexes & INDEX_FLAG (SUFFIX)) != 0 &&
+           (field->index & INDEX_FLAG (SUFFIX)) != 0)
+               g_string_append (string, ", :value_reverse");
+
+       if ((ebsdb->priv->attr_list_indexes & INDEX_FLAG (PHONE)) != 0 &&
+           (field->index & INDEX_FLAG (PHONE)) != 0)
+               g_string_append (string, ", :value_phone");
+
+       g_string_append_c (string, ')');
+
+       stmt = book_backend_sqlitedb_prepare_statement (ebsdb, string->str, error);
+       g_string_free (string, TRUE);
+
+       return stmt;
+}
+
+static gboolean
+book_backend_sqlitedb_run_multi_insert_one (EBookBackendSqliteDB *ebsdb,
+                                           sqlite3_stmt *stmt,
+                                           SummaryField *field,
+                                           const gchar *uid,
+                                           const gchar *dbname,
+                                           const gchar *value,
+                                           const gchar *default_region,
+                                           GError **error)
+{
+       EBookBackendSqliteDBPrivate *priv = ebsdb->priv;
+       gchar *normal = e_util_utf8_normalize (value);
+       gchar *str;
+       gint ret, param_idx = 1;
+
+       ret = sqlite3_bind_text (stmt, param_idx++, uid, -1, SQLITE_STATIC);
+
+       if (ret == SQLITE_OK)
+               ret = sqlite3_bind_text (stmt, param_idx++, dbname, -1, SQLITE_STATIC);
+
+       if (ret == SQLITE_OK)
+               ret = sqlite3_bind_text (stmt, param_idx++, normal, -1, g_free);
+
+       if (ret == SQLITE_OK &&
+           (priv->attr_list_indexes & INDEX_FLAG (SUFFIX)) != 0 &&
+           (field->index & INDEX_FLAG (SUFFIX)) != 0) {
+
+               if (normal)
+                       str = g_utf8_strreverse (normal, -1);
+               else
+                       str = NULL;
+
+               ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
+       }
+
+       if (ret == SQLITE_OK &&
+           (priv->attr_list_indexes & INDEX_FLAG (PHONE)) != 0 &&
+           (field->index & INDEX_FLAG (PHONE)) != 0) {
+
+               str = convert_phone (normal, default_region);
+               sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
+       }
+
+       /* Run the statement */
+       return book_backend_sqlitedb_complete_statement (ebsdb, stmt, ret, error);
+}
+
+static gboolean
+book_backend_sqlitedb_run_multi_insert (EBookBackendSqliteDB *ebsdb,
+                                       PreparedStatements *stmts,
+                                       SummaryField *field,
+                                       const gchar *uid,
+                                       EContact *contact,
+                                       const gchar *default_region,
+                                       GError **error)
+{
+       sqlite3_stmt *stmt;
+       GList *values, *l;
+       gboolean success = TRUE;
+
+       stmt = g_hash_table_lookup (stmts->multi_inserts, GUINT_TO_POINTER (field->field));
+       values = e_contact_get (contact, field->field);
+
+       for (l = values; success && l != NULL; l = l->next) {
+               gchar *value = (gchar *) l->data;
+
+               success = book_backend_sqlitedb_run_multi_insert_one (
+                       ebsdb, stmt, field, uid, field->dbname, value,
+                       default_region, error);
+       }
+
+       /* Free the list of allocated strings */
+       e_contact_attr_list_free (values);
+
+       return success;
+}
+
+static sqlite3_stmt *
+book_backend_sqlitedb_prepare_insert (EBookBackendSqliteDB *ebsdb,
+                                     const gchar *folderid,
+                                     gboolean replace_existing,
+                                     GError **error)
+{
+       sqlite3_stmt *stmt;
+       GString *string;
+       gchar *str;
        gint i;
 
        str = sqlite3_mprintf (
@@ -2087,9 +2306,10 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
         * just how we like them to be.
         */
        for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+               SummaryField *field = &(ebsdb->priv->summary_fields[i]);
 
                /* Multi values go into a separate table/statement */
-               if (ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
+               if (field->type != E_TYPE_CONTACT_ATTR_LIST) {
 
                        /* Only add a ", " before every field except the first,
                         * this will not break because the first 2 fields (UID & REV)
@@ -2098,27 +2318,26 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
                        if (i > 0)
                                g_string_append (string, ", ");
 
-                       g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
+                       g_string_append (string, field->dbname);
                }
 
-               if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
+               if (field->type == G_TYPE_STRING) {
 
-                       if (ebsdb->priv->summary_fields[i].field != E_CONTACT_UID &&
-                           ebsdb->priv->summary_fields[i].field != E_CONTACT_REV) {
+                       if ((field->index & INDEX_FLAG (SORT_KEY)) != 0) {
                                g_string_append (string, ", ");
-                               g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
+                               g_string_append (string, field->dbname);
                                g_string_append (string, "_localized");
                        }
 
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0) {
+                       if ((field->index & INDEX_FLAG (SUFFIX)) != 0) {
                                g_string_append (string, ", ");
-                               g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
+                               g_string_append (string, field->dbname);
                                g_string_append (string, "_reverse");
                        }
 
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0) {
+                       if ((field->index & INDEX_FLAG (PHONE)) != 0) {
                                g_string_append (string, ", ");
-                               g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
+                               g_string_append (string, field->dbname);
                                g_string_append (string, "_phone");
                        }
                }
@@ -2130,8 +2349,9 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
         */
        g_string_append (string, " VALUES (");
        for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+               SummaryField *field = &(ebsdb->priv->summary_fields[i]);
 
-               if (ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
+               if (field->type != E_TYPE_CONTACT_ATTR_LIST) {
                        /* Only add a ", " before every field except the first,
                         * this will not break because the first 2 fields (UID & REV)
                         * are string fields.
@@ -2140,73 +2360,132 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
                                g_string_append (string, ", ");
                }
 
-               if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
+               if (field->type == G_TYPE_STRING || field->type == G_TYPE_BOOLEAN) {
+
+                       g_string_append_c (string, ':');
+                       g_string_append (string, field->dbname);
+
+                       if ((field->index & INDEX_FLAG (SORT_KEY)) != 0)
+                               g_string_append_printf (string, ", :%s_localized", field->dbname);
+
+                       if ((field->index & INDEX_FLAG (SUFFIX)) != 0)
+                               g_string_append_printf (string, ", :%s_reverse", field->dbname);
+
+                       if ((field->index & INDEX_FLAG (PHONE)) != 0)
+                               g_string_append_printf (string, ", :%s_phone", field->dbname);
+
+               } else if (field->type != E_TYPE_CONTACT_ATTR_LIST)
+                       g_warn_if_reached ();
+       }
+
+       g_string_append (string, ", :vcard, :bdata)");
+
+       stmt = book_backend_sqlitedb_prepare_statement (ebsdb, string->str, error);
+       g_string_free (string, TRUE);
+
+       return stmt;
+}
+
+static gboolean
+book_backend_sqlitedb_run_insert (EBookBackendSqliteDB *ebsdb,
+                                 PreparedStatements *stmts,
+                                 gboolean replace_existing,
+                                 EContact *contact,
+                                 const gchar *default_region,
+                                 GError **error)
+{
+       EBookBackendSqliteDBPrivate *priv;
+       sqlite3_stmt *stmt;
+       gint i, param_idx;
+       gint ret;
+
+       priv = ebsdb->priv;
+
+       if (replace_existing)
+               stmt = stmts->replace_stmt;
+       else
+               stmt = stmts->insert_stmt;
+
+       /* This can return an error if a previous call to sqlite3_step() had errors,
+        * so let's just ignore any error in this case
+        */
+       sqlite3_reset (stmt);
+
+       /* Clear all previously set values */
+       ret = sqlite3_clear_bindings (stmt);
+
+       for (i = 0, param_idx = 1; ret == SQLITE_OK && i < ebsdb->priv->n_summary_fields; i++) {
+               SummaryField *field = &(ebsdb->priv->summary_fields[i]);
+
+               if (field->type == G_TYPE_STRING) {
                        gchar *val;
                        gchar *normal;
-                       gchar *localized = NULL;
+                       gchar *str;
 
-                       val = e_contact_get (contact, ebsdb->priv->summary_fields[i].field);
+                       val = e_contact_get (contact, field->field);
 
                        /* Special exception, never normalize/localize the UID or REV string */
-                       if (ebsdb->priv->summary_fields[i].field != E_CONTACT_UID &&
-                           ebsdb->priv->summary_fields[i].field != E_CONTACT_REV) {
+                       if (field->field != E_CONTACT_UID &&
+                           field->field != E_CONTACT_REV) {
                                normal = e_util_utf8_normalize (val);
-
-                               if (val)
-                                       localized = e_collator_generate_key (ebsdb->priv->collator, val, 
NULL);
-                               else
-                                       localized = g_strdup ("");
                        } else
                                normal = g_strdup (val);
 
-                       str = sqlite3_mprintf ("%Q", normal);
-                       g_string_append (string, str);
-                       sqlite3_free (str);
+                       /* Takes ownership of 'normal' */
+                       ret = sqlite3_bind_text (stmt, param_idx++, normal, -1, g_free);
 
-                       if (ebsdb->priv->summary_fields[i].field != E_CONTACT_UID &&
-                           ebsdb->priv->summary_fields[i].field != E_CONTACT_REV) {
-                               str = sqlite3_mprintf ("%Q", localized);
-                               g_string_append (string, ", ");
-                               g_string_append (string, str);
-                               sqlite3_free (str);
+                       if (ret == SQLITE_OK &&
+                           (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
+                               if (val)
+                                       str = e_collator_generate_key (ebsdb->priv->collator, val, NULL);
+                               else
+                                       str = g_strdup ("");
+
+                               ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
                        }
 
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0) {
-                               str = mprintf_suffix (normal);
-                               g_string_append (string, ", ");
-                               g_string_append (string, str);
-                               sqlite3_free (str);
+                       if (ret == SQLITE_OK &&
+                           (field->index & INDEX_FLAG (SUFFIX)) != 0) {
+                               if (normal)
+                                       str = g_utf8_strreverse (normal, -1);
+                               else
+                                       str = NULL;
+
+                               ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
                        }
 
-                       if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0) {
-                               str = mprintf_phone (normal, default_region);
-                               g_string_append (string, ", ");
-                               g_string_append (string, str ? str : "NULL");
-                               sqlite3_free (str);
+                       if (ret == SQLITE_OK &&
+                           (field->index & INDEX_FLAG (PHONE)) != 0) {
+                               str = convert_phone (normal, default_region);
+                               ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
                        }
 
-                       g_free (normal);
                        g_free (val);
-                       g_free (localized);
-               } else if (ebsdb->priv->summary_fields[i].type == G_TYPE_BOOLEAN) {
+               } else if (field->type == G_TYPE_BOOLEAN) {
                        gboolean val;
 
-                       val = e_contact_get (contact, ebsdb->priv->summary_fields[i].field) ? TRUE : FALSE;
-                       g_string_append_printf (string, "%d", val ? 1 : 0);
+                       val = e_contact_get (contact, field->field) ? TRUE : FALSE;
 
-               } else if (ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST)
+                       ret = sqlite3_bind_int (stmt, param_idx++, val ? 1 : 0);
+               } else if (field->type != E_TYPE_CONTACT_ATTR_LIST)
                        g_warn_if_reached ();
        }
 
-       vcard_str = store_vcard ? e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30) : NULL;
-       str = sqlite3_mprintf (", %Q, %Q)", vcard_str, NULL);
+       if (ret == SQLITE_OK) {
+               gchar *vcard = NULL;
 
-       g_string_append (string, str);
+               if (priv->store_vcard)
+                       vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 
-       sqlite3_free (str);
-       g_free (vcard_str);
+               ret = sqlite3_bind_text (stmt, param_idx++, vcard, -1, g_free);
+       }
 
-       return g_string_free (string, FALSE);
+       /* The last one is :bdata, we don't actually insert anything here */
+       if (ret == SQLITE_OK)
+               ret = sqlite3_bind_text (stmt, param_idx++, NULL, 0, SQLITE_STATIC);
+
+       /* Run the statement */
+       return book_backend_sqlitedb_complete_statement (ebsdb, stmt, ret, error);
 }
 
 static void
@@ -2279,733 +2558,142 @@ update_e164_attribute_params (EVCard *vcard,
 }
 
 static gboolean
-insert_contact (EBookBackendSqliteDB *ebsdb,
-                EContact *contact,
-                const gchar *folderid,
-                gboolean replace_existing,
-                const gchar *default_region,
-                GError **error)
+book_backend_sqlitedb_insert_contact (EBookBackendSqliteDB *ebsdb,
+                                     EContact *contact,
+                                     const gchar *folderid,
+                                     gboolean replace_existing,
+                                     const gchar *default_region,
+                                     GError **error)
 {
        EBookBackendSqliteDBPrivate *priv;
+       PreparedStatements *stmts;
        gboolean success;
-       gchar *stmt;
 
        priv = ebsdb->priv;
 
+       stmts = g_hash_table_lookup (priv->prepared_stmts, folderid);
+
        /* Update E.164 parameters in vcard if needed */
        if (priv->store_vcard)
                update_e164_attribute_params (E_VCARD (contact), default_region);
 
-       /* Update main summary table */
-       stmt = insert_stmt_from_contact (ebsdb, contact, folderid, priv->store_vcard, replace_existing, 
default_region);
-       success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
-       g_free (stmt);
+       success = book_backend_sqlitedb_run_insert (ebsdb,
+                                                   stmts,
+                                                   replace_existing,
+                                                   contact,
+                                                   default_region,
+                                                   error);
 
        /* Update attribute list table */
        if (success && priv->have_attr_list) {
-               gchar *list_folder = g_strdup_printf ("%s_lists", folderid);
-               gchar *uid;
-               gint   i;
-               GList *values, *l;
-
-               /* First remove all entries for this UID */
-               uid = e_contact_get (contact, E_CONTACT_UID);
-               stmt = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = %Q", list_folder, uid);
-               success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
-               sqlite3_free (stmt);
+               gchar *uid = e_contact_get (contact, E_CONTACT_UID);
+               gint i;
+
+               success = book_backend_sqlitedb_run_multi_delete (ebsdb,
+                                                                 stmts,
+                                                                 0 /* XXX unused for now */,
+                                                                 uid,
+                                                                 error);
 
                for (i = 0; success && i < priv->n_summary_fields; i++) {
-                       if (priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST)
-                               continue;
+                       SummaryField *field = &(ebsdb->priv->summary_fields[i]);
 
-                       values = e_contact_get (contact, priv->summary_fields[i].field);
-
-                       for (l = values; success && l != NULL; l = l->next) {
-                               gchar *value = (gchar *) l->data;
-                               gchar *normal = e_util_utf8_normalize (value);
-                               gchar *stmt_suffix = NULL;
-                               gchar *stmt_phone = NULL;
-
-                               if ((priv->attr_list_indexes & INDEX_SUFFIX) != 0
-                                       && (priv->summary_fields[i].index & INDEX_SUFFIX) != 0)
-                                       stmt_suffix = mprintf_suffix (normal);
-
-                               if ((priv->attr_list_indexes & INDEX_PHONE) != 0
-                                       && (priv->summary_fields[i].index & INDEX_PHONE) != 0)
-                                       stmt_phone = mprintf_phone (normal, default_region);
-
-                               stmt = sqlite3_mprintf (
-                                       "INSERT INTO %Q (uid, field, value%s%s) "
-                                       "VALUES (%Q, %Q, %Q%s%s%s%s)",
-                                       list_folder,
-                                       stmt_suffix ? ", value_reverse" : "",
-                                       stmt_phone ? ", value_phone" : "",
-                                       uid, priv->summary_fields[i].dbname, normal,
-                                       stmt_suffix ? ", " : "",
-                                       stmt_suffix ? stmt_suffix : "",
-                                       stmt_phone ? ", " : "",
-                                       stmt_phone ? stmt_phone : "");
-
-                               if (stmt_suffix)
-                                       sqlite3_free (stmt_suffix);
-                               if (stmt_phone)
-                                       sqlite3_free (stmt_phone);
-
-                               success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
-                               sqlite3_free (stmt);
-                               g_free (normal);
-                       }
+                       if (field->type != E_TYPE_CONTACT_ATTR_LIST)
+                               continue;
 
-                       /* Free the list of allocated strings */
-                       e_contact_attr_list_free (values);
+                       success = book_backend_sqlitedb_run_multi_insert (ebsdb,
+                                                                         stmts,
+                                                                         field,
+                                                                         uid,
+                                                                         contact,
+                                                                         default_region,
+                                                                         error);
                }
 
-               g_free (list_folder);
                g_free (uid);
        }
 
        return success;
 }
 
-/**
- * e_book_backend_sqlitedb_new_contact
- * @ebsdb: An #EBookBackendSqliteDB
- * @folderid: folder id
- * @contact: EContact to be added
- * @replace_existing: Whether this contact should replace another contact with the same UID.
- * @error: A location to store any error that may have occurred.
- *
- * This is a convenience wrapper for e_book_backend_sqlitedb_new_contacts,
- * which is the preferred means to add or modify multiple contacts when possible.
- *
- * Returns: TRUE on success.
- *
- * Since: 3.8
- **/
-gboolean
-e_book_backend_sqlitedb_new_contact (EBookBackendSqliteDB *ebsdb,
-                                     const gchar *folderid,
-                                     EContact *contact,
-                                     gboolean replace_existing,
-                                     GError **error)
-{
-       GSList l;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
-       g_return_val_if_fail (folderid != NULL, FALSE);
-       g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
-
-       l.data = contact;
-       l.next = NULL;
-
-       return e_book_backend_sqlitedb_new_contacts (
-               ebsdb, folderid, &l,
-               replace_existing, error);
-}
-
-/**
- * e_book_backend_sqlitedb_new_contacts
- * @ebsdb: An #EBookBackendSqliteDB
- * @folderid: folder id
- * @contacts: list of EContacts
- * @replace_existing: Whether this contact should replace another contact with the same UID.
- * @error: A location to store any error that may have occurred.
- *
- * Adds or replaces contacts in @ebsdb. If @replace_existing is specified then existing
- * contacts with the same UID will be replaced, otherwise adding an existing contact
- * will return an error.
- *
- * Returns: TRUE on success.
- *
- * Since: 3.8
- **/
-gboolean
-e_book_backend_sqlitedb_new_contacts (EBookBackendSqliteDB *ebsdb,
-                                      const gchar *folderid,
-                                      GSList *contacts,
-                                      gboolean replace_existing,
-                                      GError **error)
-{
-       GSList *l;
-       gboolean success = TRUE;
-       gchar *default_region = NULL;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
-       g_return_val_if_fail (folderid != NULL, FALSE);
-       g_return_val_if_fail (contacts != NULL, FALSE);
-
-       LOCK_MUTEX (&ebsdb->priv->lock);
-
-       if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
-               UNLOCK_MUTEX (&ebsdb->priv->lock);
-               return FALSE;
-       }
-
-       if (e_phone_number_is_supported ()) {
-               default_region = e_phone_number_get_default_region (error);
-
-               if (default_region == NULL)
-                       success = FALSE;
-       }
-
-       for (l = contacts; success && l != NULL; l = g_slist_next (l)) {
-               EContact *contact = (EContact *) l->data;
-
-               success = insert_contact (
-                       ebsdb, contact, folderid, replace_existing,
-                       default_region, error);
-       }
-
-       g_free (default_region);
-
-       if (success)
-               success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
-       else
-               /* The GError is already set. */
-               book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
-
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
-
-       return success;
-}
-
-/**
- * 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.
- *
- * Since: 3.2
- *
- * Deprecated: 3.8: Use e_book_backend_sqlitedb_new_contact() instead.
- **/
-gboolean
-e_book_backend_sqlitedb_add_contact (EBookBackendSqliteDB *ebsdb,
-                                     const gchar *folderid,
-                                     EContact *contact,
-                                     gboolean partial_content,
-                                     GError **error)
-{
-       return e_book_backend_sqlitedb_new_contact (ebsdb, folderid, contact, TRUE, 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.
- *
- * Since: 3.2
- *
- * Deprecated: 3.8: Use e_book_backend_sqlitedb_new_contacts() instead.
- **/
-gboolean
-e_book_backend_sqlitedb_add_contacts (EBookBackendSqliteDB *ebsdb,
-                                      const gchar *folderid,
-                                      GSList *contacts,
-                                      gboolean partial_content,
-                                      GError **error)
-{
-       return e_book_backend_sqlitedb_new_contacts (ebsdb, folderid, contacts, TRUE, error);
-}
-
-/**
- * e_book_backend_sqlitedb_remove_contact:
- *
- * FIXME: Document me.
- *
- * Since: 3.2
- **/
-gboolean
-e_book_backend_sqlitedb_remove_contact (EBookBackendSqliteDB *ebsdb,
-                                        const gchar *folderid,
-                                        const gchar *uid,
-                                        GError **error)
-{
-       GSList l;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
-       g_return_val_if_fail (folderid != NULL, FALSE);
-       g_return_val_if_fail (uid != NULL, FALSE);
-
-       l.data = (gchar *) uid; /* Won't modify it, I promise :) */
-       l.next = NULL;
-
-       return e_book_backend_sqlitedb_remove_contacts (
-               ebsdb, folderid, &l, error);
-}
-
-static gchar *
-generate_uid_list_for_stmt (GSList *uids)
-{
-       GString *str = g_string_new (NULL);
-       GSList  *l;
-       gboolean first = TRUE;
-
-       for (l = uids; l; l = l->next) {
-               gchar *uid = (gchar *) l->data;
-               gchar *tmp;
-
-               /* First uid with no comma */
-               if (!first)
-                       g_string_append_printf (str, ", ");
-               else
-                       first = FALSE;
-
-               tmp = sqlite3_mprintf ("%Q", uid);
-               g_string_append (str, tmp);
-               sqlite3_free (tmp);
-       }
-
-       return g_string_free (str, FALSE);
-}
-
-static gchar *
-generate_delete_stmt (const gchar *table,
-                      GSList *uids)
-{
-       GString *str = g_string_new (NULL);
-       gchar *tmp;
-
-       tmp = sqlite3_mprintf ("DELETE FROM %Q WHERE uid IN (", table);
-       g_string_append (str, tmp);
-       sqlite3_free (tmp);
-
-       tmp = generate_uid_list_for_stmt (uids);
-       g_string_append (str, tmp);
-       g_free (tmp);
-       g_string_append_c (str, ')');
-
-       return g_string_free (str, FALSE);
-}
-
-/**
- * e_book_backend_sqlitedb_remove_contacts:
- *
- * FIXME: Document me.
- *
- * Since: 3.2
- **/
-gboolean
-e_book_backend_sqlitedb_remove_contacts (EBookBackendSqliteDB *ebsdb,
-                                         const gchar *folderid,
-                                         GSList *uids,
-                                         GError **error)
-{
-       gboolean success = TRUE;
-       gchar *stmt;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
-       g_return_val_if_fail (folderid != NULL, FALSE);
-       g_return_val_if_fail (uids != NULL, FALSE);
-
-       LOCK_MUTEX (&ebsdb->priv->lock);
-
-       if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
-               UNLOCK_MUTEX (&ebsdb->priv->lock);
-               return FALSE;
-       }
-
-       /* Delete the auxillary contact infos first */
-       if (success && ebsdb->priv->have_attr_list) {
-               gchar *lists_folder = g_strdup_printf ("%s_lists", folderid);
-
-               stmt = generate_delete_stmt (lists_folder, uids);
-               g_free (lists_folder);
-
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-               g_free (stmt);
-       }
-
-       if (success) {
-               stmt = generate_delete_stmt (folderid, uids);
-               success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
-               g_free (stmt);
-       }
-
-       if (success)
-               success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
-       else
-               /* The GError is already set. */
-               book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
-
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
-
-       return success;
-}
-
-static gint
-contact_found_cb (gpointer ref,
-                  gint col,
-                  gchar **cols,
-                  gchar **name)
-{
-       gboolean *exists = ref;
-
-       *exists = TRUE;
-
-       return 0;
-}
-
-/**
- * e_book_backend_sqlitedb_has_contact:
- *
- * FIXME: Document me.
- *
- * Note: The @partial_content is unused here.
- *
- * Since: 3.2
- **/
-gboolean
-e_book_backend_sqlitedb_has_contact (EBookBackendSqliteDB *ebsdb,
-                                     const gchar *folderid,
-                                     const gchar *uid,
-                                     gboolean *partial_content,
-                                     GError **error)
-{
-       gboolean exists = FALSE;
-       gboolean success;
-       gchar *stmt;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
-       g_return_val_if_fail (folderid != NULL, FALSE);
-       g_return_val_if_fail (uid != NULL, FALSE);
-
-       LOCK_MUTEX (&ebsdb->priv->lock);
-
-       stmt = sqlite3_mprintf (
-               "SELECT uid FROM %Q WHERE uid = %Q",
-               folderid, uid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, contact_found_cb, &exists, error);
-       sqlite3_free (stmt);
-
-       if (partial_content)
-               *partial_content = FALSE;
-
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
-
-       /* FIXME Returning FALSE can mean either "contact not found" or
-        *       "error occurred".  Add a boolean (out) "exists" parameter. */
-       return success && 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;
-}
-
-/**
- * e_book_backend_sqlitedb_get_contact:
- *
- * FIXME: Document me.
- *
- * Since: 3.2
- **/
-EContact *
-e_book_backend_sqlitedb_get_contact (EBookBackendSqliteDB *ebsdb,
-                                     const gchar *folderid,
-                                     const gchar *uid,
-                                     GHashTable *fields_of_interest,
-                                     gboolean *with_all_required_fields,
-                                     GError **error)
-{
-       EContact *contact = NULL;
-       gchar *vcard;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
-       g_return_val_if_fail (folderid != NULL, NULL);
-       g_return_val_if_fail (uid != NULL, NULL);
-
-       vcard = e_book_backend_sqlitedb_get_vcard_string (
-               ebsdb, folderid, uid,
-               fields_of_interest, with_all_required_fields, error);
-
-       if (vcard != NULL) {
-               contact = e_contact_new_from_vcard_with_uid (vcard, uid);
-               g_free (vcard);
-       }
-
-       return contact;
-}
-
-static gboolean
-uid_rev_fields (GHashTable *fields_of_interest)
-{
-       GHashTableIter iter;
-       gpointer key, value;
-
-       if (!fields_of_interest || g_hash_table_size (fields_of_interest) > 2)
-               return FALSE;
-
-       g_hash_table_iter_init (&iter, fields_of_interest);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               const gchar *field_name = key;
-               EContactField field = e_contact_field_id (field_name);
-
-               if (field != E_CONTACT_UID &&
-                   field != E_CONTACT_REV)
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-/**
- * e_book_backend_sqlitedb_is_summary_fields:
- * @fields_of_interest: A hash table containing the fields of interest
- * 
- * This only checks if all the fields are part of the default summary fields,
- * not part of the configured summary fields.
- *
- * Since: 3.2
- *
- * Deprecated: 3.8: Use e_book_backend_sqlitedb_check_summary_fields() instead.
- **/
-gboolean
-e_book_backend_sqlitedb_is_summary_fields (GHashTable *fields_of_interest)
+static void
+prepared_statements_free (PreparedStatements *stmts)
 {
-       gboolean summary_fields = TRUE;
-       GHashTableIter iter;
-       gpointer key, value;
-       gint     i;
+       if (stmts) {
+               sqlite3_finalize (stmts->insert_stmt);
+               sqlite3_finalize (stmts->replace_stmt);
 
-       if (!fields_of_interest)
-               return FALSE;
+               g_hash_table_destroy (stmts->multi_deletes);
+               g_hash_table_destroy (stmts->multi_inserts);
 
-       g_hash_table_iter_init (&iter, fields_of_interest);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               const gchar  *field_name = key;
-               EContactField field      = e_contact_field_id (field_name);
-               gboolean      found      = FALSE;
-
-               for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++) {
-                       if (field == default_summary_fields[i]) {
-                               found = TRUE;
-                               break;
-                       }
-               }
-
-               if (!found) {
-                       summary_fields = FALSE;
-                       break;
-               }
+               g_slice_free (PreparedStatements, stmts);
        }
-
-       return summary_fields;
 }
 
-/**
- * e_book_backend_sqlitedb_check_summary_fields:
- * @ebsdb: An #EBookBackendSqliteDB
- * @fields_of_interest: A hash table containing the fields of interest
- * 
- * Checks if all the specified fields are part of the configured summary
- * fields for @ebsdb
- *
- * Since: 3.8
- **/
-gboolean
-e_book_backend_sqlitedb_check_summary_fields (EBookBackendSqliteDB *ebsdb,
-                                              GHashTable *fields_of_interest)
+static PreparedStatements *
+prepared_statements_new_for_folder (EBookBackendSqliteDB *ebsdb,
+                                   const gchar *folderid,
+                                   GError **error)
 {
-       gboolean summary_fields = TRUE;
-       GHashTableIter iter;
-       gpointer key, value;
-
-       if (!fields_of_interest)
-               return FALSE;
-
-       LOCK_MUTEX (&ebsdb->priv->lock);
-
-       g_hash_table_iter_init (&iter, fields_of_interest);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               const gchar  *field_name = key;
-               EContactField field      = e_contact_field_id (field_name);
-
-               if (summary_dbname_from_field (ebsdb, field) == NULL) {
-                       summary_fields = FALSE;
-                       break;
-               }
-       }
-
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
-
-       return summary_fields;
-}
-
-/* free return value with g_free */
-static gchar *
-summary_select_stmt (GHashTable *fields_of_interest,
-                     gboolean distinct)
-{
-       GString *string;
-
-       if (distinct)
-               string = g_string_new ("SELECT DISTINCT summary.uid");
-       else
-               string = g_string_new ("SELECT summary.uid");
-
-       /* Add the E_CONTACT_REV field if they are both requested */
-       if (g_hash_table_size (fields_of_interest) == 2)
-               g_string_append (string, ", Rev");
-
-       return g_string_free (string, FALSE);
-}
-
-static gint
-store_data_to_vcard (gpointer ref,
-                     gint ncol,
-                     gchar **cols,
-                     gchar **name)
-{
-       GSList **vcard_data = ref;
-       EbSdbSearchData *search_data = g_slice_new0 (EbSdbSearchData);
-       EContact *contact = e_contact_new ();
-       gchar *vcard;
+       PreparedStatements *stmts = g_slice_new0 (PreparedStatements);
+       sqlite3_stmt *stmt;
        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;
-
-               /* Only UID & REV can be used to create contacts from the summary columns */
-               if (!g_ascii_strcasecmp (name[i], "uid")) {
-                       e_contact_set (contact, E_CONTACT_UID, cols[i]);
-                       search_data->uid = g_strdup (cols[i]);
-               } else if (!g_ascii_strcasecmp (name[i], "Rev")) {
-                       e_contact_set (contact, E_CONTACT_REV, cols[i]);
-               } else if (!g_ascii_strcasecmp (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;
-}
-
-/**
- * e_book_backend_sqlitedb_get_vcard_string:
- * @ebsdb: An #EBookBackendSqliteDB
- * @folderid: The folder id
- * @uid: The uid to fetch a vcard for
- * @fields_of_interest: The required fields for this vcard, or %NULL to require all fields.
- * @with_all_required_fields: (allow none) (out): Whether all the required fields are present in the 
returned vcard.
- * @error: A location to store any error that may have occurred.
- *
- * Searches @ebsdb in the context of @folderid for @uid.
- *
- * If @ebsdb is configured to store the whole vcards, the whole vcard will be returned.
- * Otherwise the summary cache will be searched and the virtual vcard will be built
- * from the summary cache.
- *
- * In either case, @with_all_required_fields if specified, will be updated to reflect whether
- * the returned vcard string satisfies the passed 'fields_of_interest' parameter.
- * 
- * Returns: (transfer full): The vcard string for @uid or %NULL if @uid was not found.
- *
- * Since: 3.2
- */
-gchar *
-e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
-                                          const gchar *folderid,
-                                          const gchar *uid,
-                                          GHashTable *fields_of_interest,
-                                          gboolean *with_all_required_fields,
-                                          GError **error)
-{
-       gchar *stmt;
-       gchar *vcard_str = NULL;
-       gboolean local_with_all_required_fields = FALSE;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
-       g_return_val_if_fail (folderid != NULL, NULL);
-       g_return_val_if_fail (uid != NULL, NULL);
+       stmts->insert_stmt = book_backend_sqlitedb_prepare_insert (ebsdb, folderid, FALSE, error);
+       if (!stmts->insert_stmt)
+               goto preparation_failed;
 
-       LOCK_MUTEX (&ebsdb->priv->lock);
-
-       /* Try constructing contacts from only UID/REV first if that's requested */
-       if (uid_rev_fields (fields_of_interest)) {
-               GSList *vcards = NULL;
-               gchar *select_portion;
-
-               select_portion = summary_select_stmt (fields_of_interest, FALSE);
-
-               stmt = sqlite3_mprintf (
-                       "%s FROM %Q AS summary WHERE summary.uid = %Q",
-                       select_portion, folderid, uid);
-               book_backend_sql_exec (ebsdb->priv->db, stmt, store_data_to_vcard, &vcards, error);
-               sqlite3_free (stmt);
-               g_free (select_portion);
+       stmts->replace_stmt = book_backend_sqlitedb_prepare_insert (ebsdb, folderid, TRUE, error);
+       if (!stmts->replace_stmt)
+               goto preparation_failed;
 
-               if (vcards) {
-                       EbSdbSearchData *s_data = (EbSdbSearchData *) vcards->data;
+       stmts->multi_deletes =
+               g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                      NULL,
+                                      (GDestroyNotify)sqlite3_finalize);
+       stmts->multi_inserts =
+               g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                      NULL,
+                                      (GDestroyNotify)sqlite3_finalize);
 
-                       vcard_str = s_data->vcard;
-                       s_data->vcard = NULL;
+       for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+               SummaryField *field = &(ebsdb->priv->summary_fields[i]);
 
-                       g_slist_free_full (vcards, destroy_search_data);
-                       vcards = NULL;
-               }
+               if (field->type != E_TYPE_CONTACT_ATTR_LIST)
+                       continue;
 
-               local_with_all_required_fields = TRUE;
-       } else if (ebsdb->priv->store_vcard) {
-               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);
+               stmt = book_backend_sqlitedb_prepare_multi_insert (
+                       ebsdb, folderid, field, error);
 
-               local_with_all_required_fields = TRUE;
-       } else {
-               g_set_error (
-                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
-                       _("Full search_contacts are not stored in cache. vcards cannot be returned."));
+               if (!stmt)
+                       goto preparation_failed;
 
+               g_hash_table_insert (stmts->multi_inserts,
+                                    GUINT_TO_POINTER (field->field),
+                                    stmt);
        }
 
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
+       /* XXX HACK, for now there is only one delete statement and
+        * only one multi value table... that will change soon */
+       stmt = book_backend_sqlitedb_prepare_multi_delete (ebsdb, folderid, NULL, error);
+       if (!stmt)
+               goto preparation_failed;
 
-       if (with_all_required_fields)
-               *with_all_required_fields = local_with_all_required_fields;
+       g_hash_table_insert (stmts->multi_deletes,
+                            GUINT_TO_POINTER (0),
+                            stmt);
 
-       if (!vcard_str && error && !*error)
-               g_set_error (
-                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_CONTACT_NOT_FOUND,
-                       _("Contact '%s' not found"), uid ? uid : "NULL");
+       return stmts;
 
-       return vcard_str;
+ preparation_failed:
+
+       prepared_statements_free (stmts);
+       return NULL;
 }
 
+/**********************************************************
+ * Querying preflighting, checking if query is supported  *
+ **********************************************************/
 enum {
        CHECK_IS_SUMMARY   = (1 << 0),
        CHECK_IS_LIST_ATTR = (1 << 1),
@@ -3261,118 +2949,9 @@ e_book_backend_sqlitedb_check_summary_query_locked (EBookBackendSqliteDB *ebsdb,
        return retval;
 }
 
-/**
- * e_book_backend_sqlitedb_check_summary_query:
- * @ebsdb: an #EBookBackendSqliteDB
- * @query: the query to check
- * @with_list_attrs: Return location to store whether the query touches upon list attributes
- *
- * Checks whether @query contains only checks for the summary fields
- * configured in @ebsdb
- *
- * Since: 3.8
- **/
-gboolean
-e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
-                                             const gchar *query,
-                                             gboolean *with_list_attrs)
-{
-       gboolean is_summary;
-
-       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
-
-       LOCK_MUTEX (&ebsdb->priv->lock);
-       is_summary = e_book_backend_sqlitedb_check_summary_query_locked (ebsdb, query, with_list_attrs, NULL, 
NULL);
-       UNLOCK_MUTEX (&ebsdb->priv->lock);
-
-       return is_summary;
-}
-
-/**
- * e_book_backend_sqlitedb_is_summary_query:
- *
- * Checks whether the query contains only checks for the default summary fields
- *
- * Since: 3.2
- *
- * Deprecated: 3.8: Use e_book_backend_sqlitedb_check_summary_query() instead
- **/
-gboolean
-e_book_backend_sqlitedb_is_summary_query (const gchar *query)
-{
-       return e_book_backend_sqlitedb_check_summary_query_locked (NULL, query, NULL, NULL, NULL);
-}
-
-static ESExpResult *
-func_and (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;
-               }
-               if (r1->value.string && *r1->value.string)
-                       g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc - 
1)) ? " AND ":"");
-               e_sexp_result_free (f, r1);
-       }
-       g_string_append (string, " )");
-       r = e_sexp_result_new (f, ESEXP_RES_STRING);
-
-       if (strlen (string->str) == 4) {
-               r->value.string = g_strdup ("");
-               g_string_free (string, TRUE);
-       } else {
-               r->value.string = g_string_free (string, FALSE);
-       }
-
-       return r;
-}
-
-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;
-               }
-               if (r1->value.string && *r1->value.string)
-                       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);
-       if (strlen (string->str) == 4) {
-               r->value.string = g_strdup ("");
-               g_string_free (string, TRUE);
-       } else {
-               r->value.string = g_string_free (string, FALSE);
-       }
-
-       return r;
-}
-
+/**********************************************************
+ *                   Querying Contacts                    *
+ **********************************************************/
 typedef enum {
        MATCH_CONTAINS,
        MATCH_IS,
@@ -3391,6 +2970,25 @@ typedef enum {
        CONVERT_PHONE     = (1 << 2)
 } ConvertFlags;
 
+/* free return value with g_free */
+static gchar *
+summary_select_stmt (GHashTable *fields_of_interest,
+                     gboolean distinct)
+{
+       GString *string;
+
+       if (distinct)
+               string = g_string_new ("SELECT DISTINCT summary.uid");
+       else
+               string = g_string_new ("SELECT summary.uid");
+
+       /* Add the E_CONTACT_REV field if they are both requested */
+       if (g_hash_table_size (fields_of_interest) == 2)
+               g_string_append (string, ", Rev");
+
+       return g_string_free (string, FALSE);
+}
+
 static gchar *
 extract_digits (const gchar *normal)
 {
@@ -3519,15 +3117,15 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
                            gchar **query_term,
                            gchar **extra_term)
 {
-       gint summary_index;
+       SummaryField *field;
        gchar *field_name = NULL;
        gchar *value = NULL;
        gchar *extra = NULL;
        gboolean list_attr = FALSE;
 
-       summary_index = summary_index_from_field_name (ebsdb, field_name_input);
+       field = summary_field_get_by_name (ebsdb, field_name_input);
 
-       if (summary_index < 0) {
+       if (field == NULL) {
                g_critical ("Only summary field matches should be converted to sql queries");
                field_name = g_strconcat (folderid, ".", field_name_input, NULL);
                value = convert_string_value (
@@ -3538,22 +3136,21 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
                gboolean phone_search = FALSE;
 
                /* If its a suffix search and we have reverse data to search... */
-               if (match == MATCH_ENDS_WITH &&
-                   (ebsdb->priv->summary_fields[summary_index].index & INDEX_SUFFIX) != 0)
+               if (match == MATCH_ENDS_WITH && (field->index & INDEX_FLAG (SUFFIX)) != 0)
                        suffix_search = TRUE;
 
                /* If its a phone-number search and we have E.164 data to search... */
                else if ((match == MATCH_PHONE_NUMBER ||
-                               match == MATCH_NATIONAL_PHONE_NUMBER ||
-                               match == MATCH_SHORT_PHONE_NUMBER) &&
-                   (ebsdb->priv->summary_fields[summary_index].index & INDEX_PHONE) != 0)
+                         match == MATCH_NATIONAL_PHONE_NUMBER ||
+                         match == MATCH_SHORT_PHONE_NUMBER) &&
+                        (field->index & INDEX_FLAG (PHONE)) != 0)
                        phone_search = TRUE;
 
                /* Or also if its an exact match, and we *only* have reverse data which is indexed,
                 * then prefer the indexed reverse search. */
                else if (match == MATCH_IS &&
-                        (ebsdb->priv->summary_fields[summary_index].index & INDEX_SUFFIX) != 0 &&
-                        (ebsdb->priv->summary_fields[summary_index].index & INDEX_PREFIX) == 0)
+                        (field->index & INDEX_FLAG (SUFFIX)) != 0 &&
+                        (field->index & INDEX_FLAG (PREFIX)) == 0)
                        suffix_search = TRUE;
 
                if (suffix_search) {
@@ -3562,17 +3159,17 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
                         *  o Check the reversed column instead
                         *  o Make it a prefix search
                         */
-                       if (ebsdb->priv->summary_fields[summary_index].type == E_TYPE_CONTACT_ATTR_LIST) {
+                       if (field->type == E_TYPE_CONTACT_ATTR_LIST) {
                                field_name = g_strdup ("multi.value_reverse");
                                list_attr = TRUE;
                        } else
                                field_name = g_strconcat (
                                        "summary.",
-                                       ebsdb->priv->summary_fields[summary_index].dbname,
+                                       field->dbname,
                                        "_reverse", NULL);
 
-                       if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
-                           ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV)
+                       if (field->field == E_CONTACT_UID ||
+                           field->field == E_CONTACT_REV)
                                value = convert_string_value (
                                        ebsdb, query_term_input, region, CONVERT_REVERSE,
                                        (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
@@ -3588,13 +3185,13 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
                         */
                        const gint country_code = e_phone_number_get_country_code_for_region (region, NULL);
 
-                       if (ebsdb->priv->summary_fields[summary_index].type == E_TYPE_CONTACT_ATTR_LIST) {
+                       if (field->type == E_TYPE_CONTACT_ATTR_LIST) {
                                field_name = g_strdup ("multi.value_phone");
                                list_attr = TRUE;
                        } else {
                                field_name = g_strdup_printf (
                                        "summary.%s_phone",
-                                       ebsdb->priv->summary_fields[summary_index].dbname);
+                                       field->dbname);
                        }
 
                        if (match == MATCH_PHONE_NUMBER) {
@@ -3624,16 +3221,16 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
 
                        }
                } else {
-                       if (ebsdb->priv->summary_fields[summary_index].type == E_TYPE_CONTACT_ATTR_LIST) {
+                       if (field->type == E_TYPE_CONTACT_ATTR_LIST) {
                                field_name = g_strdup ("multi.value");
                                list_attr = TRUE;
                        } else
                                field_name = g_strconcat (
                                        "summary.",
-                                       ebsdb->priv->summary_fields[summary_index].dbname, NULL);
+                                       field->dbname, NULL);
 
-                       if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
-                           ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV) {
+                       if (field->field == E_CONTACT_UID ||
+                           field->field == E_CONTACT_REV) {
                                value = convert_string_value (
                                        ebsdb, query_term_input, region,
                                        CONVERT_NOTHING, match);
@@ -3719,7 +3316,7 @@ convert_match_exp (struct _ESExp *f,
                                g_free (field_name);
                                g_free (query_term);
 
-                               if (summary_dbname_from_field (ebsdb, E_CONTACT_FAMILY_NAME)) {
+                               if (summary_field_get (ebsdb, E_CONTACT_FAMILY_NAME)) {
                                        field_name = field_name_and_query_term (
                                                ebsdb, qdata->folderid, "family_name",
                                                argv[1]->value.string, NULL,
@@ -3731,7 +3328,7 @@ convert_match_exp (struct _ESExp *f,
                                        g_free (query_term);
                                }
 
-                               if (summary_dbname_from_field (ebsdb, E_CONTACT_GIVEN_NAME)) {
+                               if (summary_field_get (ebsdb, E_CONTACT_GIVEN_NAME)) {
                                        field_name = field_name_and_query_term (
                                                ebsdb, qdata->folderid, "given_name",
                                                argv[1]->value.string, NULL,
@@ -3743,7 +3340,7 @@ convert_match_exp (struct _ESExp *f,
                                        g_free (query_term);
                                }
 
-                               if (summary_dbname_from_field (ebsdb, E_CONTACT_NICKNAME)) {
+                               if (summary_field_get (ebsdb, E_CONTACT_NICKNAME)) {
                                        field_name = field_name_and_query_term (
                                                ebsdb, qdata->folderid, "nickname",
                                                argv[1]->value.string, NULL,
@@ -3805,6 +3402,76 @@ convert_match_exp (struct _ESExp *f,
 }
 
 static ESExpResult *
+func_and (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;
+               }
+               if (r1->value.string && *r1->value.string)
+                       g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc - 
1)) ? " AND ":"");
+               e_sexp_result_free (f, r1);
+       }
+       g_string_append (string, " )");
+       r = e_sexp_result_new (f, ESEXP_RES_STRING);
+
+       if (strlen (string->str) == 4) {
+               r->value.string = g_strdup ("");
+               g_string_free (string, TRUE);
+       } else {
+               r->value.string = g_string_free (string, FALSE);
+       }
+
+       return r;
+}
+
+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;
+               }
+               if (r1->value.string && *r1->value.string)
+                       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);
+       if (strlen (string->str) == 4) {
+               r->value.string = g_strdup ("");
+               g_string_free (string, TRUE);
+       } else {
+               r->value.string = g_string_free (string, FALSE);
+       }
+
+       return r;
+}
+
+static ESExpResult *
 func_contains (struct _ESExp *f,
                gint argc,
                struct _ESExpResult **argv,
@@ -3941,51 +3608,26 @@ sexp_to_sql_query (EBookBackendSqliteDB *ebsdb,
        return res;
 }
 
-static EbSdbSearchData *
-search_data_from_results (gchar **cols)
-{
-       EbSdbSearchData *data = g_slice_new0 (EbSdbSearchData);
-
-       if (cols[0])
-               data->uid = g_strdup (cols[0]);
-
-       if (cols[1])
-               data->vcard = g_strdup (cols[1]);
-
-       if (cols[2])
-               data->bdata = g_strdup (cols[2]);
-
-       return data;
-}
-
-static gint
-addto_vcard_list_cb (gpointer ref,
-                     gint col,
-                     gchar **cols,
-                     gchar **name)
+static gboolean
+uid_rev_fields (GHashTable *fields_of_interest)
 {
-       GSList **vcard_data = ref;
-       EbSdbSearchData *s_data;
-
-       s_data = search_data_from_results (cols);
-
-       *vcard_data = g_slist_prepend (*vcard_data, s_data);
+       GHashTableIter iter;
+       gpointer key, value;
 
-       return 0;
-}
+       if (!fields_of_interest || g_hash_table_size (fields_of_interest) > 2)
+               return FALSE;
 
-static gint
-addto_slist_cb (gpointer ref,
-                gint col,
-                gchar **cols,
-                gchar **name)
-{
-       GSList **uids = ref;
+       g_hash_table_iter_init (&iter, fields_of_interest);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               const gchar *field_name = key;
+               EContactField field = e_contact_field_id (field_name);
 
-       if (cols[0])
-               *uids = g_slist_prepend (*uids, g_strdup (cols [0]));
+               if (field != E_CONTACT_UID &&
+                   field != E_CONTACT_REV)
+                       return FALSE;
+       }
 
-       return 0;
+       return TRUE;
 }
 
 static GSList *
@@ -3998,7 +3640,6 @@ book_backend_sqlitedb_search_query (EBookBackendSqliteDB *ebsdb,
                                     GError **error)
 {
        GSList *vcard_data = NULL;
-       gchar  *stmt;
        gboolean local_with_all_required_fields = FALSE;
        gboolean success = TRUE;
 
@@ -4014,28 +3655,27 @@ book_backend_sqlitedb_search_query (EBookBackendSqliteDB *ebsdb,
                        if (query_with_list_attrs) {
                                gchar *list_table = g_strconcat (folderid, "_lists", NULL);
 
-                               stmt = sqlite3_mprintf (
+                               success = book_backend_sqlitedb_exec_printf (
+                                       ebsdb,
                                        "%s FROM %Q AS summary "
                                        "LEFT OUTER JOIN %Q AS multi ON summary.uid = multi.uid WHERE %s",
+                                       collect_lean_results_cb, &vcard_data, error,
                                        select_portion, folderid, list_table, sql);
+
                                g_free (list_table);
                        } else {
-                               stmt = sqlite3_mprintf (
-                                       "%s FROM %Q AS summary WHERE %s",
+
+                               success = book_backend_sqlitedb_exec_printf (
+                                       ebsdb, "%s FROM %Q AS summary WHERE %s",
+                                       collect_lean_results_cb, &vcard_data, error,
                                        select_portion, folderid, sql);
                        }
 
-                       success = book_backend_sql_exec (
-                               ebsdb->priv->db, stmt,
-                               store_data_to_vcard, &vcard_data, error);
-
-                       sqlite3_free (stmt);
                } else {
-                       stmt = sqlite3_mprintf ("%s FROM %Q AS summary", select_portion, folderid);
-                       success = book_backend_sql_exec (
-                               ebsdb->priv->db, stmt,
-                               store_data_to_vcard, &vcard_data, error);
-                       sqlite3_free (stmt);
+                       success = book_backend_sqlitedb_exec_printf (
+                               ebsdb, "%s FROM %Q AS summary",
+                               collect_lean_results_cb, &vcard_data, error,
+                               select_portion, folderid);
                }
 
                local_with_all_required_fields = TRUE;
@@ -4048,28 +3688,29 @@ book_backend_sqlitedb_search_query (EBookBackendSqliteDB *ebsdb,
                        if (query_with_list_attrs) {
                                gchar *list_table = g_strconcat (folderid, "_lists", NULL);
 
-                               stmt = sqlite3_mprintf (
+                               success = book_backend_sqlitedb_exec_printf (
+                                       ebsdb,
                                        "SELECT DISTINCT summary.uid, vcard, bdata FROM %Q AS summary "
                                        "LEFT OUTER JOIN %Q AS multi ON summary.uid = multi.uid WHERE %s",
+                                       collect_full_results_cb, &vcard_data, error,
                                        folderid, list_table, sql);
+
                                g_free (list_table);
+
                        } else {
-                               stmt = sqlite3_mprintf (
-                                       "SELECT uid, vcard, bdata FROM %Q as summary WHERE %s", folderid, 
sql);
-                       }
 
-                       success = book_backend_sql_exec (
-                               ebsdb->priv->db, stmt,
-                               addto_vcard_list_cb, &vcard_data, error);
+                               success = book_backend_sqlitedb_exec_printf (
+                                       ebsdb,
+                                       "SELECT uid, vcard, bdata FROM %Q as summary WHERE %s",
+                                       collect_full_results_cb, &vcard_data, error,
+                                       folderid, sql);
+                       }
 
-                       sqlite3_free (stmt);
                } else {
-                       stmt = sqlite3_mprintf (
-                               "SELECT uid, vcard, bdata FROM %Q", folderid);
-                       success = book_backend_sql_exec (
-                               ebsdb->priv->db, stmt,
-                               addto_vcard_list_cb , &vcard_data, error);
-                       sqlite3_free (stmt);
+
+                       success = book_backend_sqlitedb_exec_printf (
+                               ebsdb, "SELECT uid, vcard, bdata FROM %Q",
+                               collect_full_results_cb, &vcard_data, error, folderid);
                }
 
                local_with_all_required_fields = TRUE;
@@ -4100,12 +3741,10 @@ book_backend_sqlitedb_search_full (EBookBackendSqliteDB *ebsdb,
        GSList *r_list = NULL, *all = NULL, *l;
        EBookBackendSExp *bsexp = NULL;
        gboolean success;
-       gchar *stmt;
 
-       stmt = sqlite3_mprintf ("SELECT uid, vcard, bdata FROM %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, addto_vcard_list_cb , &all, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT uid, vcard, bdata FROM %Q",
+               collect_full_results_cb, &all, error, folderid);
 
        if (!success) {
                g_warn_if_fail (all == NULL);
@@ -4135,6 +3774,1000 @@ book_backend_sqlitedb_search_full (EBookBackendSqliteDB *ebsdb,
        return r_list;
 }
 
+
+
+/**********************************************************
+ *                     GObjectClass                       *
+ **********************************************************/
+static void
+e_book_backend_sqlitedb_dispose (GObject *object)
+{
+       EBookBackendSqliteDB *ebsdb = E_BOOK_BACKEND_SQLITEDB (object);
+
+       book_backend_sqlitedb_unregister_from_hash (ebsdb);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_book_backend_sqlitedb_parent_class)->dispose (object);
+}
+
+static void
+e_book_backend_sqlitedb_finalize (GObject *object)
+{
+       EBookBackendSqliteDB *ebsdb = E_BOOK_BACKEND_SQLITEDB (object);
+       EBookBackendSqliteDBPrivate *priv = ebsdb->priv;
+
+       sqlite3_close (priv->db);
+
+       g_free (priv->path);
+       g_free (priv->summary_fields);
+       g_free (priv->locale);
+
+       if (priv->collator)
+               e_collator_unref (priv->collator);
+
+       g_mutex_clear (&priv->lock);
+       g_mutex_clear (&priv->updates_lock);
+
+       g_hash_table_destroy (ebsdb->priv->prepared_stmts);
+
+       /* 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->dispose = e_book_backend_sqlitedb_dispose;
+       object_class->finalize = e_book_backend_sqlitedb_finalize;
+}
+
+static void
+e_book_backend_sqlitedb_init (EBookBackendSqliteDB *ebsdb)
+{
+       ebsdb->priv = 
+               G_TYPE_INSTANCE_GET_PRIVATE (ebsdb,
+                                            E_TYPE_BOOK_BACKEND_SQLITEDB,
+                                            EBookBackendSqliteDBPrivate);
+
+       ebsdb->priv->store_vcard = TRUE;
+
+       ebsdb->priv->in_transaction = 0;
+       g_mutex_init (&ebsdb->priv->lock);
+       g_mutex_init (&ebsdb->priv->updates_lock);
+
+       ebsdb->priv->prepared_stmts =
+               g_hash_table_new_full (g_str_hash,
+                                      g_str_equal,
+                                      g_free,
+                                      (GDestroyNotify)prepared_statements_free);
+}
+
+static EBookBackendSqliteDB *
+e_book_backend_sqlitedb_new_internal (const gchar *path,
+                                      const gchar *emailid,
+                                      const gchar *folderid,
+                                      const gchar *folder_name,
+                                      gboolean store_vcard,
+                                      SummaryField *fields,
+                                      gint n_fields,
+                                      gboolean have_attr_list,
+                                      gint attr_list_indexes,
+                                      GError **error)
+{
+       EBookBackendSqliteDB *ebsdb;
+       gchar *filename;
+       gint previous_schema = 0;
+       gboolean already_exists = FALSE;
+       GSList *introspected_columns = NULL;
+       PreparedStatements *stmts;
+
+       g_return_val_if_fail (path != NULL, NULL);
+       g_return_val_if_fail (emailid != NULL, NULL);
+       g_return_val_if_fail (folderid != NULL, NULL);
+       g_return_val_if_fail (folder_name != NULL, NULL);
+
+       g_mutex_lock (&dbcon_lock);
+
+       ebsdb = book_backend_sqlitedb_ref_from_hash (path, emailid);
+       if (ebsdb)
+               goto exit;
+
+       ebsdb = g_object_new (E_TYPE_BOOK_BACKEND_SQLITEDB, NULL);
+       ebsdb->priv->path = g_strdup (path);
+       ebsdb->priv->summary_fields = fields;
+       ebsdb->priv->n_summary_fields = n_fields;
+       ebsdb->priv->have_attr_list = have_attr_list;
+       ebsdb->priv->attr_list_indexes = attr_list_indexes;
+       ebsdb->priv->store_vcard = store_vcard;
+
+       if (g_mkdir_with_parents (path, 0777) < 0) {
+               g_mutex_unlock (&dbcon_lock);
+               g_object_unref (ebsdb);
+               g_set_error (
+                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+                       "Can not make parent directory: errno %d", errno);
+               return NULL;
+       }
+
+       filename = g_build_filename (path, DB_FILENAME, NULL);
+
+       if (!book_backend_sqlitedb_init_sqlite (ebsdb, filename, error)) {
+               g_mutex_unlock (&dbcon_lock);
+               g_object_unref (ebsdb);
+               g_free (filename);
+               return NULL;
+       }
+       g_free (filename);
+
+       /* Initialize main folders table */
+       if (!book_backend_sqlitedb_init_folders (ebsdb, &previous_schema, error)) {
+               g_mutex_unlock (&dbcon_lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+
+       /* Initialize key/value table */
+       if (!book_backend_sqlitedb_init_keys (ebsdb, error)) {
+               g_mutex_unlock (&dbcon_lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+
+       book_backend_sqlitedb_register_to_hash (ebsdb, path, emailid);
+
+ exit:
+       /* While the global dbcon_lock is held, hold the ebsdb specific lock and
+        * prolong the locking on that instance until the folders are all set up
+        */
+       LOCK_MUTEX (&ebsdb->priv->lock);
+       g_mutex_unlock (&dbcon_lock);
+
+       if (!book_backend_sqlitedb_add_folder (ebsdb, folderid, folder_name,
+                                              &already_exists, error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+
+       if (already_exists &&
+           !book_backend_sqlitedb_introspect_summary (ebsdb, folderid,
+                                                      previous_schema,
+                                                      &introspected_columns,
+                                                      error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+
+       if (!book_backend_sqlitedb_init_contacts (ebsdb, folderid,
+                                                 introspected_columns,
+                                                 error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               g_slist_free_full (introspected_columns, (GDestroyNotify) g_free);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+       g_slist_free_full (introspected_columns, (GDestroyNotify) g_free);
+
+       if (!book_backend_sqlitedb_init_aux_tables (ebsdb, folderid,
+                                                   error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+
+       /* At this point we have resolved our schema, let's build our
+        * precompiled statements, we might use them to re-insert contacts
+        * in the next step
+        */
+       stmts = prepared_statements_new_for_folder (ebsdb, folderid, error);
+       if (!stmts) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+       g_hash_table_insert (ebsdb->priv->prepared_stmts, g_strdup (folderid), stmts);
+
+       if (!book_backend_sqlitedb_init_locale (ebsdb, folderid,
+                                               previous_schema,
+                                               already_exists,
+                                               error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               g_object_unref (ebsdb);
+               return NULL;
+       }
+
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       return ebsdb;
+}
+
+/**
+ * e_book_backend_sqlitedb_new_full:
+ * @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.
+ * @setup: an #ESourceBackendSummarySetup describing how the summary should be setup
+ * @error: A location to store any error that may have occurred
+ *
+ * Like e_book_backend_sqlitedb_new(), but allows configuration of which contact fields
+ * will be stored for quick reference in the summary. The configuration indicated by
+ * @setup will only be taken into account when initially creating the underlying table,
+ * further configurations will be ignored.
+ *
+ * The fields %E_CONTACT_UID and %E_CONTACT_REV are not optional,
+ * they will be stored in the summary regardless of this function's parameters
+ *
+ * <note><para>Only #EContactFields with the type #G_TYPE_STRING, #G_TYPE_BOOLEAN or
+ * #E_TYPE_CONTACT_ATTR_LIST are currently supported.</para></note>
+ *
+ * Returns: (transfer full): The newly created #EBookBackendSqliteDB
+ *
+ * Since: 3.8
+ **/
+EBookBackendSqliteDB *
+e_book_backend_sqlitedb_new_full (const gchar *path,
+                                  const gchar *emailid,
+                                  const gchar *folderid,
+                                  const gchar *folder_name,
+                                  gboolean store_vcard,
+                                  ESourceBackendSummarySetup *setup,
+                                  GError **error)
+{
+       EBookBackendSqliteDB *ebsdb = NULL;
+       EContactField *fields;
+       EContactField *indexed_fields;
+       EBookIndexType *index_types = NULL;
+       gboolean have_attr_list = FALSE;
+       gint attr_list_indexes = 0;
+       gboolean had_error = FALSE;
+       GArray *summary_fields;
+       gint n_fields = 0, n_indexed_fields = 0, i;
+
+       fields         = e_source_backend_summary_setup_get_summary_fields (setup, &n_fields);
+       indexed_fields = e_source_backend_summary_setup_get_indexed_fields (setup, &index_types, 
&n_indexed_fields);
+
+       /* No specified summary fields indicates the default summary configuration should be used */
+       if (n_fields <= 0) {
+               ebsdb = e_book_backend_sqlitedb_new (path, emailid, folderid, folder_name, store_vcard, 
error);
+               g_free (fields);
+               g_free (index_types);
+               g_free (indexed_fields);
+
+               return ebsdb;
+       }
+
+       summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
+
+       /* Ensure the non-optional fields first */
+       summary_field_append (summary_fields, E_CONTACT_UID, &have_attr_list, error);
+       summary_field_append (summary_fields, E_CONTACT_REV, &have_attr_list, error);
+
+       for (i = 0; i < n_fields; i++) {
+               if (!summary_field_append (summary_fields, fields[i], &have_attr_list, error)) {
+                       had_error = TRUE;
+                       break;
+               }
+       }
+
+       if (had_error) {
+               g_array_free (summary_fields, TRUE);
+               g_free (fields);
+               g_free (index_types);
+               g_free (indexed_fields);
+               return NULL;
+       }
+
+       /* Add the 'indexed' flag to the SummaryField structs */
+       summary_fields_add_indexes (
+               summary_fields, indexed_fields, index_types, n_indexed_fields,
+               &attr_list_indexes);
+
+       ebsdb = e_book_backend_sqlitedb_new_internal (
+               path, emailid, folderid, folder_name,
+               store_vcard,
+               (SummaryField *) summary_fields->data,
+               summary_fields->len,
+               have_attr_list,
+               attr_list_indexes,
+               error);
+
+       g_free (fields);
+       g_free (index_types);
+       g_free (indexed_fields);
+       g_array_free (summary_fields, FALSE);
+
+       return ebsdb;
+}
+
+/**
+ * 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:
+ *
+ * Since: 3.2
+ **/
+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;
+       GArray *summary_fields;
+       gboolean have_attr_list = FALSE;
+       gint attr_list_indexes = 0;
+       gint i;
+
+       /* Create the default summary structs */
+       summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
+       for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++)
+               summary_field_append (summary_fields, default_summary_fields[i], &have_attr_list, NULL);
+
+       /* Add the default index flags */
+       summary_fields_add_indexes (
+               summary_fields,
+               default_indexed_fields,
+               default_index_types,
+               G_N_ELEMENTS (default_indexed_fields),
+               &attr_list_indexes);
+
+       ebsdb = e_book_backend_sqlitedb_new_internal (
+               path, emailid, folderid, folder_name,
+               store_vcard,
+               (SummaryField *) summary_fields->data,
+               summary_fields->len,
+               have_attr_list,
+               attr_list_indexes,
+               error);
+
+       g_array_free (summary_fields, FALSE);
+
+       return ebsdb;
+}
+
+gboolean
+e_book_backend_sqlitedb_lock_updates (EBookBackendSqliteDB *ebsdb,
+                                      GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+
+       LOCK_MUTEX (&ebsdb->priv->updates_lock);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+       success = book_backend_sqlitedb_start_transaction (ebsdb, error);
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       return success;
+}
+
+gboolean
+e_book_backend_sqlitedb_unlock_updates (EBookBackendSqliteDB *ebsdb,
+                                        gboolean do_commit,
+                                        GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+       success = do_commit ?
+               book_backend_sqlitedb_commit_transaction (ebsdb, error) :
+               book_backend_sqlitedb_rollback_transaction (ebsdb, error);
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       UNLOCK_MUTEX (&ebsdb->priv->updates_lock);
+
+       return success;
+}
+
+/**
+ * e_book_backend_sqlitedb_ref_collator:
+ * @ebsdb: An #EBookBackendSqliteDB
+ *
+ * References the currently active #ECollator for @ebsdb,
+ * use e_collator_unref() when finished using the returned collator.
+ *
+ * Note that the active collator will change with the active locale setting.
+ *
+ * Returns: (transfer full): A reference to the active collator.
+ */
+ECollator *
+e_book_backend_sqlitedb_ref_collator (EBookBackendSqliteDB *ebsdb)
+{
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
+
+       return e_collator_ref (ebsdb->priv->collator);
+}
+
+/**
+ * e_book_backend_sqlitedb_new_contact
+ * @ebsdb: An #EBookBackendSqliteDB
+ * @folderid: folder id
+ * @contact: EContact to be added
+ * @replace_existing: Whether this contact should replace another contact with the same UID.
+ * @error: A location to store any error that may have occurred.
+ *
+ * This is a convenience wrapper for e_book_backend_sqlitedb_new_contacts,
+ * which is the preferred means to add or modify multiple contacts when possible.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_book_backend_sqlitedb_new_contact (EBookBackendSqliteDB *ebsdb,
+                                     const gchar *folderid,
+                                     EContact *contact,
+                                     gboolean replace_existing,
+                                     GError **error)
+{
+       GSList l;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+       g_return_val_if_fail (folderid != NULL, FALSE);
+       g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
+
+       l.data = contact;
+       l.next = NULL;
+
+       return e_book_backend_sqlitedb_new_contacts (
+               ebsdb, folderid, &l,
+               replace_existing, error);
+}
+
+/**
+ * e_book_backend_sqlitedb_new_contacts
+ * @ebsdb: An #EBookBackendSqliteDB
+ * @folderid: folder id
+ * @contacts: list of EContacts
+ * @replace_existing: Whether this contact should replace another contact with the same UID.
+ * @error: A location to store any error that may have occurred.
+ *
+ * Adds or replaces contacts in @ebsdb. If @replace_existing is specified then existing
+ * contacts with the same UID will be replaced, otherwise adding an existing contact
+ * will return an error.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_book_backend_sqlitedb_new_contacts (EBookBackendSqliteDB *ebsdb,
+                                      const gchar *folderid,
+                                      GSList *contacts,
+                                      gboolean replace_existing,
+                                      GError **error)
+{
+       GSList *l;
+       gboolean success = TRUE;
+       gchar *default_region = NULL;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+       g_return_val_if_fail (folderid != NULL, FALSE);
+       g_return_val_if_fail (contacts != NULL, FALSE);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+
+       if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               return FALSE;
+       }
+
+       if (e_phone_number_is_supported ()) {
+               default_region = e_phone_number_get_default_region (error);
+
+               if (default_region == NULL)
+                       success = FALSE;
+       }
+
+       for (l = contacts; success && l != NULL; l = g_slist_next (l)) {
+               EContact *contact = (EContact *) l->data;
+
+               success = book_backend_sqlitedb_insert_contact (
+                       ebsdb, contact, folderid, replace_existing,
+                       default_region, error);
+       }
+
+       g_free (default_region);
+
+       if (success)
+               success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+       else
+               /* The GError is already set. */
+               book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       return success;
+}
+
+/**
+ * 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.
+ *
+ * Since: 3.2
+ *
+ * Deprecated: 3.8: Use e_book_backend_sqlitedb_new_contact() instead.
+ **/
+gboolean
+e_book_backend_sqlitedb_add_contact (EBookBackendSqliteDB *ebsdb,
+                                     const gchar *folderid,
+                                     EContact *contact,
+                                     gboolean partial_content,
+                                     GError **error)
+{
+       return e_book_backend_sqlitedb_new_contact (ebsdb, folderid, contact, TRUE, 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.
+ *
+ * Since: 3.2
+ *
+ * Deprecated: 3.8: Use e_book_backend_sqlitedb_new_contacts() instead.
+ **/
+gboolean
+e_book_backend_sqlitedb_add_contacts (EBookBackendSqliteDB *ebsdb,
+                                      const gchar *folderid,
+                                      GSList *contacts,
+                                      gboolean partial_content,
+                                      GError **error)
+{
+       return e_book_backend_sqlitedb_new_contacts (ebsdb, folderid, contacts, TRUE, error);
+}
+
+/**
+ * e_book_backend_sqlitedb_remove_contact:
+ *
+ * FIXME: Document me.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_backend_sqlitedb_remove_contact (EBookBackendSqliteDB *ebsdb,
+                                        const gchar *folderid,
+                                        const gchar *uid,
+                                        GError **error)
+{
+       GSList l;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+       g_return_val_if_fail (folderid != NULL, FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+
+       l.data = (gchar *) uid; /* Won't modify it, I promise :) */
+       l.next = NULL;
+
+       return e_book_backend_sqlitedb_remove_contacts (
+               ebsdb, folderid, &l, error);
+}
+
+static gchar *
+generate_uid_list_for_stmt (GSList *uids)
+{
+       GString *str = g_string_new (NULL);
+       GSList  *l;
+       gboolean first = TRUE;
+
+       for (l = uids; l; l = l->next) {
+               gchar *uid = (gchar *) l->data;
+               gchar *tmp;
+
+               /* First uid with no comma */
+               if (!first)
+                       g_string_append_printf (str, ", ");
+               else
+                       first = FALSE;
+
+               tmp = sqlite3_mprintf ("%Q", uid);
+               g_string_append (str, tmp);
+               sqlite3_free (tmp);
+       }
+
+       return g_string_free (str, FALSE);
+}
+
+static gchar *
+generate_delete_stmt (const gchar *table,
+                      GSList *uids)
+{
+       GString *str = g_string_new (NULL);
+       gchar *tmp;
+
+       tmp = sqlite3_mprintf ("DELETE FROM %Q WHERE uid IN (", table);
+       g_string_append (str, tmp);
+       sqlite3_free (tmp);
+
+       tmp = generate_uid_list_for_stmt (uids);
+       g_string_append (str, tmp);
+       g_free (tmp);
+       g_string_append_c (str, ')');
+
+       return g_string_free (str, FALSE);
+}
+
+/**
+ * e_book_backend_sqlitedb_remove_contacts:
+ *
+ * FIXME: Document me.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_backend_sqlitedb_remove_contacts (EBookBackendSqliteDB *ebsdb,
+                                         const gchar *folderid,
+                                         GSList *uids,
+                                         GError **error)
+{
+       gboolean success = TRUE;
+       gchar *stmt;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+       g_return_val_if_fail (folderid != NULL, FALSE);
+       g_return_val_if_fail (uids != NULL, FALSE);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+
+       if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+               UNLOCK_MUTEX (&ebsdb->priv->lock);
+               return FALSE;
+       }
+
+       /* Delete the auxillary contact infos first */
+       if (success && ebsdb->priv->have_attr_list) {
+               gchar *lists_folder = g_strdup_printf ("%s_lists", folderid);
+
+               stmt = generate_delete_stmt (lists_folder, uids);
+               g_free (lists_folder);
+
+               success = book_backend_sqlitedb_exec (ebsdb, stmt, NULL, NULL, error);
+               g_free (stmt);
+       }
+
+       if (success) {
+               stmt = generate_delete_stmt (folderid, uids);
+               success = book_backend_sqlitedb_exec (ebsdb, stmt, NULL, NULL, error);
+               g_free (stmt);
+       }
+
+       if (success)
+               success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+       else
+               /* The GError is already set. */
+               book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       return success;
+}
+
+/**
+ * e_book_backend_sqlitedb_has_contact:
+ *
+ * FIXME: Document me.
+ *
+ * Note: The @partial_content is unused here.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_book_backend_sqlitedb_has_contact (EBookBackendSqliteDB *ebsdb,
+                                     const gchar *folderid,
+                                     const gchar *uid,
+                                     gboolean *partial_content,
+                                     GError **error)
+{
+       gboolean exists = FALSE;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+       g_return_val_if_fail (folderid != NULL, FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT uid FROM %Q WHERE uid = %Q",
+               get_exists_cb, &exists, error, folderid, uid);
+
+       if (partial_content)
+               *partial_content = FALSE;
+
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       /* FIXME Returning FALSE can mean either "contact not found" or
+        *       "error occurred".  Add a boolean (out) "exists" parameter. */
+       return success && exists;
+}
+
+/**
+ * e_book_backend_sqlitedb_get_contact:
+ *
+ * FIXME: Document me.
+ *
+ * Since: 3.2
+ **/
+EContact *
+e_book_backend_sqlitedb_get_contact (EBookBackendSqliteDB *ebsdb,
+                                     const gchar *folderid,
+                                     const gchar *uid,
+                                     GHashTable *fields_of_interest,
+                                     gboolean *with_all_required_fields,
+                                     GError **error)
+{
+       EContact *contact = NULL;
+       gchar *vcard;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
+       g_return_val_if_fail (folderid != NULL, NULL);
+       g_return_val_if_fail (uid != NULL, NULL);
+
+       vcard = e_book_backend_sqlitedb_get_vcard_string (
+               ebsdb, folderid, uid,
+               fields_of_interest, with_all_required_fields, error);
+
+       if (vcard != NULL) {
+               contact = e_contact_new_from_vcard_with_uid (vcard, uid);
+               g_free (vcard);
+       }
+
+       return contact;
+}
+
+/**
+ * e_book_backend_sqlitedb_is_summary_fields:
+ * @fields_of_interest: A hash table containing the fields of interest
+ * 
+ * This only checks if all the fields are part of the default summary fields,
+ * not part of the configured summary fields.
+ *
+ * Since: 3.2
+ *
+ * Deprecated: 3.8: Use e_book_backend_sqlitedb_check_summary_fields() instead.
+ **/
+gboolean
+e_book_backend_sqlitedb_is_summary_fields (GHashTable *fields_of_interest)
+{
+       gboolean summary_fields = TRUE;
+       GHashTableIter iter;
+       gpointer key, value;
+       gint     i;
+
+       if (!fields_of_interest)
+               return FALSE;
+
+       g_hash_table_iter_init (&iter, fields_of_interest);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               const gchar  *field_name = key;
+               EContactField field      = e_contact_field_id (field_name);
+               gboolean      found      = FALSE;
+
+               for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++) {
+                       if (field == default_summary_fields[i]) {
+                               found = TRUE;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       summary_fields = FALSE;
+                       break;
+               }
+       }
+
+       return summary_fields;
+}
+
+/**
+ * e_book_backend_sqlitedb_check_summary_fields:
+ * @ebsdb: An #EBookBackendSqliteDB
+ * @fields_of_interest: A hash table containing the fields of interest
+ * 
+ * Checks if all the specified fields are part of the configured summary
+ * fields for @ebsdb
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_book_backend_sqlitedb_check_summary_fields (EBookBackendSqliteDB *ebsdb,
+                                              GHashTable *fields_of_interest)
+{
+       gboolean summary_fields = TRUE;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       if (!fields_of_interest)
+               return FALSE;
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+
+       g_hash_table_iter_init (&iter, fields_of_interest);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               const gchar  *field_name = key;
+               
+               if (!summary_field_get_by_name (ebsdb, field_name)) {
+                       summary_fields = FALSE;
+                       break;
+               }
+       }
+
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       return summary_fields;
+}
+
+/**
+ * e_book_backend_sqlitedb_get_vcard_string:
+ * @ebsdb: An #EBookBackendSqliteDB
+ * @folderid: The folder id
+ * @uid: The uid to fetch a vcard for
+ * @fields_of_interest: The required fields for this vcard, or %NULL to require all fields.
+ * @with_all_required_fields: (allow none) (out): Whether all the required fields are present in the 
returned vcard.
+ * @error: A location to store any error that may have occurred.
+ *
+ * Searches @ebsdb in the context of @folderid for @uid.
+ *
+ * If @ebsdb is configured to store the whole vcards, the whole vcard will be returned.
+ * Otherwise the summary cache will be searched and the virtual vcard will be built
+ * from the summary cache.
+ *
+ * In either case, @with_all_required_fields if specified, will be updated to reflect whether
+ * the returned vcard string satisfies the passed 'fields_of_interest' parameter.
+ * 
+ * Returns: (transfer full): The vcard string for @uid or %NULL if @uid was not found.
+ *
+ * Since: 3.2
+ */
+gchar *
+e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
+                                          const gchar *folderid,
+                                          const gchar *uid,
+                                          GHashTable *fields_of_interest,
+                                          gboolean *with_all_required_fields,
+                                          GError **error)
+{
+       gchar *vcard_str = NULL;
+       gboolean local_with_all_required_fields = FALSE;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
+       g_return_val_if_fail (folderid != NULL, NULL);
+       g_return_val_if_fail (uid != NULL, NULL);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+
+       /* Try constructing contacts from only UID/REV first if that's requested */
+       if (uid_rev_fields (fields_of_interest)) {
+               GSList *vcards = NULL;
+               gchar *select_portion;
+
+               select_portion = summary_select_stmt (fields_of_interest, FALSE);
+
+               book_backend_sqlitedb_exec_printf (
+                       ebsdb, "%s FROM %Q AS summary WHERE summary.uid = %Q",
+                       collect_lean_results_cb, &vcards, error,
+                       select_portion, folderid, uid);
+
+               g_free (select_portion);
+
+               if (vcards) {
+                       EbSdbSearchData *s_data = (EbSdbSearchData *) vcards->data;
+
+                       vcard_str = s_data->vcard;
+                       s_data->vcard = NULL;
+
+                       g_slist_free_full (vcards, (GDestroyNotify)e_book_backend_sqlitedb_search_data_free);
+                       vcards = NULL;
+               }
+
+               local_with_all_required_fields = TRUE;
+       } else if (ebsdb->priv->store_vcard) {
+
+               book_backend_sqlitedb_exec_printf (
+                       ebsdb, "SELECT vcard FROM %Q WHERE uid = %Q",
+                       get_string_cb, &vcard_str, error, folderid, uid);
+
+               local_with_all_required_fields = TRUE;
+       } else {
+               g_set_error (
+                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+                       _("Full search_contacts are not stored in cache. vcards cannot be returned."));
+
+       }
+
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       if (with_all_required_fields)
+               *with_all_required_fields = local_with_all_required_fields;
+       if (!vcard_str && error && !*error)
+               g_set_error (
+                       error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_CONTACT_NOT_FOUND,
+                       _("Contact '%s' not found"), uid ? uid : "NULL");
+
+       return vcard_str;
+}
+
+/**
+ * e_book_backend_sqlitedb_check_summary_query:
+ * @ebsdb: an #EBookBackendSqliteDB
+ * @query: the query to check
+ * @with_list_attrs: Return location to store whether the query touches upon list attributes
+ *
+ * Checks whether @query contains only checks for the summary fields
+ * configured in @ebsdb
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
+                                             const gchar *query,
+                                             gboolean *with_list_attrs)
+{
+       gboolean is_summary;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+
+       LOCK_MUTEX (&ebsdb->priv->lock);
+       is_summary = e_book_backend_sqlitedb_check_summary_query_locked (ebsdb, query, with_list_attrs, NULL, 
NULL);
+       UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+       return is_summary;
+}
+
+/**
+ * e_book_backend_sqlitedb_is_summary_query:
+ *
+ * Checks whether the query contains only checks for the default summary fields
+ *
+ * Since: 3.2
+ *
+ * Deprecated: 3.8: Use e_book_backend_sqlitedb_check_summary_query() instead
+ **/
+gboolean
+e_book_backend_sqlitedb_is_summary_query (const gchar *query)
+{
+       return e_book_backend_sqlitedb_check_summary_query_locked (NULL, query, NULL, NULL, NULL);
+}
+
 /**
  * e_book_backend_sqlitedb_search 
  * @ebsdb: 
@@ -4289,7 +4922,6 @@ e_book_backend_sqlitedb_search_uids (EBookBackendSqliteDB *ebsdb,
                        error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_INVALID_QUERY,
                        _("Invalid query"));
        else if (!sexp || summary_query) {
-               gchar *stmt;
                gchar *sql_query = sexp ? sexp_to_sql_query (ebsdb, folderid, sexp) : NULL;
 
                if (sql_query && sql_query[0]) {
@@ -4297,24 +4929,24 @@ e_book_backend_sqlitedb_search_uids (EBookBackendSqliteDB *ebsdb,
                        if (query_with_list_attrs) {
                                gchar *list_table = g_strconcat (folderid, "_lists", NULL);
 
-                               stmt = sqlite3_mprintf (
+                               book_backend_sqlitedb_exec_printf (
+                                       ebsdb,
                                        "SELECT DISTINCT summary.uid FROM %Q AS summary "
                                        "LEFT OUTER JOIN %Q AS multi ON summary.uid = multi.uid WHERE %s",
+                                       collect_uid_results_cb, &uids, error,
                                        folderid, list_table, sql_query);
 
                                g_free (list_table);
-                       } else
-                               stmt = sqlite3_mprintf (
-                                       "SELECT summary.uid FROM %Q AS summary WHERE %s",
-                                       folderid, sql_query);
-
-                       book_backend_sql_exec (ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
-                       sqlite3_free (stmt);
+                       } else {
+                               book_backend_sqlitedb_exec_printf (
+                                       ebsdb, "SELECT summary.uid FROM %Q AS summary WHERE %s",
+                                       collect_uid_results_cb, &uids, error, folderid, sql_query);
+                       }
 
                } else {
-                       stmt = sqlite3_mprintf ("SELECT uid FROM %Q", folderid);
-                       book_backend_sql_exec (ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
-                       sqlite3_free (stmt);
+                       book_backend_sqlitedb_exec_printf (
+                               ebsdb, "SELECT uid FROM %Q",
+                               collect_uid_results_cb, &uids, error, folderid);
                }
 
                local_searched = TRUE;
@@ -4342,20 +4974,6 @@ e_book_backend_sqlitedb_search_uids (EBookBackendSqliteDB *ebsdb,
        return uids;
 }
 
-static gint
-get_uids_and_rev_cb (gpointer user_data,
-                     gint col,
-                     gchar **cols,
-                     gchar **name)
-{
-       GHashTable *uids_and_rev = user_data;
-
-       if (col == 2 && cols[0])
-               g_hash_table_insert (uids_and_rev, g_strdup (cols[0]), g_strdup (cols[1] ? cols[1] : ""));
-
-       return 0;
-}
-
 /**
  * e_book_backend_sqlitedb_get_uids_and_rev:
  *
@@ -4372,7 +4990,6 @@ e_book_backend_sqlitedb_get_uids_and_rev (EBookBackendSqliteDB *ebsdb,
                                           GError **error)
 {
        GHashTable *uids_and_rev;
-       gchar *stmt;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
        g_return_val_if_fail (folderid != NULL, NULL);
@@ -4381,11 +4998,9 @@ e_book_backend_sqlitedb_get_uids_and_rev (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       stmt = sqlite3_mprintf ("SELECT uid,rev FROM %Q", folderid);
-       book_backend_sql_exec (
-               ebsdb->priv->db, stmt,
-               get_uids_and_rev_cb, uids_and_rev, error);
-       sqlite3_free (stmt);
+       book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT uid, rev FROM %Q",
+               collect_uids_and_rev_cb, uids_and_rev, error, folderid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
@@ -4404,7 +5019,6 @@ e_book_backend_sqlitedb_get_is_populated (EBookBackendSqliteDB *ebsdb,
                                           const gchar *folderid,
                                           GError **error)
 {
-       gchar *stmt;
        gboolean ret = FALSE;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4412,12 +5026,9 @@ e_book_backend_sqlitedb_get_is_populated (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       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);
+       book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT is_populated FROM folders WHERE folder_id = %Q",
+               get_bool_cb, &ret, error, folderid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
@@ -4438,7 +5049,6 @@ e_book_backend_sqlitedb_set_is_populated (EBookBackendSqliteDB *ebsdb,
                                           gboolean populated,
                                           GError **error)
 {
-       gchar *stmt = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4451,12 +5061,9 @@ e_book_backend_sqlitedb_set_is_populated (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf (
-               "UPDATE folders SET is_populated = %d "
-               "WHERE folder_id = %Q", populated, folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "UPDATE folders SET is_populated = %d WHERE folder_id = %Q",
+               NULL, NULL, error, populated, folderid);
 
        if (success)
                success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
@@ -4492,7 +5099,6 @@ e_book_backend_sqlitedb_get_revision (EBookBackendSqliteDB *ebsdb,
                                       gchar **revision_out,
                                       GError **error)
 {
-       gchar *stmt;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4501,11 +5107,9 @@ e_book_backend_sqlitedb_get_revision (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       stmt = sqlite3_mprintf (
-               "SELECT revision FROM folders WHERE folder_id = %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, get_string_cb, revision_out, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT revision FROM folders WHERE folder_id = %Q", 
+               get_string_cb, revision_out, error, folderid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
@@ -4531,7 +5135,6 @@ e_book_backend_sqlitedb_set_revision (EBookBackendSqliteDB *ebsdb,
                                       const gchar *revision,
                                       GError **error)
 {
-       gchar *stmt = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4544,12 +5147,9 @@ e_book_backend_sqlitedb_set_revision (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf (
-               "UPDATE folders SET revision = %Q "
-               "WHERE folder_id = %Q", revision, folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "UPDATE folders SET revision = %Q WHERE folder_id = %Q",
+               NULL, NULL, error, revision, folderid);
 
        if (success)
                success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
@@ -4579,7 +5179,6 @@ e_book_backend_sqlitedb_get_has_partial_content (EBookBackendSqliteDB *ebsdb,
                                                  const gchar *folderid,
                                                  GError **error)
 {
-       gchar *stmt;
        gboolean ret = FALSE;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4587,12 +5186,9 @@ e_book_backend_sqlitedb_get_has_partial_content (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       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);
+       book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT partial_content FROM folders WHERE folder_id = %Q",
+               get_bool_cb, &ret, error, folderid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
@@ -4612,7 +5208,6 @@ e_book_backend_sqlitedb_set_has_partial_content (EBookBackendSqliteDB *ebsdb,
                                                  gboolean partial_content,
                                                  GError **error)
 {
-       gchar *stmt = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4625,12 +5220,9 @@ e_book_backend_sqlitedb_set_has_partial_content (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf (
-               "UPDATE folders SET partial_content = %d "
-               "WHERE folder_id = %Q", partial_content, folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "UPDATE folders SET partial_content = %d WHERE folder_id = %Q",
+               NULL, NULL, error, partial_content, folderid);
 
        if (success)
                success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
@@ -4656,7 +5248,7 @@ e_book_backend_sqlitedb_get_contact_bdata (EBookBackendSqliteDB *ebsdb,
                                            const gchar *uid,
                                            GError **error)
 {
-       gchar *stmt, *ret = NULL;
+       gchar *bdata = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
@@ -4665,20 +5257,18 @@ e_book_backend_sqlitedb_get_contact_bdata (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       stmt = sqlite3_mprintf (
-               "SELECT bdata FROM %Q WHERE uid = %Q", folderid, uid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, get_string_cb , &ret, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT bdata FROM %Q WHERE uid = %Q", 
+               get_string_cb, &bdata, error, folderid, uid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
        if (!success) {
-               g_warn_if_fail (ret == NULL);
+               g_warn_if_fail (bdata == NULL);
                return NULL;
        }
 
-       return ret;
+       return bdata;
 }
 
 /**
@@ -4695,7 +5285,6 @@ e_book_backend_sqlitedb_set_contact_bdata (EBookBackendSqliteDB *ebsdb,
                                            const gchar *value,
                                            GError **error)
 {
-       gchar *stmt = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4710,12 +5299,9 @@ e_book_backend_sqlitedb_set_contact_bdata (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf (
-               "UPDATE %Q SET bdata = %Q WHERE uid = %Q",
-               folderid, value, uid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "UPDATE %Q SET bdata = %Q WHERE uid = %Q",
+               NULL, NULL, error, folderid, value, uid);
 
        if (success)
                success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
@@ -4740,23 +5326,20 @@ e_book_backend_sqlitedb_get_sync_data (EBookBackendSqliteDB *ebsdb,
                                        const gchar *folderid,
                                        GError **error)
 {
-       gchar *stmt, *ret = NULL;
+       gchar *sync_data = NULL;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
        g_return_val_if_fail (folderid != NULL, NULL);
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       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);
+       book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT sync_data FROM folders WHERE folder_id = %Q",
+               get_string_cb, &sync_data, error, folderid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
-       return ret;
+       return sync_data;
 }
 
 /**
@@ -4772,7 +5355,6 @@ e_book_backend_sqlitedb_set_sync_data (EBookBackendSqliteDB *ebsdb,
                                        const gchar *sync_data,
                                        GError **error)
 {
-       gchar *stmt = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4786,12 +5368,9 @@ e_book_backend_sqlitedb_set_sync_data (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf (
-               "UPDATE folders SET sync_data = %Q "
-               "WHERE folder_id = %Q", sync_data, folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "UPDATE folders SET sync_data = %Q WHERE folder_id = %Q",
+               NULL, NULL, error, sync_data, folderid);
 
        if (success)
                success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
@@ -4817,7 +5396,7 @@ e_book_backend_sqlitedb_get_key_value (EBookBackendSqliteDB *ebsdb,
                                        const gchar *key,
                                        GError **error)
 {
-       gchar *stmt, *ret = NULL;
+       gchar *value = NULL;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
        g_return_val_if_fail (folderid != NULL, NULL);
@@ -4825,16 +5404,13 @@ e_book_backend_sqlitedb_get_key_value (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       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);
+       book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT value FROM keys WHERE folder_id = %Q AND key = %Q",
+               get_string_cb, &value, error, folderid, key);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
-       return ret;
+       return value;
 }
 
 /**
@@ -4851,7 +5427,6 @@ e_book_backend_sqlitedb_set_key_value (EBookBackendSqliteDB *ebsdb,
                                        const gchar *value,
                                        GError **error)
 {
-       gchar *stmt = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4866,12 +5441,9 @@ e_book_backend_sqlitedb_set_key_value (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf (
-               "INSERT or REPLACE INTO keys (key, value, folder_id) "
-               "values (%Q, %Q, %Q)", key, value, folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "INSERT or REPLACE INTO keys (key, value, folder_id) values (%Q, %Q, %Q)",
+               NULL, NULL, error, key, value, folderid);
 
        if (success)
                success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
@@ -4898,7 +5470,6 @@ e_book_backend_sqlitedb_get_partially_cached_ids (EBookBackendSqliteDB *ebsdb,
                                                   const gchar *folderid,
                                                   GError **error)
 {
-       gchar *stmt;
        GSList *uids = NULL;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
@@ -4906,12 +5477,9 @@ e_book_backend_sqlitedb_get_partially_cached_ids (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       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);
+       book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT uid FROM %Q WHERE partial_content = 1",
+               collect_uid_results_cb, &uids, error, folderid);
 
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
@@ -4930,7 +5498,6 @@ e_book_backend_sqlitedb_delete_addressbook (EBookBackendSqliteDB *ebsdb,
                                             const gchar *folderid,
                                             GError **error)
 {
-       gchar *stmt;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
@@ -4943,31 +5510,25 @@ e_book_backend_sqlitedb_delete_addressbook (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       /* delete the contacts table */
-       stmt = sqlite3_mprintf ("DROP TABLE %Q ", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "DROP TABLE %Q ",
+               NULL, NULL, error, folderid);
 
        if (!success)
                goto rollback;
 
        /* delete the key/value pairs corresponding to this table */
-       stmt = sqlite3_mprintf (
-               "DELETE FROM keys WHERE folder_id = %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "DELETE FROM keys WHERE folder_id = %Q",
+               NULL, NULL, error, folderid);
 
        if (!success)
                goto rollback;
 
        /* delete the folder from the folders table */
-       stmt = sqlite3_mprintf (
-               "DELETE FROM folders WHERE folder_id = %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, NULL, NULL, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "DELETE FROM folders WHERE folder_id = %Q",
+               NULL, NULL, error, folderid);
 
        if (!success)
                goto rollback;
@@ -5041,70 +5602,9 @@ e_book_backend_sqlitedb_remove (EBookBackendSqliteDB *ebsdb,
 }
 
 static gboolean
-upgrade_contacts_table (EBookBackendSqliteDB *ebsdb,
-                       const gchar          *folderid,
-                       const gchar          *region,
-                       const gchar          *lc_collate,
-                       GError              **error)
-{
-       gchar *stmt;
-       gboolean success = FALSE;
-       GSList *vcard_data = NULL;
-       GSList *l;
-
-       stmt = sqlite3_mprintf ("SELECT uid, vcard, NULL FROM %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, addto_vcard_list_cb, &vcard_data, error);
-       sqlite3_free (stmt);
-
-       for (l = vcard_data; success && l; l = l->next) {
-               EbSdbSearchData *const s_data = l->data;
-               EContact *contact = NULL;
-
-               /* It can be we're opening a light summary which was created without
-                * storing the vcards, such as was used in EDS versions 3.2 to 3.6.
-                *
-                * In this case we just want to skip the contacts we can't load
-                * and leave them as is in the SQLite, they will be added from
-                * the old BDB in the case of a migration anyway.
-                */
-               if (s_data->vcard)
-                       contact = e_contact_new_from_vcard_with_uid (s_data->vcard, s_data->uid);
-
-               if (contact == NULL)
-                       continue;
-
-               success = insert_contact (ebsdb, contact, folderid, TRUE, region, error);
-
-               g_object_unref (contact);
-       }
-
-       g_slist_free_full (vcard_data, destroy_search_data);
-
-       if (success) {
-
-               stmt = sqlite3_mprintf (
-                       "UPDATE folders SET countrycode = %Q WHERE folder_id = %Q",
-                       region, folderid);
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, stmt, NULL, NULL, error);
-               sqlite3_free (stmt);
-
-               stmt = sqlite3_mprintf (
-                       "UPDATE folders SET lc_collate = %Q WHERE folder_id = %Q",
-                       lc_collate, folderid);
-               success = book_backend_sql_exec (
-                       ebsdb->priv->db, stmt, NULL, NULL, error);
-               sqlite3_free (stmt);
-       }
-
-       return success;
-}
-
-static gboolean
-sqlitedb_set_locale_internal (EBookBackendSqliteDB *ebsdb,
-                             const gchar          *locale,
-                             GError              **error)
+book_backend_sqlitedb_set_locale_internal (EBookBackendSqliteDB *ebsdb,
+                                          const gchar          *locale,
+                                          GError              **error)
 {
        EBookBackendSqliteDBPrivate *priv = ebsdb->priv;
        ECollator *collator;
@@ -5153,7 +5653,6 @@ e_book_backend_sqlitedb_set_locale (EBookBackendSqliteDB *ebsdb,
                                    GError              **error)
 {
        gboolean success;
-       gchar *stmt;
        gchar *stored_lc_collate;
        gchar *current_region = NULL;
 
@@ -5171,7 +5670,7 @@ e_book_backend_sqlitedb_set_locale (EBookBackendSqliteDB *ebsdb,
                }
        }
 
-       if (!sqlitedb_set_locale_internal (ebsdb, lc_collate, error)) {
+       if (!book_backend_sqlitedb_set_locale_internal (ebsdb, lc_collate, error)) {
                UNLOCK_MUTEX (&ebsdb->priv->lock);
                g_free (current_region);
                return FALSE;
@@ -5183,16 +5682,16 @@ e_book_backend_sqlitedb_set_locale (EBookBackendSqliteDB *ebsdb,
                return FALSE;
        }
 
-       stmt = sqlite3_mprintf ("SELECT lc_collate FROM folders WHERE folder_id = %Q", folderid);
-       success = book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb, &stored_lc_collate, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT lc_collate FROM folders WHERE folder_id = %Q",
+               get_string_cb, &stored_lc_collate, error, folderid);
 
        if (success && g_strcmp0 (stored_lc_collate, lc_collate) != 0)
-               success = upgrade_contacts_table (ebsdb, folderid, current_region, lc_collate, error);
+               success = book_backend_sqlitedb_upgrade (ebsdb, folderid, current_region, lc_collate, error);
 
        /* If for some reason we failed, then reset the collator to use the old locale */
        if (!success)
-               sqlitedb_set_locale_internal (ebsdb, stored_lc_collate, NULL);
+               book_backend_sqlitedb_set_locale_internal (ebsdb, stored_lc_collate, NULL);
 
        g_free (stored_lc_collate);
        g_free (current_region);
@@ -5236,7 +5735,6 @@ e_book_backend_sqlitedb_get_locale (EBookBackendSqliteDB *ebsdb,
                                    gchar               **locale_out,
                                    GError              **error)
 {
-       gchar *stmt;
        gboolean success;
        GError *local_error = NULL;
 
@@ -5246,13 +5744,11 @@ e_book_backend_sqlitedb_get_locale (EBookBackendSqliteDB *ebsdb,
 
        LOCK_MUTEX (&ebsdb->priv->lock);
 
-       stmt = sqlite3_mprintf (
-               "SELECT lc_collate FROM folders WHERE folder_id = %Q", folderid);
-       success = book_backend_sql_exec (
-               ebsdb->priv->db, stmt, get_string_cb, locale_out, error);
-       sqlite3_free (stmt);
+       success = book_backend_sqlitedb_exec_printf (
+               ebsdb, "SELECT lc_collate FROM folders WHERE folder_id = %Q",
+               get_string_cb, locale_out, error, folderid);
 
-       if (!sqlitedb_set_locale_internal (ebsdb, *locale_out, &local_error)) {
+       if (!book_backend_sqlitedb_set_locale_internal (ebsdb, *locale_out, &local_error)) {
                g_warning ("Error loading new locale: %s", local_error->message);
                g_clear_error (&local_error);
        }
@@ -5447,19 +5943,17 @@ ebsdb_cursor_order_by_fragment (EBookBackendSqliteDB *ebsdb,
                                gboolean              reverse)
 {
        GString *string;
-       const gchar *field_name;
        gint i;
 
        string = g_string_new ("ORDER BY ");
 
        for (i = 0; i < n_sort_fields; i++) {
-
-               field_name = summary_dbname_from_field (ebsdb, sort_fields[i]);
+               SummaryField *field = summary_field_get (ebsdb, sort_fields[i]);
 
                if (i > 0)
                        g_string_append (string, ", ");
 
-               g_string_append_printf (string, "summary.%s_localized %s", field_name,
+               g_string_append_printf (string, "summary.%s_localized %s", field->dbname,
                                        reverse ?
                                        (sort_types[i] == E_BOOK_CURSOR_SORT_ASCENDING ? "DESC" : "ASC") :
                                        (sort_types[i] == E_BOOK_CURSOR_SORT_ASCENDING ? "ASC"  : "DESC"));
@@ -5547,7 +6041,7 @@ ebsdb_cursor_constraints (EBookBackendSqliteDB *ebsdb,
                          gboolean              include_current_uid)
 {
        GString *string;
-       const gchar *field_name;
+       SummaryField *field;
        gint i, j;
 
        /* Example for:
@@ -5590,16 +6084,15 @@ ebsdb_cursor_constraints (EBookBackendSqliteDB *ebsdb,
 
                /* Create the '=' statements leading up to the current tie breaker */
                for (j = 0; j < i; j++) {
-                       field_name = summary_dbname_from_field (ebsdb, cursor->sort_fields[j]);
+                       field = summary_field_get (ebsdb, cursor->sort_fields[j]);
 
                        stmt = sqlite3_mprintf ("summary.%s_localized = %Q",
-                                               field_name, state->values[j]);
+                                               field->dbname, state->values[j]);
 
                        g_string_append (string, stmt);
                        g_string_append (string, " AND ");
 
                        sqlite3_free (stmt);
-
                }
 
                if (i == cursor->n_sort_fields) {
@@ -5643,10 +6136,9 @@ ebsdb_cursor_constraints (EBookBackendSqliteDB *ebsdb,
                                g_string_append_c (string, '(');
 
                        /* Append the final qualifier for this field */
-                       field_name = summary_dbname_from_field (ebsdb, cursor->sort_fields[i]);
-
+                       field = summary_field_get (ebsdb, cursor->sort_fields[i]);
                        stmt = sqlite3_mprintf ("summary.%s_localized %c %Q",
-                                               field_name,
+                                               field->dbname,
                                                GREATER_OR_LESS (cursor, i, reverse),
                                                state->values[i]);
 
@@ -5656,7 +6148,7 @@ ebsdb_cursor_constraints (EBookBackendSqliteDB *ebsdb,
                        if (include_exact_match) {
 
                                stmt = sqlite3_mprintf (" OR summary.%s_localized = %Q",
-                                                       field_name, state->values[i]);
+                                                       field->dbname, state->values[i]);
 
                                g_string_append (string, stmt);
                                g_string_append_c (string, ')');
@@ -5692,8 +6184,8 @@ cursor_count_total_locked (EBookBackendSqliteDB *ebsdb,
        }
 
        /* Execute the query */
-       success = book_backend_sql_exec (ebsdb->priv->db, query->str,
-                                        get_count_cb, total, error);
+       success = book_backend_sqlitedb_exec (ebsdb, query->str,
+                                             get_count_cb, total, error);
 
        g_string_free (query, TRUE);
 
@@ -5745,8 +6237,8 @@ cursor_count_position_locked (EBookBackendSqliteDB *ebsdb,
        }
 
        /* Execute the query */
-       success = book_backend_sql_exec (ebsdb->priv->db, query->str,
-                                        get_count_cb, position, error);
+       success = book_backend_sqlitedb_exec (ebsdb, query->str,
+                                             get_count_cb, position, error);
 
        g_string_free (query, TRUE);
 
@@ -6049,9 +6541,9 @@ e_book_backend_sqlitedb_cursor_step (EBookBackendSqliteDB *ebsdb,
 
        /* Execute the query */
        LOCK_MUTEX (&ebsdb->priv->lock);
-       success = book_backend_sql_exec (ebsdb->priv->db, query->str,
-                                        collect_results_for_cursor_cb, &data,
-                                        error);
+       success = book_backend_sqlitedb_exec (ebsdb, query->str,
+                                             collect_results_for_cursor_cb, &data,
+                                             error);
        UNLOCK_MUTEX (&ebsdb->priv->lock);
 
        g_string_free (query, TRUE);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]