commit 6781f124cbc1ed93437ce40474e40e8e0336d02a
Author: Chenthill Palanisamy <pchenthill@novell.com>
Date:   Tue Jun 7 15:34:04 2011 +0530

    Bug 640054 - sqlite summary hang. Fix vfolder transactions.

diff --git a/camel/camel-vee-folder.c b/camel/camel-vee-folder.c
index ec4caa4..5ec9ad8 100644
--- a/camel/camel-vee-folder.c
+++ b/camel/camel-vee-folder.c
@@ -66,6 +66,10 @@ struct _update_data {
 	CamelVeeFolder *folder_unmatched;
 	GHashTable *unmatched_uids;
 	gboolean rebuilt, correlating;
+	
+	/* used for storing uids that needs to be updated in db later by unmatched_check_uid and
+   	   folder_added_uid */	   
+	GSList *uids_to_update;
 };
 
 struct _folder_changed_msg {
@@ -125,12 +129,15 @@ expression_is_correlating (const gchar *expr)
 
 /* Hold all these with summary lock and unmatched summary lock held */
 static void
-folder_changed_add_uid (CamelFolder *sub, const gchar *uid, const gchar hash[8], CamelVeeFolder *vf, gboolean use_db)
+folder_changed_add_uid (CamelFolder *sub, 
+			const gchar *uid, 
+			const gchar hash[8], 
+			CamelVeeFolder *vf, 
+			gboolean use_db,
+			GSList **m_added_l,
+			GSList **unm_added_l)
 {
-	CamelFolder *folder = CAMEL_FOLDER (vf);
-	CamelStore *parent_store;
 	CamelVeeMessageInfo *vinfo;
-	const gchar *full_name;
 	const gchar *vuid;
 	gchar *oldkey;
 	gpointer oldval;
@@ -138,19 +145,16 @@ folder_changed_add_uid (CamelFolder *sub, const gchar *uid, const gchar hash[8],
 	CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
 	GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
 
-	parent_store = camel_folder_get_parent_store (folder);
-
 	vinfo = vee_folder_add_uid (vf, sub, uid, hash);
 	if (vinfo == NULL)
 		return;
 
 	vuid = camel_pstring_strdup (camel_message_info_uid (vinfo));
 	camel_message_info_free ((CamelMessageInfo *) vinfo);
-	if (use_db) {
-		full_name = camel_folder_get_full_name (folder);
-		camel_db_add_to_vfolder_transaction (
-			parent_store->cdb_w, full_name, vuid, NULL);
-	}
+	
+	if (use_db)
+		*m_added_l = g_slist_prepend (*m_added_l, (gpointer) camel_pstring_strdup (vuid));
+
 	camel_folder_change_info_add_uid (vf->changes,  vuid);
 	if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER (sub) && folder_unmatched != NULL) {
 		if (g_hash_table_lookup_extended (unmatched_uids, vuid, (gpointer *)&oldkey, &oldval)) {
@@ -161,12 +165,11 @@ folder_changed_add_uid (CamelFolder *sub, const gchar *uid, const gchar hash[8],
 		}
 		vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info ((CamelFolder *)folder_unmatched, vuid);
 		if (vinfo) {
-			full_name = camel_folder_get_full_name (
-				CAMEL_FOLDER (folder_unmatched));
 			camel_folder_change_info_remove_uid (
 				folder_unmatched->changes, vuid);
-			camel_db_delete_uid_from_vfolder_transaction (
-				parent_store->cdb_w, full_name, vuid, NULL);
+		
+			*unm_added_l = g_slist_prepend (*unm_added_l, (gpointer) camel_pstring_strdup (vuid));
+			
 			camel_folder_summary_remove_uid_fast (
 				CAMEL_FOLDER (folder_unmatched)->summary, vuid);
 			camel_folder_free_message_info (
@@ -179,11 +182,16 @@ folder_changed_add_uid (CamelFolder *sub, const gchar *uid, const gchar hash[8],
 }
 
 static void
-folder_changed_remove_uid (CamelFolder *sub, const gchar *uid, const gchar hash[8], gint keep, CamelVeeFolder *vf, gboolean use_db)
+folder_changed_remove_uid 	(CamelFolder *sub, 
+				 const gchar *uid, 
+				 const gchar hash[8], 
+				 gint keep, 
+				 CamelVeeFolder *vf, 
+				 gboolean use_db,
+				 GSList **m_removed_l,
+				 GSList **unm_removed_l)
 {
 	CamelFolder *folder = (CamelFolder *)vf;
-	CamelStore *parent_store;
-	const gchar *full_name;
 	gchar *vuid, *oldkey;
 	gpointer oldval;
 	gint n;
@@ -195,14 +203,10 @@ folder_changed_remove_uid (CamelFolder *sub, const gchar *uid, const gchar hash[
 	memcpy (vuid, hash, 8);
 	strcpy (vuid+8, uid);
 
-	parent_store = camel_folder_get_parent_store (folder);
-
 	camel_folder_change_info_remove_uid (vf->changes, vuid);
-	if (use_db) {
-		full_name = camel_folder_get_full_name (folder);
-		camel_db_delete_uid_from_vfolder_transaction (
-			parent_store->cdb_w, full_name, vuid, NULL);
-	}
+	if (use_db)
+		*m_removed_l = g_slist_prepend (*m_removed_l, (gpointer) camel_pstring_strdup (vuid));
+
 	camel_folder_summary_remove_uid_fast (folder->summary, vuid);
 
 	if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0 && !CAMEL_IS_VEE_FOLDER (sub) && folder_unmatched != NULL) {
@@ -229,12 +233,11 @@ folder_changed_remove_uid (CamelFolder *sub, const gchar *uid, const gchar hash[
 
 			vinfo = (CamelVeeMessageInfo *)camel_folder_get_message_info ((CamelFolder *)folder_unmatched, vuid);
 			if (vinfo) {
-				full_name = camel_folder_get_full_name (
-					CAMEL_FOLDER (folder_unmatched));
 				camel_folder_change_info_remove_uid (
 					folder_unmatched->changes, vuid);
-				camel_db_delete_uid_from_vfolder_transaction (
-					parent_store->cdb_w, full_name, vuid, NULL);
+		
+				*unm_removed_l = g_slist_prepend (*unm_removed_l, (gpointer) camel_pstring_strdup (vuid));
+				
 				camel_folder_summary_remove_uid_fast (
 					CAMEL_FOLDER (folder_unmatched)->summary, vuid);
 				camel_folder_free_message_info (
@@ -246,7 +249,13 @@ folder_changed_remove_uid (CamelFolder *sub, const gchar *uid, const gchar hash[
 }
 
 static void
-folder_changed_change_uid (CamelFolder *sub, const gchar *uid, const gchar hash[8], CamelVeeFolder *vf, gboolean use_db)
+folder_changed_change_uid	(CamelFolder *sub, 
+				 const gchar *uid, 
+				 const gchar hash[8], 
+				 CamelVeeFolder *vf, 
+				 gboolean use_db, 
+				 GSList **m_removed_l,
+				 GSList **unm_removed_l)
 {
 	gchar *vuid;
 	CamelVeeMessageInfo *vinfo, *uinfo = NULL;
@@ -277,7 +286,7 @@ folder_changed_change_uid (CamelFolder *sub, const gchar *uid, const gchar hash[
 			camel_folder_free_message_info (sub, info);
 		} else {
 			if (vinfo) {
-				folder_changed_remove_uid (sub, uid, hash, FALSE, vf, use_db);
+				folder_changed_remove_uid (sub, uid, hash, FALSE, vf, use_db, m_removed_l, unm_removed_l);
 				camel_message_info_free ((CamelMessageInfo *)vinfo);
 			}
 			if (uinfo)
@@ -286,6 +295,27 @@ folder_changed_change_uid (CamelFolder *sub, const gchar *uid, const gchar hash[
 	}
 }
 
+
+static void
+vfolder_add_remove_transaction	(CamelStore *parent_store, const gchar *full_name, GSList **uids, gboolean add, GError **error)
+{
+	GSList *l;
+
+	for (l = *uids; l != NULL; l = g_slist_next (l)) {
+		if (add)	
+			 camel_db_add_to_vfolder_transaction	(parent_store->cdb_w, full_name,
+				                                (const gchar *) l->data, error);
+		else
+			camel_db_delete_uid_from_vfolder_transaction 	
+								(parent_store->cdb_w, full_name, 
+								 (const gchar *) l->data, error);
+	}
+	
+	g_slist_foreach (*uids, (GFunc) camel_pstring_free, NULL);
+	g_slist_free (*uids);
+	*uids = NULL;
+}
+
 static void
 folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 {
@@ -294,7 +324,6 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 	CamelFolder *folder = (CamelFolder *)m->vee_folder;
 	CamelVeeFolder *vf = m->vee_folder;
 	CamelFolderChangeInfo *changes = m->changes;
-	CamelStore *parent_store;
 	gchar *vuid = NULL, hash[8];
 	const gchar *uid;
 	CamelVeeMessageInfo *vinfo;
@@ -309,6 +338,7 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 	CamelVeeFolder *folder_unmatched = vf->parent_vee_store ? vf->parent_vee_store->folder_unmatched : NULL;
 	GHashTable *unmatched_uids = vf->parent_vee_store ? vf->parent_vee_store->unmatched_uids : NULL;
 	GPtrArray *present = NULL;
+	GSList *m_added_l = NULL, *m_removed_l = NULL, *unm_added_l = NULL, *unm_removed_l = NULL;
 
 	/* See vee_folder_rebuild_folder. */
 	gboolean correlating = expression_is_correlating (vf->expression);
@@ -372,15 +402,10 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 	if (folder_unmatched != NULL)
 		camel_vee_folder_lock (folder_unmatched, CAMEL_VEE_FOLDER_SUMMARY_LOCK);
 
-	if (matches_changed || matches_added || changes->uid_removed->len||present) {
-		parent_store = camel_folder_get_parent_store (folder);
-		camel_db_begin_transaction (parent_store->cdb_w, NULL);
-	}
-
 	/* Always remove removed uid's, in any case */
 	for (i=0;i<changes->uid_removed->len;i++) {
 		dd (printf ("  removing uid '%s'\n", (gchar *)changes->uid_removed->pdata[i]));
-		folder_changed_remove_uid (sub, changes->uid_removed->pdata[i], hash, FALSE, vf, !correlating);
+		folder_changed_remove_uid (sub, changes->uid_removed->pdata[i], hash, FALSE, vf, !correlating, &m_removed_l, &unm_removed_l);
 	}
 
 	/* Add any newly matched or to unmatched folder if they dont */
@@ -394,7 +419,7 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 			uid = changes->uid_added->pdata[i];
 			if (g_hash_table_lookup (matches_hash, uid)) {
 				dd (printf ("  adding uid '%s' [newly matched]\n", (gchar *)uid));
-				folder_changed_add_uid (sub, uid, hash, vf, !correlating);
+				folder_changed_add_uid (sub, uid, hash, vf, !correlating, &m_added_l, &unm_added_l);
 			} else if ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) {
 				if (strlen (uid)+9 > vuidlen) {
 					vuidlen = strlen (uid)+64;
@@ -427,12 +452,12 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 			 * vee_folder_rebuild_folder will make any necessary removals
 			 * when it re-queries the entire source folder. */
 			for (i=0;i<always_changed->len;i++)
-				folder_changed_change_uid (sub, always_changed->pdata[i], hash, vf, !correlating);
+				folder_changed_change_uid (sub, always_changed->pdata[i], hash, vf, !correlating, &m_removed_l, &unm_removed_l);
 		} else {
 			GHashTable *ht_present = g_hash_table_new (g_str_hash, g_str_equal);
 
 			for (i=0;present && i<present->len;i++) {
-				folder_changed_change_uid (sub, present->pdata[i], hash, vf, !correlating);
+				folder_changed_change_uid (sub, present->pdata[i], hash, vf, !correlating, &m_removed_l, &unm_removed_l);
 				g_hash_table_insert (ht_present, present->pdata[i], present->pdata[i]);
 			}
 
@@ -440,7 +465,7 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 				if (!present || !g_hash_table_lookup (ht_present, always_changed->pdata[i]))
 					/* XXX: IIUC, these messages haven't been deleted from the
 					 * source folder, so shouldn't "keep" be set to TRUE? */
-					folder_changed_remove_uid (sub, always_changed->pdata[i], hash, TRUE, vf, !correlating);
+					folder_changed_remove_uid (sub, always_changed->pdata[i], hash, TRUE, vf, !correlating, &m_removed_l, &unm_removed_l);
 			}
 
 			g_hash_table_destroy (ht_present);
@@ -472,21 +497,21 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 				if (g_hash_table_lookup (matches_hash, uid)) {
 					/* A uid we dont have, but now it matches, add it */
 					dd (printf ("  adding uid '%s' [newly matched]\n", uid));
-					folder_changed_add_uid (sub, uid, hash, vf, !correlating);
+					folder_changed_add_uid (sub, uid, hash, vf, !correlating, &m_added_l, &unm_added_l);
 				} else {
 					/* A uid we still don't have, just change it (for unmatched) */
-					folder_changed_change_uid (sub, uid, hash, vf, !correlating);
+					folder_changed_change_uid (sub, uid, hash, vf, !correlating, &m_removed_l, &unm_removed_l);
 				}
 			} else {
 				if ((vf->flags & CAMEL_STORE_VEE_FOLDER_AUTO) == 0
 				    || g_hash_table_lookup (matches_hash, uid)) {
 					/* still match, or we're not auto-updating, change event, (if it changed) */
 					dd (printf ("  changing uid '%s' [still matches]\n", uid));
-					folder_changed_change_uid (sub, uid, hash, vf, !correlating);
+					folder_changed_change_uid (sub, uid, hash, vf, !correlating, &m_removed_l, &unm_removed_l);
 				} else {
 					/* No longer matches, remove it, but keep it in unmatched (potentially) */
 					dd (printf ("  removing uid '%s' [did match]\n", uid));
-					folder_changed_remove_uid (sub, uid, hash, TRUE, vf, !correlating);
+					folder_changed_remove_uid (sub, uid, hash, TRUE, vf, !correlating, &m_removed_l, &unm_removed_l);
 				}
 				camel_message_info_free ((CamelMessageInfo *)vinfo);
 			}
@@ -495,7 +520,7 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 	} else {
 		/* stuff didn't match but it changed - check unmatched folder for changes */
 		for (i=0;i<changed->len;i++)
-			folder_changed_change_uid (sub, changed->pdata[i], hash, vf, !correlating);
+			folder_changed_change_uid (sub, changed->pdata[i], hash, vf, !correlating, &m_removed_l, &unm_removed_l);
 	}
 
 	if (folder_unmatched != NULL) {
@@ -513,9 +538,27 @@ folder_changed_change (CamelSession *session, CamelSessionThreadMsg *msg)
 	}
 
 	if (matches_changed || matches_added || changes->uid_removed->len || present) {
+		const gchar *full_name, *unm_full_name;
+		CamelStore *parent_store;
+
 		parent_store = camel_folder_get_parent_store (folder);
+		full_name = camel_folder_get_full_name (folder);
+		unm_full_name = camel_folder_get_full_name (CAMEL_FOLDER (folder_unmatched));
+		
+		camel_db_begin_transaction (parent_store->cdb_w, NULL);
+	
+		if (m_added_l)	
+			vfolder_add_remove_transaction (parent_store, full_name, &m_added_l, TRUE, NULL);
+		if (m_removed_l)
+			vfolder_add_remove_transaction (parent_store, full_name, &m_removed_l, FALSE, NULL);
+		if (unm_added_l)	
+			vfolder_add_remove_transaction (parent_store, unm_full_name, &unm_added_l, TRUE, NULL);
+		if (unm_removed_l)
+			vfolder_add_remove_transaction (parent_store, unm_full_name, &unm_removed_l, FALSE, NULL);
+		
 		camel_db_end_transaction (parent_store->cdb_w, NULL);
 	}
+
 	camel_vee_folder_unlock (vf, CAMEL_VEE_FOLDER_SUMMARY_LOCK);
 
 	/* Cleanup stuff on our folder */
@@ -666,16 +709,7 @@ unmatched_check_uid (gchar *uidin, gpointer value, struct _update_data *u)
 	} else {
 		CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_uid (((CamelFolder *)u->folder_unmatched)->summary, uid);
 		if (mi) {
-			CamelStore *parent_store;
-			const gchar *full_name;
-
-			full_name = camel_folder_get_full_name (
-				CAMEL_FOLDER (u->folder_unmatched));
-			parent_store = camel_folder_get_parent_store (
-				CAMEL_FOLDER (u->folder_unmatched));
-
-			camel_db_delete_uid_from_vfolder_transaction (
-				parent_store->cdb_w, full_name, uid, NULL);
+			u->uids_to_update = g_slist_prepend (u->uids_to_update, (gpointer) camel_pstring_strdup (uid));
 			camel_folder_summary_remove_uid_fast (
 				((CamelFolder *)u->folder_unmatched)->summary, uid);
 			camel_folder_change_info_remove_uid (
@@ -695,21 +729,9 @@ folder_added_uid (gchar *uidin, gpointer value, struct _update_data *u)
 
 	if ((mi = vee_folder_add_uid (u->vee_folder, u->source, uidin, u->hash)) != NULL) {
 		camel_folder_change_info_add_uid (u->vee_folder->changes, camel_message_info_uid (mi));
-		/* FIXME[disk-summary] Handle exceptions */
-		/* FIXME[disk-summary] Make all these as transactions, just
-		 * testing atm */
-		if (u->rebuilt && !u->correlating) {
-			CamelStore *parent_store;
-			const gchar *full_name;
-
-			full_name = camel_folder_get_full_name (
-				CAMEL_FOLDER (u->vee_folder));
-			parent_store = camel_folder_get_parent_store (
-				CAMEL_FOLDER (u->vee_folder));
-			camel_db_add_to_vfolder_transaction (
-				parent_store->cdb_w, full_name,
-				camel_message_info_uid (mi), NULL);
-		}
+		if (u->rebuilt && !u->correlating) 
+			u->uids_to_update = g_slist_prepend (u->uids_to_update, (gpointer) camel_pstring_strdup (camel_message_info_uid (mi)));
+		
 		if (!CAMEL_IS_VEE_FOLDER (u->source) && u->unmatched_uids != NULL) {
 			if (g_hash_table_lookup_extended (u->unmatched_uids, camel_message_info_uid (mi), (gpointer *)&oldkey, &oldval)) {
 				n = GPOINTER_TO_INT (oldval);
@@ -1670,6 +1692,7 @@ vee_folder_rebuild_folder (CamelVeeFolder *vee_folder,
 	u.unmatched_uids = unmatched_uids;
 	u.rebuilt = rebuilded;
 	u.correlating = correlating;
+	u.uids_to_update = NULL;
 
 	camel_vee_folder_lock (vee_folder, CAMEL_VEE_FOLDER_SUMMARY_LOCK);
 
@@ -1752,19 +1775,18 @@ vee_folder_rebuild_folder (CamelVeeFolder *vee_folder,
 		camel_folder_summary_remove_range (folder->summary, start, last);
 
 	/* now matchhash contains any new uid's, add them, etc */
-	if (rebuilded && !correlating) {
-		CamelStore *parent_store;
-
-		parent_store = camel_folder_get_parent_store (folder);
-		camel_db_begin_transaction (parent_store->cdb_w, NULL);
-	}
-
 	g_hash_table_foreach (matchhash, (GHFunc)folder_added_uid, &u);
 
-	if (rebuilded && !correlating) {
+	if (rebuilded && !correlating && u.uids_to_update) {
 		CamelStore *parent_store;
+		const gchar *full_name;
 
 		parent_store = camel_folder_get_parent_store (folder);
+		full_name = camel_folder_get_full_name (folder);
+
+		/* FIXME[disk-summary] Handle exceptions */
+		camel_db_begin_transaction (parent_store->cdb_w, NULL);
+		vfolder_add_remove_transaction (parent_store, full_name, &(u.uids_to_update), TRUE, NULL);
 		camel_db_end_transaction (parent_store->cdb_w, NULL);
 	}
 
@@ -1790,8 +1812,23 @@ vee_folder_rebuild_folder (CamelVeeFolder *vee_folder,
 		}
 
 		/* now allhash contains all potentially new uid's for the unmatched folder, process */
-		if (!CAMEL_IS_VEE_FOLDER (source))
+		if (!CAMEL_IS_VEE_FOLDER (source)) {
 			g_hash_table_foreach (allhash, (GHFunc)unmatched_check_uid, &u);
+			
+			if (u.uids_to_update) {
+				CamelStore *parent_store;
+				const gchar *full_name;
+
+				full_name = camel_folder_get_full_name (
+						CAMEL_FOLDER (u.folder_unmatched));
+				parent_store = camel_folder_get_parent_store (
+						CAMEL_FOLDER (u.folder_unmatched));
+
+				camel_db_begin_transaction (parent_store->cdb_w, NULL);
+				vfolder_add_remove_transaction (parent_store, full_name, &(u.uids_to_update), FALSE, NULL);
+				camel_db_end_transaction (parent_store->cdb_w, NULL);
+			}
+		}
 
 		/* copy any changes so we can raise them outside the lock */
 		if (camel_folder_change_info_changed (folder_unmatched->changes)) {
