diff --git a/mozilla/mail/base/content/msgMail3PaneWindow.js b/mozilla/mail/base/content/msgMail3PaneWindow.js index e7960f67424..c14f38f7de1 100644 --- a/mozilla/mail/base/content/msgMail3PaneWindow.js +++ b/mozilla/mail/base/content/msgMail3PaneWindow.js @@ -275,6 +275,7 @@ var folderListener = { { gDefaultSearchViewTerms = null; viewDebug("searching gVirtualFolderTerms\n"); + gDBView.viewFolder = gMsgFolderSelected; loadVirtualFolder(); } else if (gMsgFolderSelected.flags & MSG_FOLDER_FLAG_VIRTUAL) diff --git a/mozilla/mailnews/base/search/src/nsMsgLocalSearch.cpp b/mozilla/mailnews/base/search/src/nsMsgLocalSearch.cpp index 985e3c62215..394e63436af 100644 --- a/mozilla/mailnews/base/search/src/nsMsgLocalSearch.cpp +++ b/mozilla/mailnews/base/search/src/nsMsgLocalSearch.cpp @@ -616,15 +616,6 @@ nsresult nsMsgSearchOfflineMail::MatchTerms(nsIMsgDBHdr *msgToMatch, NS_ENSURE_ARG(aExpressionTree); nsresult err; - // Don't even bother to look at expunged messages awaiting compression - PRUint32 msgFlags; - msgToMatch->GetFlags(&msgFlags); - if (msgFlags & MSG_FLAG_EXPUNGED) - { - *pResult = PR_FALSE; - return NS_OK; - } - if (!*aExpressionTree) { PRUint32 initialPos = 0; diff --git a/mozilla/mailnews/base/search/src/nsMsgSearchSession.cpp b/mozilla/mailnews/base/search/src/nsMsgSearchSession.cpp index f4d2bdd3f8f..a9e9788652b 100644 --- a/mozilla/mailnews/base/search/src/nsMsgSearchSession.cpp +++ b/mozilla/mailnews/base/search/src/nsMsgSearchSession.cpp @@ -171,7 +171,8 @@ nsMsgSearchSession::GetNthSearchTerm(PRInt32 whichTerm, NS_IMETHODIMP nsMsgSearchSession::CountSearchScopes(PRInt32 *_retval) { NS_ENSURE_ARG(_retval); - return NS_ERROR_NOT_IMPLEMENTED; + *_retval = m_scopeList.Count(); + return NS_OK; } /* void GetNthSearchScope (in long which, out nsMsgSearchScope scopeId, out nsIMsgFolder folder); */ diff --git a/mozilla/mailnews/base/src/nsMsgAccountManager.cpp b/mozilla/mailnews/base/src/nsMsgAccountManager.cpp index 25c93ac303f..8194cbf7a2c 100644 --- a/mozilla/mailnews/base/src/nsMsgAccountManager.cpp +++ b/mozilla/mailnews/base/src/nsMsgAccountManager.cpp @@ -2750,6 +2750,13 @@ NS_IMETHODIMP VirtualFolderChangeListener::OnHdrChange(nsIMsgDBHdr *aHdrChanged, if (numNewMessages == 1) m_virtualFolder->SetHasNewMessages(PR_FALSE); } + if (totalDelta) + { + nsXPIDLCString searchUri; + m_virtualFolder->GetURI(getter_Copies(searchUri)); + msgDB->UpdateHdrInCache(searchUri, aHdrChanged, totalDelta == 1); + } + m_virtualFolder->UpdateSummaryTotals(PR_TRUE); // force update from db. virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } @@ -2787,6 +2794,10 @@ NS_IMETHODIMP VirtualFolderChangeListener::OnHdrDeleted(nsIMsgDBHdr *aHdrDeleted if (numNewMessages == 1) m_virtualFolder->SetHasNewMessages(PR_FALSE); } + nsXPIDLCString searchUri; + m_virtualFolder->GetURI(getter_Copies(searchUri)); + msgDB->UpdateHdrInCache(searchUri, aHdrDeleted, PR_FALSE); + m_virtualFolder->UpdateSummaryTotals(PR_TRUE); // force update from db. virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } @@ -2822,6 +2833,9 @@ NS_IMETHODIMP VirtualFolderChangeListener::OnHdrAdded(nsIMsgDBHdr *aNewHdr, nsMs m_virtualFolder->SetHasNewMessages(PR_TRUE); m_virtualFolder->SetNumNewMessages(numNewMessages + 1); } + nsXPIDLCString searchUri; + m_virtualFolder->GetURI(getter_Copies(searchUri)); + msgDB->UpdateHdrInCache(searchUri, aNewHdr, PR_TRUE); dbFolderInfo->ChangeNumMessages(1); m_virtualFolder->UpdateSummaryTotals(true); // force update from db. virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit); @@ -2970,20 +2984,8 @@ NS_IMETHODIMP nsMsgAccountManager::LoadVirtualFolders() // and we have to add a pending listener for each of them. if (buffer.Length()) { - nsCStringArray folderUris; dbFolderInfo->SetCharPtrProperty("searchFolderUri", buffer.get()); - folderUris.ParseString(buffer.get(), "|"); - for (PRInt32 i = 0; i < folderUris.Count(); i++) - { - rdf->GetResource(*(folderUris[i]), getter_AddRefs(resource)); - nsCOMPtr realFolder = do_QueryInterface(resource); - VirtualFolderChangeListener *dbListener = new VirtualFolderChangeListener(); - m_virtualFolderListeners.AppendObject(dbListener); - dbListener->m_virtualFolder = virtualFolder; - dbListener->m_folderWatching = realFolder; - dbListener->Init(); - msgDBService->RegisterPendingListener(realFolder, dbListener); - } + AddVFListenersForVF(virtualFolder, buffer.get(), rdf, msgDBService); } else // this folder is useless { @@ -3092,6 +3094,29 @@ nsresult nsMsgAccountManager::WriteLineToOutputStream(const char *prefix, const return NS_OK; } +nsresult nsMsgAccountManager::AddVFListenersForVF(nsIMsgFolder *virtualFolder, + const char *srchFolderUris, + nsIRDFService *rdf, + nsIMsgDBService *msgDBService) +{ + nsCStringArray folderUris; + folderUris.ParseString(srchFolderUris, "|"); + nsCOMPtr resource; + + for (PRInt32 i = 0; i < folderUris.Count(); i++) + { + rdf->GetResource(*(folderUris[i]), getter_AddRefs(resource)); + nsCOMPtr realFolder = do_QueryInterface(resource); + VirtualFolderChangeListener *dbListener = new VirtualFolderChangeListener(); + NS_ENSURE_TRUE(dbListener, NS_ERROR_OUT_OF_MEMORY); + m_virtualFolderListeners.AppendObject(dbListener); + dbListener->m_virtualFolder = virtualFolder; + dbListener->m_folderWatching = realFolder; + dbListener->Init(); + msgDBService->RegisterPendingListener(realFolder, dbListener); + } +} + NS_IMETHODIMP nsMsgAccountManager::OnItemAdded(nsIRDFResource *parentItem, nsISupports *item) { nsCOMPtr folder = do_QueryInterface(item); @@ -3108,23 +3133,14 @@ NS_IMETHODIMP nsMsgAccountManager::OnItemAdded(nsIRDFResource *parentItem, nsISu nsCOMPtr msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv); if (msgDBService) { - VirtualFolderChangeListener *dbListener = new VirtualFolderChangeListener(); - dbListener->m_virtualFolder = folder; nsCOMPtr virtDatabase; nsCOMPtr dbFolderInfo; - m_virtualFolderListeners.AppendObject(dbListener); rv = folder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase)); NS_ENSURE_SUCCESS(rv, rv); nsXPIDLCString srchFolderUri; dbFolderInfo->GetCharPtrProperty("searchFolderUri", getter_Copies(srchFolderUri)); - // if we supported cross folder virtual folders, we'd have a list of folders uris, - // and we'd have to add a pending listener for each of them. - rv = GetExistingFolder(srchFolderUri.get(), getter_AddRefs(dbListener->m_folderWatching)); - if (dbListener->m_folderWatching) - { - dbListener->Init(); - msgDBService->RegisterPendingListener(dbListener->m_folderWatching, dbListener); - } + nsCOMPtr rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv)); + AddVFListenersForVF(folder, srchFolderUri, rdf, msgDBService); } rv = SaveVirtualFolders(); } diff --git a/mozilla/mailnews/base/src/nsMsgAccountManager.h b/mozilla/mailnews/base/src/nsMsgAccountManager.h index a814afa1886..16323573b4d 100644 --- a/mozilla/mailnews/base/src/nsMsgAccountManager.h +++ b/mozilla/mailnews/base/src/nsMsgAccountManager.h @@ -56,6 +56,8 @@ #include "nsIUrlListener.h" #include "nsCOMArray.h" +class nsIRDFService; + class nsMsgAccountManager: public nsIMsgAccountManager, public nsIObserver, public nsSupportsWeakReference, @@ -205,6 +207,10 @@ private: // handle virtual folders nsresult GetVirtualFoldersFile(nsCOMPtr& file); nsresult WriteLineToOutputStream(const char *prefix, const char * line, nsIOutputStream *outputStream); + nsresult AddVFListenersForVF(nsIMsgFolder *virtualFolder, + const char *srchFolderUris, + nsIRDFService *rdf, + nsIMsgDBService *msgDBService); static void getUniqueKey(const char* prefix, nsHashtable *hashTable, diff --git a/mozilla/mailnews/base/src/nsMsgDBView.cpp b/mozilla/mailnews/base/src/nsMsgDBView.cpp index 37b29898cd9..d1e9e3d9958 100644 --- a/mozilla/mailnews/base/src/nsMsgDBView.cpp +++ b/mozilla/mailnews/base/src/nsMsgDBView.cpp @@ -4170,7 +4170,10 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsMsgKeyAr nsMsgViewIndex tryIndex = (lowIndex + highIndex - 1) / 2; EntryInfo2.id = keys->GetAt(tryIndex); nsCOMPtr tryHdr; - rv = m_db->GetMsgHdrForKey(EntryInfo2.id, getter_AddRefs(tryHdr)); + nsCOMPtr db; + GetDBForViewIndex(tryIndex, getter_AddRefs(db)); + if (db) + rv = db->GetMsgHdrForKey(EntryInfo2.id, getter_AddRefs(tryHdr)); if (!tryHdr) break; if (fieldType == kCollationKey) diff --git a/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.cpp b/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.cpp index 01373286843..ca7e16b0894 100644 --- a/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.cpp +++ b/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.cpp @@ -48,6 +48,8 @@ nsMsgQuickSearchDBView::nsMsgQuickSearchDBView() { + m_usingCachedHits = PR_FALSE; + m_cacheEmpty = PR_TRUE; } nsMsgQuickSearchDBView::~nsMsgQuickSearchDBView() @@ -219,17 +221,55 @@ nsMsgQuickSearchDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *folder) NS_ENSURE_ARG(aMsgHdr); if (!m_db) return NS_ERROR_NULL_POINTER; + // remember search hit and when search is done, reconcile cache + // with new hits; + m_hdrHits.AppendObject(aMsgHdr); + nsMsgKey key; + aMsgHdr->GetMessageKey(&key); + // is FindKey going to be expensive here? A lot of hits could make + // it a little bit slow to search through the view for every hit. + if (m_cacheEmpty || FindKey(key, PR_FALSE) == nsMsgViewIndex_None) return AddHdr(aMsgHdr); + else + return NS_OK; } NS_IMETHODIMP nsMsgQuickSearchDBView::OnSearchDone(nsresult status) { + if (m_viewFolder) + { + nsMsgKeyArray keyArray; + nsXPIDLCString searchUri; + m_viewFolder->GetURI(getter_Copies(searchUri)); + PRUint32 count = m_hdrHits.Count(); + // build up message keys. + PRUint32 i; + for (i = 0; i < count; i++) + { + nsMsgKey key; + m_hdrHits[i]->GetMessageKey(&key); + keyArray.Add(key); + } + nsMsgKey *staleHits; + PRUint32 numBadHits; + m_db->RefreshCache(searchUri, m_hdrHits.Count(), keyArray.GetArray(), &numBadHits, &staleHits); + for (i = 0; i < numBadHits; i++) + { + nsMsgViewIndex staleHitIndex = FindKey(staleHits[i], PR_TRUE); + if (staleHitIndex != nsMsgViewIndex_None) + RemoveByIndex(staleHitIndex); + } + delete [] staleHits; + // we also need to add new hits - should we have RefreshCache calculate these? + // or just look through the view for each hit to see if we're already displaying it? + } if (m_sortType != nsMsgViewSortType::byThread)//we do not find levels for the results. { m_sortValid = PR_FALSE; //sort the results Sort(m_sortType, m_sortOrder); } + m_hdrHits.Clear(); return NS_OK; } @@ -242,11 +282,41 @@ nsMsgQuickSearchDBView::OnNewSearch() m_keys.RemoveAll(); m_levels.RemoveAll(); m_flags.RemoveAll(); - + m_hdrHits.Clear(); // this needs to happen after we remove all the keys, since RowCountChanged() will call our GetRowCount() if (mTree) mTree->RowCountChanged(0, -oldSize); + PRUint32 folderFlags = 0; + if (m_viewFolder) + m_viewFolder->GetFlags(&folderFlags); + // check if it's a virtual folder - if so, we should get the cached hits + // from the db, and set a flag saying that we're using cached values. + if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL) + { + nsCOMPtr cachedHits; + nsXPIDLCString searchUri; + m_viewFolder->GetURI(getter_Copies(searchUri)); + m_db->GetCachedHits(searchUri, getter_AddRefs(cachedHits)); + if (cachedHits) + { + PRBool hasMore; + m_usingCachedHits = PR_TRUE; + cachedHits->HasMoreElements(&hasMore); + m_cacheEmpty = !hasMore; + while (hasMore) + { + nsCOMPtr pHeader; + nsresult rv = cachedHits->GetNext(getter_AddRefs(pHeader)); + NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken"); + if (pHeader && NS_SUCCEEDED(rv)) + AddHdr(pHeader); + else + break; + cachedHits->HasMoreElements(&hasMore); + } + } + } return NS_OK; } diff --git a/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.h b/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.h index 6331d3f584f..2808d725ce0 100644 --- a/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.h +++ b/mozilla/mailnews/base/src/nsMsgQuickSearchDBView.h @@ -42,6 +42,9 @@ #include "nsMsgThreadedDBView.h" #include "nsIMsgSearchNotify.h" #include "nsIMsgSearchSession.h" +#include "nsCOMArray.h" +#include "nsIMsgHdr.h" + class nsMsgQuickSearchDBView : public nsMsgThreadedDBView, public nsIMsgSearchNotify { @@ -66,6 +69,9 @@ public: protected: nsWeakPtr m_searchSession; nsMsgKeyArray m_origKeys; + PRBool m_usingCachedHits; + PRBool m_cacheEmpty; + nsCOMArray m_hdrHits; virtual nsresult OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentKey, PRBool ensureListed); virtual nsresult SortThreads(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder); virtual nsresult GetFirstMessageHdrToDisplayInThread(nsIMsgThread *threadHdr, nsIMsgDBHdr **result); diff --git a/mozilla/mailnews/base/src/nsMsgSearchDBView.cpp b/mozilla/mailnews/base/src/nsMsgSearchDBView.cpp index 9d3e7ce27ba..58696be1895 100644 --- a/mozilla/mailnews/base/src/nsMsgSearchDBView.cpp +++ b/mozilla/mailnews/base/src/nsMsgSearchDBView.cpp @@ -190,6 +190,27 @@ nsresult nsMsgSearchDBView::GetDBForViewIndex(nsMsgViewIndex index, nsIMsgDataba return NS_MSG_INVALID_DBVIEW_INDEX; } +nsresult nsMsgSearchDBView::AddHdrFromFolder(nsIMsgDBHdr *msgHdr, nsISupports *folder) +{ + m_folders->AppendElement(folder); + nsMsgKey msgKey; + PRUint32 msgFlags; + msgHdr->GetMessageKey(&msgKey); + // nsMsgKey_None means it's not a valid hdr. + if (msgKey != nsMsgKey_None) + { + msgHdr->GetFlags(&msgFlags); + m_keys.Add(msgKey); + m_levels.Add(0); + m_flags.Add(msgFlags); + + // this needs to be called after we add the key, since RowCountChanged() will call our GetRowCount() + if (mTree) + mTree->RowCountChanged(GetSize() - 1, 1); + } + return NS_OK; + } + NS_IMETHODIMP nsMsgSearchDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *folder) { @@ -209,20 +230,7 @@ nsMsgSearchDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *folder) } } - m_folders->AppendElement(supports); - nsMsgKey msgKey; - PRUint32 msgFlags; - aMsgHdr->GetMessageKey(&msgKey); - aMsgHdr->GetFlags(&msgFlags); - m_keys.Add(msgKey); - m_levels.Add(0); - m_flags.Add(msgFlags); - - // this needs to be called after we add the key, since RowCountChanged() will call our GetRowCount() - if (mTree) - mTree->RowCountChanged(GetSize() - 1, 1); - - return NS_OK; + return AddHdrFromFolder(aMsgHdr, supports); } NS_IMETHODIMP @@ -508,7 +516,7 @@ nsresult nsMsgSearchDBView::ProcessRequestsInOneFolder(nsIMsgWindow *window) // called for delete with trash, copy and move if (mCommand == nsMsgViewCommandType::deleteMsg) - curFolder->DeleteMessages(messageArray, window, PR_FALSE /* delete storage */, PR_FALSE /* is move*/, this, PR_FALSE /*allowUndo*/); + curFolder->DeleteMessages(messageArray, window, PR_FALSE /* delete storage */, PR_FALSE /* is move*/, this, PR_TRUE /*allowUndo*/); else { NS_ASSERTION(!(curFolder == mDestFolder), "The source folder and the destination folder are the same"); @@ -518,9 +526,9 @@ nsresult nsMsgSearchDBView::ProcessRequestsInOneFolder(nsIMsgWindow *window) if (NS_SUCCEEDED(rv)) { if (mCommand == nsMsgViewCommandType::moveMessages) - copyService->CopyMessages(curFolder, messageArray, mDestFolder, PR_TRUE /* isMove */, this, window, PR_FALSE /*allowUndo*/); + copyService->CopyMessages(curFolder, messageArray, mDestFolder, PR_TRUE /* isMove */, this, window, PR_TRUE /*allowUndo*/); else if (mCommand == nsMsgViewCommandType::copyMessages) - copyService->CopyMessages(curFolder, messageArray, mDestFolder, PR_FALSE /* isMove */, this, window, PR_FALSE /*allowUndo*/); + copyService->CopyMessages(curFolder, messageArray, mDestFolder, PR_FALSE /* isMove */, this, window, PR_TRUE /*allowUndo*/); } } } diff --git a/mozilla/mailnews/base/src/nsMsgSearchDBView.h b/mozilla/mailnews/base/src/nsMsgSearchDBView.h index 4f0691ea720..00ed52a9df3 100644 --- a/mozilla/mailnews/base/src/nsMsgSearchDBView.h +++ b/mozilla/mailnews/base/src/nsMsgSearchDBView.h @@ -76,6 +76,7 @@ public: protected: nsresult FetchLocation(PRInt32 aRow, PRUnichar ** aLocationString); + virtual nsresult AddHdrFromFolder(nsIMsgDBHdr *msgHdr, nsISupports *folder); virtual nsresult GetDBForViewIndex(nsMsgViewIndex index, nsIMsgDatabase **db); virtual nsresult RemoveByIndex(nsMsgViewIndex index); virtual nsresult CopyMessages(nsIMsgWindow *window, nsMsgViewIndex *indices, PRInt32 numIndices, PRBool isMove, nsIMsgFolder *destFolder); diff --git a/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.cpp b/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.cpp index d3aa7184405..e48e587a573 100755 --- a/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.cpp +++ b/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.cpp @@ -142,6 +142,86 @@ nsresult nsMsgXFVirtualFolderDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey a return NS_OK; } +nsresult nsMsgXFVirtualFolderDBView::InsertHdrFromFolder(nsIMsgDBHdr *msgHdr, nsISupports *folder) +{ + nsMsgViewIndex insertIndex = GetInsertIndex(msgHdr); + if (insertIndex == nsMsgViewIndex_None) + return AddHdrFromFolder(msgHdr, folder); + + nsMsgKey msgKey; + PRUint32 msgFlags; + msgHdr->GetMessageKey(&msgKey); + msgHdr->GetFlags(&msgFlags); + m_keys.InsertAt(insertIndex, msgKey); + m_flags.InsertAt(insertIndex, msgFlags); + m_folders->InsertElementAt(folder, insertIndex); + m_levels.InsertAt((PRInt32) insertIndex, (PRUint8) 0); + + // the call to NoteChange() has to happen after we add the key + // as NoteChange() will call RowCountChanged() which will call our GetRowCount() + NoteChange(insertIndex, 1, nsMsgViewNotificationCode::insertOrDelete); + return NS_OK; +} + +void nsMsgXFVirtualFolderDBView::UpdateCacheAndViewForFolder(nsIMsgFolder *folder, nsMsgKey *newHits, PRUint32 numNewHits) +{ + nsCOMPtr db; + nsresult rv = folder->GetMsgDatabase(nsnull, getter_AddRefs(db)); + if (NS_SUCCEEDED(rv) && db) + { + nsXPIDLCString searchUri; + m_viewFolder->GetURI(getter_Copies(searchUri)); + PRUint32 numBadHits; + nsMsgKey *badHits; + rv = db->RefreshCache(searchUri, numNewHits, newHits, + &numBadHits, &badHits); + if (NS_SUCCEEDED(rv)) + { + for (PRUint32 badHitIndex = 0; badHitIndex < numBadHits; badHitIndex++) + { + // of course, this isn't quite right + nsMsgViewIndex staleHitIndex = FindKey(badHits[badHitIndex], PR_TRUE); + if (staleHitIndex != nsMsgViewIndex_None) + RemoveByIndex(staleHitIndex); + } + delete [] badHits; + } + } +} + +void nsMsgXFVirtualFolderDBView::UpdateCacheAndViewForPrevSearchedFolders(nsIMsgFolder *curSearchFolder) +{ + // Handle the most recent folder with hits, if any. + if (m_curFolderGettingHits) + { + PRUint32 count = m_hdrHits.Count(); + nsMsgKeyArray newHits; + for (PRUint32 i = 0; i < count; i++) + { + nsMsgKey key; + m_hdrHits[i]->GetMessageKey(&key); + newHits.Add(key); + } + UpdateCacheAndViewForFolder(m_curFolderGettingHits, newHits.GetArray(), newHits.GetSize()); + } + + while (m_foldersWithNonVerifiedCachedHits.Count() > 0) + { + // this new folder has cached hits. + if (m_foldersWithNonVerifiedCachedHits[0] == curSearchFolder) + { + m_curFolderHasCachedHits = PR_TRUE; + break; + } + else if (m_foldersWithNonVerifiedCachedHits[0] != m_curFolderGettingHits) + { + // this must be a folder that had cached hits but no hits with + // the current search. So all cached hits need to be removed. + UpdateCacheAndViewForFolder(m_foldersWithNonVerifiedCachedHits[0], 0, nsnull); + } + m_foldersWithNonVerifiedCachedHits.RemoveObjectAt(0); + } +} NS_IMETHODIMP nsMsgXFVirtualFolderDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *folder) { @@ -149,32 +229,54 @@ nsMsgXFVirtualFolderDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *fold NS_ENSURE_ARG(folder); nsCOMPtr supports = do_QueryInterface(folder); + nsCOMPtr dbToUse; + nsCOMPtr folderInfo; + folder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(dbToUse)); + if (m_folders->IndexOf(supports) < 0 ) //do this just for new folder { - nsCOMPtr dbToUse; - nsCOMPtr folderInfo; - folder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(dbToUse)); if (dbToUse) { dbToUse->AddListener(this); m_dbToUseList.AppendObject(dbToUse); } } + if (m_curFolderGettingHits != folder) + { + m_curFolderHasCachedHits = PR_FALSE; + // since we've gotten a hit for a new folder, the searches for + // any previous folders are done, so deal with stale cached hits + // for those folders now. - m_folders->AppendElement(supports); - nsMsgKey msgKey; - PRUint32 msgFlags; - aMsgHdr->GetMessageKey(&msgKey); - aMsgHdr->GetFlags(&msgFlags); - m_keys.Add(msgKey); - m_levels.Add(0); - m_flags.Add(msgFlags); - - // this needs to be called after we add the key, since RowCountChanged() will call our GetRowCount() - if (mTree) - mTree->RowCountChanged(GetSize() - 1, 1); + UpdateCacheAndViewForPrevSearchedFolders(folder); + m_curFolderGettingHits = folder; + m_hdrHits.Clear(); + m_curFolderStartKeyIndex = m_keys.GetSize(); + } + // calling FindHdr is sub-optimal here since for virtual folders with + // a large number of hits, we'll have an 0(n2) algorithm + // - we could add a way to check if a hdr is in the cached hits for + // this folder, or we could make FindHdr do a binary search when + // the view is sorted. Checking the cached hits is probably best + // since they're in id order, so we can do a binary search. + // Actually, I think Mork has a hash table for uid lookups in a + // table, so we should definitely use the db. + PRBool hdrInCache = PR_FALSE; + nsXPIDLCString searchUri; + m_viewFolder->GetURI(getter_Copies(searchUri)); + dbToUse->HdrIsInCache(searchUri, aMsgHdr, &hdrInCache); + if (!m_curFolderHasCachedHits || !hdrInCache) + { + if (m_sortValid) + InsertHdrFromFolder(aMsgHdr, supports); + else + AddHdrFromFolder(aMsgHdr, supports); + } + m_hdrHits.AppendObject(aMsgHdr); m_numTotal++; + PRUint32 msgFlags; + aMsgHdr->GetFlags(&msgFlags); if (!(msgFlags & MSG_FLAG_READ)) m_numUnread++; @@ -184,6 +286,9 @@ nsMsgXFVirtualFolderDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *fold NS_IMETHODIMP nsMsgXFVirtualFolderDBView::OnSearchDone(nsresult status) { + // handle any non verified hits we haven't handled yet. + UpdateCacheAndViewForPrevSearchedFolders(nsnull); + //we want to set imap delete model once the search is over because setting next //message after deletion will happen before deleting the message and search scope //can change with every search. @@ -200,11 +305,13 @@ nsMsgXFVirtualFolderDBView::OnSearchDone(nsresult status) dbFolderInfo->SetNumMessages(m_numTotal); m_viewFolder->UpdateSummaryTotals(true); // force update from db. virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit); - if (m_sortType != nsMsgViewSortType::byThread) + if (!m_sortValid && m_sortType != nsMsgViewSortType::byThread) { m_sortValid = PR_FALSE; //sort the results Sort(m_sortType, m_sortOrder); } + m_foldersWithNonVerifiedCachedHits.Clear(); + m_curFolderGettingHits = nsnull; return rv; } @@ -229,6 +336,73 @@ nsMsgXFVirtualFolderDBView::OnNewSearch() if (mTree) mTree->RowCountChanged(0, -oldSize); + // to use the search results cache, we'll need to iterate over the scopes in the + // search session, calling getNthSearchScope for i = 0; i < searchSession.countSearchScopes; i++ + // and for each folder, then open the db and pull out the cached hits, add them to the view. + // For each hit in a new folder, we'll then clean up the stale hits from the previous folder(s). + + PRInt32 scopeCount; + nsCOMPtr searchSession = do_QueryReferent(m_searchSession); + searchSession->CountSearchScopes(&scopeCount); + for (PRInt32 i = 0; i < scopeCount; i++) + { + nsMsgSearchScopeValue scopeId; + nsCOMPtr searchFolder; + searchSession->GetNthSearchScope(i, &scopeId, getter_AddRefs(searchFolder)); + if (searchFolder) + { + nsCOMPtr cachedHits; + nsCOMPtr searchDB; + nsXPIDLCString searchUri; + m_viewFolder->GetURI(getter_Copies(searchUri)); + nsresult rv = searchFolder->GetMsgDatabase(nsnull, getter_AddRefs(searchDB)); + if (NS_SUCCEEDED(rv) && searchDB) + { + searchDB->GetCachedHits(searchUri, getter_AddRefs(cachedHits)); + PRBool hasMore; + if (cachedHits) + { + cachedHits->HasMoreElements(&hasMore); + if (hasMore) + { + nsMsgKey prevKey = nsMsgKey_None; + m_foldersWithNonVerifiedCachedHits.AppendObject(searchFolder); + while (hasMore) + { + nsCOMPtr pHeader; + nsresult rv = cachedHits->GetNext(getter_AddRefs(pHeader)); + NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken"); + if (pHeader && NS_SUCCEEDED(rv)) + { + nsMsgKey msgKey; + pHeader->GetMessageKey(&msgKey); + NS_ASSERTION(prevKey == nsMsgKey_None || msgKey > prevKey, "cached Hits not sorted"); + prevKey = msgKey; + AddHdrFromFolder(pHeader, searchFolder); // need to QI to nsISupports? + } + else + break; + cachedHits->HasMoreElements(&hasMore); + } + } + } + } + } + } + + m_curFolderStartKeyIndex = 0; + m_curFolderGettingHits = nsnull; + m_curFolderHasCachedHits = PR_FALSE; + + // if we have cached hits, sort them. + if (GetSize() > 0) + { + if (m_sortType != nsMsgViewSortType::byThread) + { + m_sortValid = PR_FALSE; //sort the results + Sort(m_sortType, m_sortOrder); + } + } // mSearchResults->Clear(); return NS_OK; } diff --git a/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.h b/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.h index 720d0553baa..89e85cd9985 100755 --- a/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.h +++ b/mozilla/mailnews/base/src/nsMsgXFVirtualFolderDBView.h @@ -65,11 +65,20 @@ public: NS_IMETHOD GetViewType(nsMsgViewTypeValue *aViewType); NS_IMETHOD DoCommand(nsMsgViewCommandTypeValue command); virtual nsresult OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey parentKey, PRBool ensureListed); + virtual nsresult InsertHdrFromFolder(nsIMsgDBHdr *msgHdr, nsISupports *folder); NS_IMETHOD GetMsgFolder(nsIMsgFolder **aMsgFolder); + void UpdateCacheAndViewForPrevSearchedFolders(nsIMsgFolder *curSearchFolder); + void UpdateCacheAndViewForFolder(nsIMsgFolder *folder, nsMsgKey *newHits, PRUint32 numNewHits); protected: nsCOMPtr m_viewFolder; + PRUint32 m_cachedFolderArrayIndex; // array index of next folder with cached hits to deal with. + nsCOMArray m_foldersWithNonVerifiedCachedHits; + nsCOMArray m_hdrHits; + nsCOMPtr m_curFolderGettingHits; + PRUint32 m_curFolderStartKeyIndex; // keeps track of the index of the first hit from the cur folder + PRBool m_curFolderHasCachedHits; nsWeakPtr m_searchSession; PRInt32 m_numUnread; PRInt32 m_numTotal; diff --git a/mozilla/mailnews/db/msgdb/public/nsIMsgDatabase.idl b/mozilla/mailnews/db/msgdb/public/nsIMsgDatabase.idl index e520089edb2..8bc563334fc 100644 --- a/mozilla/mailnews/db/msgdb/public/nsIMsgDatabase.idl +++ b/mozilla/mailnews/db/msgdb/public/nsIMsgDatabase.idl @@ -54,6 +54,7 @@ interface nsIMsgOfflineImapOperation; interface nsIMsgFolder; interface nsIOFileStream; interface nsIUrlListener; +interface nsISupportsArray; [ptr] native octetPtr(PRUint8); @@ -117,7 +118,7 @@ interface nsIMsgDBService : nsISupports void unregisterPendingListener(in nsIDBChangeListener aListener); }; -[scriptable, uuid(7b510984-bbac-4e8f-b6cd-134509000776)] +[scriptable, uuid(6393FEF1-D6C6-444A-90B8-BEC84E8BD8D7)] interface nsIMsgDatabase : nsIDBChangeAnnouncer { void Open(in nsIFileSpec aFolderName, in boolean aCreate, in boolean aLeaveInvalidDB); @@ -301,5 +302,13 @@ interface nsIMsgDatabase : nsIDBChangeAnnouncer { * the caller should free when done using nsMemory::Free. */ void getNewList(out unsigned long count, [array, size_is(count)] out nsMsgKey newKeys); + + // These are used for caching search hits in a db, to speed up saved search folders. + nsISimpleEnumerator getCachedHits(in string aSearchFolderUri); + void refreshCache(in string aSearchFolderUri, in unsigned long aNumKeys, [array, size_is (aNumKeys)] in nsMsgKey aNewHits, + out unsigned long aNumBadHits, [array, size_is(aNumBadHits)] out nsMsgKey aStaleHits); + void updateHdrInCache(in string aSearchFolderUri, in nsIMsgDBHdr aHdr, in boolean aAdd); + boolean hdrIsInCache(in string aSearchFolderUri, in nsIMsgDBHdr aHdr); + }; diff --git a/mozilla/mailnews/db/msgdb/public/nsMsgDatabase.h b/mozilla/mailnews/db/msgdb/public/nsMsgDatabase.h index 9e304bec709..19774fd2f19 100644 --- a/mozilla/mailnews/db/msgdb/public/nsMsgDatabase.h +++ b/mozilla/mailnews/db/msgdb/public/nsMsgDatabase.h @@ -95,6 +95,7 @@ public: virtual nsresult CreateMsgHdr(nsIMdbRow* hdrRow, nsMsgKey key, nsIMsgDBHdr **result); virtual nsresult GetThreadForMsgKey(nsMsgKey msgKey, nsIMsgThread **result); virtual nsresult EnumerateMessagesWithFlag(nsISimpleEnumerator* *result, PRUint32 *pFlag); + nsresult GetSearchResultsTable(const char *searchFolderUri, PRBool createIfMissing, nsIMdbTable **table); // this might just be for debugging - we'll see. nsresult ListAllThreads(nsMsgKeyArray *threadIds); @@ -285,6 +286,8 @@ protected: nsresult ClearUseHdrCache(); nsresult RemoveHdrFromUseCache(nsIMsgDBHdr *hdr, nsMsgKey key); + mdb_pos FindInsertIndexInSortedTable(nsIMdbTable *table, mdb_id idToInsert); + void ClearCachedObjects(PRBool dbGoingAway); // all instantiated headers, but doesn't hold refs. PLDHashTable *m_headersInUse; diff --git a/mozilla/mailnews/db/msgdb/src/nsMsgDatabase.cpp b/mozilla/mailnews/db/msgdb/src/nsMsgDatabase.cpp index 8127a877738..400b974243d 100644 --- a/mozilla/mailnews/db/msgdb/src/nsMsgDatabase.cpp +++ b/mozilla/mailnews/db/msgdb/src/nsMsgDatabase.cpp @@ -160,7 +160,7 @@ NS_IMETHODIMP nsMsgDBService::OpenFolderDB(nsIMsgFolder *aFolder, PRBool aCreate PRInt32 numMessages; msgDatabase->m_mdbAllMsgHeadersTable->GetCount(msgDatabase->GetEnv(), &numHdrsInTable); msgDatabase->m_dbFolderInfo->GetNumMessages(&numMessages); - if (numMessages != numHdrsInTable) + if (numMessages != (PRInt32) numHdrsInTable) msgDatabase->SyncCounts(); } } @@ -2134,7 +2134,7 @@ nsMsgDatabase::MarkThreadIgnored(nsIMsgThread *thread, nsMsgKey threadKey, PRBoo nsCOMPtr msg; nsresult rv = GetMsgHdrForKey(threadKey, getter_AddRefs(msg)); - + NS_ENSURE_SUCCESS(rv, rv); return NotifyHdrChangeAll(msg, oldThreadFlags, threadFlags, instigator); } @@ -2549,7 +2549,7 @@ public: // nsMsgDBEnumerator methods: typedef nsresult (*nsMsgDBEnumeratorFilter)(nsIMsgDBHdr* hdr, void* closure); - nsMsgDBEnumerator(nsMsgDatabase* db, + nsMsgDBEnumerator(nsMsgDatabase* db, nsIMdbTable *table, nsMsgDBEnumeratorFilter filter, void* closure); virtual ~nsMsgDBEnumerator(); @@ -2562,16 +2562,20 @@ protected: PRBool mDone; PRBool mNextPrefetched; nsMsgDBEnumeratorFilter mFilter; + nsCOMPtr mTable; void* mClosure; }; nsMsgDBEnumerator::nsMsgDBEnumerator(nsMsgDatabase* db, - nsMsgDBEnumeratorFilter filter, void* closure) + nsIMdbTable *table, + nsMsgDBEnumeratorFilter filter, + void* closure) : mDB(db), mRowCursor(nsnull), mResultHdr(nsnull), mDone(PR_FALSE), mFilter(filter), mClosure(closure) { NS_ADDREF(mDB); mNextPrefetched = PR_FALSE; + mTable = table; } nsMsgDBEnumerator::~nsMsgDBEnumerator() @@ -2588,10 +2592,10 @@ nsresult nsMsgDBEnumerator::GetRowCursor() { mDone = PR_FALSE; - if (!mDB || !mDB->m_mdbAllMsgHeadersTable) + if (!mDB || !mTable) return NS_ERROR_NULL_POINTER; - return mDB->m_mdbAllMsgHeadersTable->GetTableRowCursor(mDB->GetEnv(), -1, &mRowCursor); + return mTable->GetTableRowCursor(mDB->GetEnv(), -1, &mRowCursor); } NS_IMETHODIMP nsMsgDBEnumerator::GetNext(nsISupports **aItem) @@ -2687,7 +2691,7 @@ NS_IMETHODIMP nsMsgDBEnumerator::HasMoreElements(PRBool *aResult) NS_IMETHODIMP nsMsgDatabase::EnumerateMessages(nsISimpleEnumerator* *result) { - nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, nsnull, nsnull); + nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable, nsnull, nsnull); if (e == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(e); @@ -2982,7 +2986,7 @@ nsMsgFlagSetFilter(nsIMsgDBHdr *msg, void *closure) nsresult nsMsgDatabase::EnumerateMessagesWithFlag(nsISimpleEnumerator* *result, PRUint32 *pFlag) { - nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, nsMsgFlagSetFilter, pFlag); + nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable, nsMsgFlagSetFilter, pFlag); if (e == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(e); @@ -4794,3 +4798,233 @@ nsMsgDatabase::GetNewList(PRUint32 *aCount, PRUint32 **aNewKeys) *aNewKeys = nsnull; return NS_OK; } + +nsresult nsMsgDatabase::GetSearchResultsTable(const char *searchFolderUri, PRBool createIfMissing, nsIMdbTable **table) +{ + mdb_kind kindToken; + mdb_count numTables; + mdb_bool mustBeUnique; + mdb_err err = m_mdbStore->StringToToken(GetEnv(), searchFolderUri, &kindToken); + err = m_mdbStore->GetTableKind(GetEnv(), m_hdrRowScopeToken, kindToken, + &numTables, &mustBeUnique, table); + if ((!*table || NS_FAILED(err)) && createIfMissing) + err = m_mdbStore->NewTable(GetEnv(), m_hdrRowScopeToken, kindToken, PR_TRUE, nsnull, table); + + return *table ? err : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMsgDatabase::GetCachedHits(const char *aSearchFolderUri, nsISimpleEnumerator **aEnumerator) +{ + nsCOMPtr table; + nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_FALSE, getter_AddRefs(table)); + NS_ENSURE_SUCCESS(err, err); + if (!table) + return NS_ERROR_FAILURE; + nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, table, nsnull, nsnull); + if (e == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(*aEnumerator = e); + return NS_OK; +} + +NS_IMETHODIMP nsMsgDatabase::RefreshCache(const char *aSearchFolderUri, PRUint32 aNumKeys, nsMsgKey *aNewHits, PRUint32 *aNumBadHits, nsMsgKey **aStaleHits) +{ + nsCOMPtr table; + nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_TRUE, getter_AddRefs(table)); + NS_ENSURE_SUCCESS(err, err); + // update the table so that it just contains aNewHits. + // And, keep track of the headers in the original table but not in aNewHits, so we + // can put those in aStaleHits. + // both aNewHits and the db table are sorted by uid/key. + // So, start at the beginning of the table and the aNewHits array. + PRUint32 newHitIndex = 0; + PRUint32 tableRowIndex = 0; + + PRUint32 rowCount; + table->GetCount(GetEnv(), &rowCount); + nsMsgKeyArray staleHits; + // should assert that each array is sorted + while (newHitIndex < aNumKeys || tableRowIndex < rowCount) + { + mdbOid oid; + nsMsgKey tableRowKey = nsMsgKey_None; + if (tableRowIndex < rowCount) + { + nsresult ret = table->PosToOid (GetEnv(), tableRowIndex, &oid); + if (NS_FAILED(ret)) + { + tableRowIndex++; + continue; + } + tableRowKey = oid.mOid_Id; // ### TODO need the real key for the 0th key problem. + } + + if (newHitIndex < aNumKeys && aNewHits[newHitIndex] == tableRowKey) + { + newHitIndex++; + tableRowIndex++; + continue; + } + else if (tableRowIndex >= rowCount || (newHitIndex < aNumKeys && aNewHits[newHitIndex] < tableRowKey)) + { + nsCOMPtr hdrRow; + mdbOid rowObjectId; + + rowObjectId.mOid_Id = aNewHits[newHitIndex]; + rowObjectId.mOid_Scope = m_hdrRowScopeToken; + err = m_mdbStore->GetRow(GetEnv(), &rowObjectId, getter_AddRefs(hdrRow)); + if (hdrRow) + { + table->AddRow(GetEnv(), hdrRow); + mdb_pos newPos; +#ifdef DEBUG_David_Bienvenu + printf("adding row %lx at pos %lx \n", rowObjectId.mOid_Id, tableRowIndex); +#endif + table->MoveRow(GetEnv(), hdrRow, rowCount, tableRowIndex, &newPos); + rowCount++; + tableRowIndex++; + } + newHitIndex++; + continue; + } + else if (newHitIndex >= aNumKeys || aNewHits[newHitIndex] > tableRowKey) + { + staleHits.Add(tableRowKey); + table->CutOid(GetEnv(), &oid); + rowCount--; + continue; // don't increment tableRowIndex since we removed that row. + } + } + *aNumBadHits = staleHits.GetSize(); + if (*aNumBadHits) + { + *aStaleHits = NS_STATIC_CAST(PRUint32 *, nsMemory::Alloc(*aNumBadHits * sizeof(PRUint32))); + if (!*aStaleHits) + return NS_ERROR_OUT_OF_MEMORY; + memcpy(*aStaleHits, staleHits.GetArray(), *aNumBadHits * sizeof(PRUint32)); + } + else + *aStaleHits = nsnull; + +#ifdef DEBUG_David_Bienvenu + printf("after refreshing cache\n"); + // iterate over table and assert that it's in id order + table->GetCount(GetEnv(), &rowCount); + mdbOid oid; + tableRowIndex = 0; + mdb_id prevId = 0; + while (tableRowIndex < rowCount) + { + nsresult ret = table->PosToOid (m_mdbEnv, tableRowIndex++, &oid); + if (tableRowIndex > 1 && oid.mOid_Id <= prevId) + { + NS_ASSERTION(PR_FALSE, "inserting row into cached hits table, not sorted correctly"); + printf("key %lx is before or equal %lx \n", prevId, oid.mOid_Id); + } + prevId = oid.mOid_Id; + } + +#endif + Commit(nsMsgDBCommitType::kLargeCommit); + return NS_OK; +} + +// search sorted table +mdb_pos nsMsgDatabase::FindInsertIndexInSortedTable(nsIMdbTable *table, mdb_id idToInsert) +{ + mdb_pos searchPos = 0; + PRUint32 rowCount; + table->GetCount(GetEnv(), &rowCount); + mdb_pos hi = rowCount; + mdb_pos lo = 0; + + while (hi > lo) + { + mdbOid outOid; + searchPos = (lo + hi - 1) / 2; + table->PosToOid(GetEnv(), searchPos, &outOid); + if (outOid.mOid_Id == idToInsert) + { + NS_ASSERTION(PR_FALSE, "id shouldn't be in table"); + return hi; + } + if (outOid.mOid_Id > idToInsert) + hi = searchPos; + else // if (outOid.mOid_Id < idToInsert) + lo = searchPos + 1; + } + return hi; +} +NS_IMETHODIMP +nsMsgDatabase::UpdateHdrInCache(const char *aSearchFolderUri, nsIMsgDBHdr *aHdr, PRBool aAdd) +{ + nsCOMPtr table; + nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_TRUE, getter_AddRefs(table)); + NS_ENSURE_SUCCESS(err, err); + nsMsgKey key; + aHdr->GetMessageKey(&key); + nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, aHdr); // closed system, so this is ok + if (err == NS_OK && m_mdbStore && msgHdr->m_mdbRow) + { + if (!aAdd) + { + table->CutRow(m_mdbEnv, msgHdr->m_mdbRow); +#ifdef DEBUG_David_Bienvenu + printf("removing key %lx \n", key); +#endif + } + else + { + mdbOid rowId; + msgHdr->m_mdbRow->GetOid(m_mdbEnv, &rowId); + mdb_pos insertPos = FindInsertIndexInSortedTable(table, rowId.mOid_Id); + PRUint32 rowCount; + table->GetCount(m_mdbEnv, &rowCount); + table->AddRow(m_mdbEnv, msgHdr->m_mdbRow); + mdb_pos newPos; + table->MoveRow(m_mdbEnv, msgHdr->m_mdbRow, rowCount, insertPos, &newPos); +#ifdef DEBUG_David_Bienvenu + printf("moving row with key %lx to pos %lx \n", key, newPos); + // iterate over table and assert that it's in id order + table->GetCount(GetEnv(), &rowCount); + mdbOid oid; + PRUint32 tableRowIndex = 0; + mdb_id prevId = 0; + while (tableRowIndex < rowCount) + { + nsresult ret = table->PosToOid (m_mdbEnv, tableRowIndex++, &oid); + if (tableRowIndex > 1 && oid.mOid_Id <= prevId) + { + NS_ASSERTION(PR_FALSE, "inserting row into cached hits table, not sorted correctly"); + printf("key %lx is before or equal %lx \n", prevId, oid.mOid_Id); + } + prevId = oid.mOid_Id; + } + +#endif + } + } + +// if (aAdd) + // if we need to add this hdr, we need to insert it in key order. + return NS_OK; +} +NS_IMETHODIMP +nsMsgDatabase::HdrIsInCache(const char* aSearchFolderUri, nsIMsgDBHdr *aHdr, PRBool *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + nsCOMPtr table; + nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_TRUE, getter_AddRefs(table)); + NS_ENSURE_SUCCESS(err, err); + nsMsgKey key; + aHdr->GetMessageKey(&key); + mdbOid rowObjectId; + rowObjectId.mOid_Id = key; + rowObjectId.mOid_Scope = m_hdrRowScopeToken; + mdb_bool hasOid; + err = table->HasOid(GetEnv(), &rowObjectId, &hasOid); + *aResult = hasOid; + return err; +} +