From 962ea85e6df7cdeb34cd115b5fd5628da0ace339 Mon Sep 17 00:00:00 2001 From: "reed%reedloden.com" Date: Tue, 1 Apr 2008 08:39:58 +0000 Subject: [PATCH] Bug 418551 - "Convert panacea.dat from mork" [p=Pidgeot18@gmail.com (Joshua Cranmer [jcranmer]) r=bienvenu sr=Neil] git-svn-id: svn://10.0.0.236/trunk@248944 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/mailnews/base/build/Makefile.in | 1 + .../base/public/nsIMsgFolderCache.idl | 55 ++- .../base/public/nsIMsgFolderCacheElement.idl | 50 +++ mozilla/mailnews/base/src/Makefile.in | 2 +- .../mailnews/base/src/nsMailDirProvider.cpp | 2 +- .../mailnews/base/src/nsMsgFolderCache.cpp | 422 ++++++------------ mozilla/mailnews/base/src/nsMsgFolderCache.h | 24 +- .../base/src/nsMsgFolderCacheElement.cpp | 130 +++--- .../base/src/nsMsgFolderCacheElement.h | 12 +- .../base/test/unit/test_nsMailDirProvider.js | 2 +- mozilla/mailnews/build/Makefile.in | 1 + 11 files changed, 309 insertions(+), 392 deletions(-) diff --git a/mozilla/mailnews/base/build/Makefile.in b/mozilla/mailnews/base/build/Makefile.in index 4e65b4399ff..d8b07db5090 100644 --- a/mozilla/mailnews/base/build/Makefile.in +++ b/mozilla/mailnews/base/build/Makefile.in @@ -72,6 +72,7 @@ REQUIRES = xpcom \ mailnews \ addrbook \ mork \ + storage \ txmgr \ pref \ msgcompose \ diff --git a/mozilla/mailnews/base/public/nsIMsgFolderCache.idl b/mozilla/mailnews/base/public/nsIMsgFolderCache.idl index 2a6f4395693..8332c355920 100644 --- a/mozilla/mailnews/base/public/nsIMsgFolderCache.idl +++ b/mozilla/mailnews/base/public/nsIMsgFolderCache.idl @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Joshua Cranmer * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -41,13 +42,65 @@ interface nsIFile; interface nsIMsgFolderCacheElement; +/** + * An interface representing the message folder cache. + * + * The default cache is a SQLite file, msgFolderCache.sqlite, located in the + * profile root directory. This can be specified with the key + * NS_APP_MESSENGER_FOLDER_CACHE_50_FILE ("MFCaF") through a directory service + * provider. This is the default schema: + * + * CREATE TABLE entries ( + * folderKey CHAR, + * propertyName CHAR, + * value CHAR + * ); + * + * + * All methods, except (obviously) Init, will throw a NS_ERROR_NOT_INITIALIZED + * if the Init function has not been called. They may also throw underlying + * errors from the backing database should a database error occur. + */ [scriptable, uuid(78C2B6A2-E29F-44de-9543-10DBB51E245C)] interface nsIMsgFolderCache : nsISupports { + /** + * Initializes the folder cache with the specified file. + * Do not call multiple times. + */ void Init(in nsIFile aFile); - nsIMsgFolderCacheElement GetCacheElement(in ACString key, in boolean createIfMissing); + /** + * Returns an element of the folder cache. + * + * @param key The name of cache element to get. + * @param createIfMissing If true, create the cache if it does not exist. + * + * @exception NS_ERROR_FAILURE if the key does not exist and is not created. + */ + nsIMsgFolderCacheElement GetCacheElement(in ACString key, + in boolean createIfMissing); + /** + * Clears the cache. + */ void clear(); + /** + * Closes the cache. + * + * This will cause a compressed commit to occur. + */ void close(); + /** + * Commits the cache. + * + * @param compress If true, compress the changes. + */ void commit(in boolean compress); + /** + * Removes the named element. + * + * @param key The name of cache element to get. + * + * @exception NS_ERROR_FAILURE if the key does not exist. + */ void removeElement(in ACString key); }; diff --git a/mozilla/mailnews/base/public/nsIMsgFolderCacheElement.idl b/mozilla/mailnews/base/public/nsIMsgFolderCacheElement.idl index f902d2f35cb..310ada49846 100644 --- a/mozilla/mailnews/base/public/nsIMsgFolderCacheElement.idl +++ b/mozilla/mailnews/base/public/nsIMsgFolderCacheElement.idl @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Joshua Cranmer * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -38,12 +39,61 @@ #include "nsISupports.idl" +/** + * An element of the folder cache. + * + * By convention, a separate element is used for each .msf file in the account + * manager. + * + * Since the folder cache is expected to be backed by a database, each method + * of this cache element (the key excluded) may throw a database-based error. + */ [scriptable, uuid(D7ED2508-A608-46cd-AA01-FBB019B0FA44)] interface nsIMsgFolderCacheElement : nsISupports { + /** + * The key of this element. + * + * By convention, this key is the name and path of the summary file that this + * cache element represents. + */ attribute ACString key; + /** + * Gets a string for the named property. + * + * @param propertyName The name of the property to get. + * @return The property value, or "" if it does not exist. + * + * @throws NS_ERROR_NULL_POINTER if propertyName is null. + * @throws NS_ERROR_FAILURE if the property does not exist. + */ ACString getStringProperty(in string propertyName); + /** + * Gets a long for the named property. + * + * @param propertyName The name of the property to get. + * @return The property value, or 0 if it does not exist. + * + * @throws NS_ERROR_NULL_POINTER if propertyName is null. + * @throws NS_ERROR_FAILURE if the property does not exist. + */ long getInt32Property(in string propertyName); + /** + * Sets the named property. + * + * @param propertyValue The new value of the property to set. + * @param propertyName The name of the property to set. + * + * @throws NS_ERROR_NULL_POINTER if propertyName is null. + */ void setStringProperty(in string propertyName, in ACString propertyValue); + /** + * Sets the named property. + * + * @param propertyValue The new value of the property to set. + * @param propertyName The name of the property to set. + * + * @throws NS_ERROR_NULL_POINTER if propertyName is null. + */ void setInt32Property(in string propertyName, in long propertyValue); }; diff --git a/mozilla/mailnews/base/src/Makefile.in b/mozilla/mailnews/base/src/Makefile.in index ff457a7d929..eb0ca33553d 100644 --- a/mozilla/mailnews/base/src/Makefile.in +++ b/mozilla/mailnews/base/src/Makefile.in @@ -77,7 +77,7 @@ REQUIRES = xpcom \ pref \ msglocal \ msgimap \ - mork \ + storage \ msgnews \ addrbook \ mime \ diff --git a/mozilla/mailnews/base/src/nsMailDirProvider.cpp b/mozilla/mailnews/base/src/nsMailDirProvider.cpp index 11185225657..ac4526e67ba 100755 --- a/mozilla/mailnews/base/src/nsMailDirProvider.cpp +++ b/mozilla/mailnews/base/src/nsMailDirProvider.cpp @@ -50,7 +50,7 @@ #define MAIL_DIR_50_NAME "Mail" #define IMAP_MAIL_DIR_50_NAME "ImapMail" #define NEWS_DIR_50_NAME "News" -#define MSG_FOLDER_CACHE_DIR_50_NAME "panacea.dat" +#define MSG_FOLDER_CACHE_DIR_50_NAME "msgFolderCache.sqlite" nsresult nsMailDirProvider::EnsureDirectory(nsIFile *aDirectory) diff --git a/mozilla/mailnews/base/src/nsMsgFolderCache.cpp b/mozilla/mailnews/base/src/nsMsgFolderCache.cpp index 7311240f708..e91f72260f7 100644 --- a/mozilla/mailnews/base/src/nsMsgFolderCache.cpp +++ b/mozilla/mailnews/base/src/nsMsgFolderCache.cpp @@ -21,6 +21,7 @@ * * Contributor(s): * Pierre Phaneuf + * Joshua Cranmer * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -40,224 +41,96 @@ #include "nsIMsgAccountManager.h" #include "nsMsgFolderCacheElement.h" #include "nsMsgFolderCache.h" -#include "nsMorkCID.h" -#include "nsIMdbFactoryFactory.h" #include "nsMsgBaseCID.h" -const char *kFoldersScope = "ns:msg:db:row:scope:folders:all"; // scope for all folders table -const char *kFoldersTableKind = "ns:msg:db:table:kind:folders"; +#include "mozStorageCID.h" +#include "mozIStorageService.h" +#include "mozIStorageStatement.h" nsMsgFolderCache::nsMsgFolderCache() +: m_dbConnection(nsnull) { - m_mdbEnv = nsnull; - m_mdbStore = nsnull; - m_mdbAllFoldersTable = nsnull; } -// should this, could this be an nsCOMPtr ? -static nsIMdbFactory *gMDBFactory = nsnull; - nsMsgFolderCache::~nsMsgFolderCache() { - m_cacheElements.Clear(); // make sure the folder cache elements are released before we release our m_mdb objects... - if (m_mdbAllFoldersTable) - m_mdbAllFoldersTable->Release(); - if (m_mdbStore) - m_mdbStore->Release(); - NS_IF_RELEASE(gMDBFactory); - if (m_mdbEnv) - m_mdbEnv->Release(); + // Clear the cache elements first, for good measure. + m_cacheElements.Clear(); + if (m_dbConnection) + { + // If this still exists, roll it back + m_dbConnection->RollbackTransaction(); + m_dbConnection->Close(); + } } - NS_IMPL_ISUPPORTS1(nsMsgFolderCache, nsIMsgFolderCache) -void nsMsgFolderCache::GetMDBFactory(nsIMdbFactory ** aMdbFactory) +nsresult nsMsgFolderCache::OpenSQL(nsIFile * dbFile, PRBool create) { - if (!mMdbFactory) + // If we have an old connection, this is bad. We'll just clean up as neatly + // as possible and hope for the best. + NS_ASSERTION(!m_dbConnection, "Should not reuse for multiple SQL files!"); + if (m_dbConnection) { - nsresult rv; - nsCOMPtr mdbFactoryService = do_GetService(NS_MORK_CONTRACTID, &rv); - if (NS_SUCCEEDED(rv) && mdbFactoryService) - rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory)); + m_dbConnection->RollbackTransaction(); + m_dbConnection->Close(); } - NS_IF_ADDREF(*aMdbFactory = mMdbFactory); -} -// initialize the various tokens and tables in our db's env -nsresult nsMsgFolderCache::InitMDBInfo() -{ - nsresult err = NS_OK; - if (GetStore()) + nsresult rv; + nsCOMPtr storageService = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storageService->OpenDatabase(dbFile, getter_AddRefs(m_dbConnection)); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool connectionReady; + m_dbConnection->GetConnectionReady(&connectionReady); + if (!connectionReady) + return NS_ERROR_FAILURE; + + /*************************************************************************** + * BIG BOLD NOTE TO PAY ATTENTION TO * + *************************************************************************** + * If changing the schema, change the schema version and add handling to * + * the bottom part of the if statement. * + ***************************************************************************/ + if (create) { - err = GetStore()->StringToToken(GetEnv(), kFoldersScope, &m_folderRowScopeToken); - if (NS_SUCCEEDED(err)) - { - err = GetStore()->StringToToken(GetEnv(), kFoldersTableKind, &m_folderTableKindToken); - if (NS_SUCCEEDED(err)) - { - // The table of all message hdrs will have table id 1. - m_allFoldersTableOID.mOid_Scope = m_folderRowScopeToken; - m_allFoldersTableOID.mOid_Id = 1; - } - } - } - return err; -} - -// set up empty tables, dbFolderInfo, etc. -nsresult nsMsgFolderCache::InitNewDB() -{ - nsresult err = InitMDBInfo(); - if (NS_SUCCEEDED(err)) - { - nsIMdbStore *store = GetStore(); - // create the unique table for the dbFolderInfo. - mdb_err mdberr; - mdberr = (nsresult) store->NewTable(GetEnv(), m_folderRowScopeToken, - m_folderTableKindToken, PR_FALSE, nsnull, &m_mdbAllFoldersTable); - } - return err; -} - -nsresult nsMsgFolderCache::InitExistingDB() -{ - nsresult err = InitMDBInfo(); - if (NS_FAILED(err)) - return err; - - err = GetStore()->GetTable(GetEnv(), &m_allFoldersTableOID, &m_mdbAllFoldersTable); - if (NS_SUCCEEDED(err) && m_mdbAllFoldersTable) - { - nsIMdbTableRowCursor* rowCursor = nsnull; - err = m_mdbAllFoldersTable->GetTableRowCursor(GetEnv(), -1, &rowCursor); - if (NS_SUCCEEDED(err) && rowCursor) - { - // iterate over the table rows and create nsMsgFolderCacheElements for each. - while (PR_TRUE) - { - nsresult rv; - nsIMdbRow* hdrRow; - mdb_pos rowPos; - - rv = rowCursor->NextRow(GetEnv(), &hdrRow, &rowPos); - if (NS_FAILED(rv) || !hdrRow) - break; - - rv = AddCacheElement(EmptyCString(), hdrRow, nsnull); - hdrRow->Release(); - if (NS_FAILED(rv)) - return rv; - } - rowCursor->Release(); - } + // XXX: drop table if exists? + rv = m_dbConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE entries (" + "folderKey CHAR," + "propertyName CHAR," + "value CHAR)")); + NS_ENSURE_SUCCESS(rv, rv); } else - err = NS_ERROR_FAILURE; - - return err; -} - -nsresult nsMsgFolderCache::OpenMDB(const nsACString& dbName, PRBool exists) -{ - nsresult ret=NS_OK; - nsCOMPtr mdbFactory; - GetMDBFactory(getter_AddRefs(mdbFactory)); - if (mdbFactory) { - ret = mdbFactory->MakeEnv(nsnull, &m_mdbEnv); - if (NS_SUCCEEDED(ret)) - { - nsIMdbThumb *thumb = nsnull; - nsIMdbHeap* dbHeap = 0; - mdb_bool dbFrozen = mdbBool_kFalse; // not readonly, we want modifiable - - if (m_mdbEnv) - m_mdbEnv->SetAutoClear(PR_TRUE); - if (exists) - { - mdbOpenPolicy inOpenPolicy; - mdb_bool canOpen; - mdbYarn outFormatVersion; - - nsIMdbFile* oldFile = 0; - ret = mdbFactory->OpenOldFile(m_mdbEnv, dbHeap, nsCString(dbName).get(), - dbFrozen, &oldFile); - if ( oldFile ) - { - if (NS_SUCCEEDED(ret)) - { - ret = mdbFactory->CanOpenFilePort(m_mdbEnv, oldFile, // file to investigate - &canOpen, &outFormatVersion); - if (NS_SUCCEEDED(ret) && canOpen) - { - inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0; - inOpenPolicy.mOpenPolicy_MinMemory = 0; - inOpenPolicy.mOpenPolicy_MaxLazy = 0; - - ret = mdbFactory->OpenFileStore(m_mdbEnv, NULL, oldFile, &inOpenPolicy, - &thumb); - } - else - ret = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE; - } - NS_RELEASE(oldFile); // always release our file ref, store has own - } - } - if (NS_SUCCEEDED(ret) && thumb) - { - mdb_count outTotal; // total somethings to do in operation - mdb_count outCurrent; // subportion of total completed so far - mdb_bool outDone = PR_FALSE; // is operation finished? - mdb_bool outBroken; // is operation irreparably dead and broken? - do - { - ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken); - if (ret != 0) - {// mork isn't really doing NS errors yet. - outDone = PR_TRUE; - break; - } - } - while (NS_SUCCEEDED(ret) && !outBroken && !outDone); - // m_mdbEnv->ClearErrors(); // ### temporary... - if (NS_SUCCEEDED(ret) && outDone) - { - ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore); - if (NS_SUCCEEDED(ret) && m_mdbStore) - ret = InitExistingDB(); - } -#ifdef DEBUG_bienvenu1 - DumpContents(); -#endif - } - else // ### need error code saying why open file store failed - { - nsIMdbFile* newFile = 0; - ret = mdbFactory->CreateNewFile(m_mdbEnv, dbHeap, nsCString(dbName).get(), &newFile); - if ( newFile ) - { - if (NS_SUCCEEDED(ret)) - { - mdbOpenPolicy inOpenPolicy; - - inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0; - inOpenPolicy.mOpenPolicy_MinMemory = 0; - inOpenPolicy.mOpenPolicy_MaxLazy = 0; - - ret = mdbFactory->CreateNewFileStore(m_mdbEnv, dbHeap, - newFile, &inOpenPolicy, &m_mdbStore); - if (NS_SUCCEEDED(ret)) - ret = InitNewDB(); - } - NS_RELEASE(newFile); // always release our file ref, store has own - } - - } - NS_IF_RELEASE(thumb); - } + // Get the schema version + PRInt32 schemaVersion; + rv = m_dbConnection->GetSchemaVersion(&schemaVersion); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(schemaVersion == 0, NS_ERROR_FAILURE); } - return ret; + + // Preload all cache entries... + nsCOMPtr folderQuery; + rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT DISTINCT folderKey FROM entries"), getter_AddRefs(folderQuery)); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool hasMore; + nsCString value; + while (NS_SUCCEEDED(folderQuery->ExecuteStep(&hasMore)) && hasMore) + { + folderQuery->GetUTF8String(0, value); + nsMsgFolderCacheElement* element = new nsMsgFolderCacheElement( + this->m_dbConnection, value); + m_cacheElements.Put(value, element); + } + m_dbConnection->BeginTransaction(); + return NS_OK; } NS_IMETHODIMP nsMsgFolderCache::Init(nsIFile *aFile) @@ -269,26 +142,36 @@ NS_IMETHODIMP nsMsgFolderCache::Init(nsIFile *aFile) PRBool exists; aFile->Exists(&exists); - nsCAutoString dbPath; - aFile->GetNativePath(dbPath); - // ### evil cast until MDB supports file streams. - nsresult rv = OpenMDB(dbPath, exists); - // if this fails and panacea.dat exists, try blowing away the db and recreating it +#ifdef DEBUG + printf("Initializing folder cache\n"); +#endif + + nsresult rv = OpenSQL(aFile, !exists); + + // If we can't open a database, let's destroy it and rebuild it... if (NS_FAILED(rv) && exists) { - if (m_mdbStore) - m_mdbStore->Release(); + // If we got halfway, close it. + if (m_dbConnection) + { + m_dbConnection->Close(); + m_dbConnection = nsnull; + } +#ifdef DEBUG + printf("Initialization failed, recreating database\n"); +#endif aFile->Remove(PR_FALSE); - rv = OpenMDB(dbPath, PR_FALSE); + rv = OpenSQL(aFile, PR_TRUE); } return rv; } -NS_IMETHODIMP nsMsgFolderCache::GetCacheElement(const nsACString& pathKey, PRBool createIfMissing, - nsIMsgFolderCacheElement **result) +NS_IMETHODIMP nsMsgFolderCache::GetCacheElement(const nsACString& pathKey, + PRBool createIfMissing, nsIMsgFolderCacheElement **result) { NS_ENSURE_ARG_POINTER(result); NS_ENSURE_TRUE(!pathKey.IsEmpty(), NS_ERROR_FAILURE); + NS_ENSURE_TRUE(m_dbConnection, NS_ERROR_NOT_INITIALIZED); nsCOMPtr folderCacheEl; m_cacheElements.Get(pathKey, getter_AddRefs(folderCacheEl)); @@ -296,115 +179,72 @@ NS_IMETHODIMP nsMsgFolderCache::GetCacheElement(const nsACString& pathKey, PRBoo if (*result) return NS_OK; - else if (createIfMissing) + + if (createIfMissing) { - nsIMdbRow* hdrRow; - - if (GetStore()) - { - mdb_err err = GetStore()->NewRow(GetEnv(), m_folderRowScopeToken, // row scope for row ids - &hdrRow); - if (NS_SUCCEEDED(err) && hdrRow) - { - m_mdbAllFoldersTable->AddRow(GetEnv(), hdrRow); - nsresult ret = AddCacheElement(pathKey, hdrRow, result); - if (*result) - (*result)->SetStringProperty("key", pathKey); - hdrRow->Release(); - return ret; - } - } + folderCacheEl = new nsMsgFolderCacheElement(this->m_dbConnection, pathKey); + + // Copy a new hash key for storage purposes + m_cacheElements.Put(nsDependentCString(pathKey), folderCacheEl); + folderCacheEl.swap(*result); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsMsgFolderCache::RemoveElement(const nsACString& key) { + NS_ENSURE_TRUE(m_dbConnection, NS_ERROR_NOT_INITIALIZED); +#ifdef DEBUG + printf("Removing element %s from cache.\n", PromiseFlatCString(key).get()); +#endif nsCOMPtr folderCacheEl; - m_cacheElements.Get(key, getter_AddRefs(folderCacheEl)); - if (!folderCacheEl) + if (!m_cacheElements.Get(key, getter_AddRefs(folderCacheEl))) return NS_ERROR_FAILURE; - nsMsgFolderCacheElement *element = static_cast(static_cast(folderCacheEl.get())); // why the double cast?? - m_mdbAllFoldersTable->CutRow(GetEnv(), element->m_mdbRow); m_cacheElements.Remove(key); - return NS_OK; + + nsCOMPtr statement; + nsresult rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING( + "DELETE FROM entries WHERE folderKey=?1"), getter_AddRefs(statement)); + NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindUTF8StringParameter(0, key); + NS_ENSURE_SUCCESS(rv, rv); + return statement->Execute(); } NS_IMETHODIMP nsMsgFolderCache::Clear() { + NS_ENSURE_TRUE(m_dbConnection, NS_ERROR_NOT_INITIALIZED); +#ifdef DEBUG + printf("Clearing cache\n"); +#endif m_cacheElements.Clear(); - if (m_mdbAllFoldersTable) - m_mdbAllFoldersTable->CutAllRows(GetEnv()); - return NS_OK; + + return m_dbConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DELETE FROM entries")); } NS_IMETHODIMP nsMsgFolderCache::Close() { - return Commit(PR_TRUE); + nsresult rv = Commit(PR_TRUE); + NS_ENSURE_SUCCESS(rv, rv); + + rv = m_dbConnection->Close(); + m_dbConnection = nsnull; + return rv; } NS_IMETHODIMP nsMsgFolderCache::Commit(PRBool compress) { - nsresult ret = NS_OK; - nsIMdbThumb *commitThumb = nsnull; - if (m_mdbStore) + NS_ENSURE_TRUE(m_dbConnection, NS_ERROR_NOT_INITIALIZED); + nsresult rv = m_dbConnection->CommitTransaction(); + NS_ENSURE_SUCCESS(rv, rv); + + if (compress) { - if (compress) - ret = m_mdbStore->CompressCommit(GetEnv(), &commitThumb); - else - ret = m_mdbStore->LargeCommit(GetEnv(), &commitThumb); + rv = m_dbConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM")); + NS_ENSURE_SUCCESS(rv, rv); } - if (commitThumb) - { - mdb_count outTotal = 0; // total somethings to do in operation - mdb_count outCurrent = 0; // subportion of total completed so far - mdb_bool outDone = PR_FALSE; // is operation finished? - mdb_bool outBroken = PR_FALSE; // is operation irreparably dead and broken? - while (!outDone && !outBroken && NS_SUCCEEDED(ret)) - ret = commitThumb->DoMore(GetEnv(), &outTotal, &outCurrent, &outDone, &outBroken); - NS_IF_RELEASE(commitThumb); - } - // ### do something with error, but clear it now because mork errors out on commits. - if (GetEnv()) - GetEnv()->ClearErrors(); - return ret; -} - -nsresult nsMsgFolderCache::AddCacheElement(const nsACString& key, nsIMdbRow *row, nsIMsgFolderCacheElement **result) -{ - nsMsgFolderCacheElement *cacheElement = new nsMsgFolderCacheElement; - NS_ENSURE_TRUE(cacheElement, NS_ERROR_OUT_OF_MEMORY); - nsCOMPtr folderCacheEl(do_QueryInterface(cacheElement)); - - cacheElement->SetMDBRow(row); - cacheElement->SetOwningCache(this); - nsCString hashStrKey(key); - // if caller didn't pass in key, try to get it from row. - if (key.IsEmpty()) - folderCacheEl->GetStringProperty("key", hashStrKey); - folderCacheEl->SetKey(hashStrKey); - m_cacheElements.Put(hashStrKey, folderCacheEl); - if (result) - folderCacheEl.swap(*result); - return NS_OK; -} - -nsresult nsMsgFolderCache::RowCellColumnToCharPtr(nsIMdbRow *hdrRow, mdb_token columnToken, nsACString& resultStr) -{ - nsresult err = NS_OK; - nsIMdbCell *hdrCell; - if (hdrRow) // ### probably should be an error if hdrRow is NULL... - { - err = hdrRow->GetCell(GetEnv(), columnToken, &hdrCell); - if (NS_SUCCEEDED(err) && hdrCell) - { - struct mdbYarn yarn; - hdrCell->AliasYarn(GetEnv(), &yarn); - resultStr.Assign((const char *)yarn.mYarn_Buf, yarn.mYarn_Fill); - resultStr.SetLength(yarn.mYarn_Fill); // ensure the string is null terminated. - hdrCell->Release(); // always release ref - } - } - return err; + // Reinitiate our transaction + return m_dbConnection->BeginTransaction(); } diff --git a/mozilla/mailnews/base/src/nsMsgFolderCache.h b/mozilla/mailnews/base/src/nsMsgFolderCache.h index 6f653c469c7..9d58deea357 100644 --- a/mozilla/mailnews/base/src/nsMsgFolderCache.h +++ b/mozilla/mailnews/base/src/nsMsgFolderCache.h @@ -42,14 +42,12 @@ #include "nsIMsgFolderCacheElement.h" #include "nsInterfaceHashtable.h" #include "nsCOMPtr.h" -#include "mdb.h" +#include "mozIStorageConnection.h" class nsMsgFolderCache : public nsIMsgFolderCache { public: - friend class nsMsgFolderCacheElement; - nsMsgFolderCache(); virtual ~nsMsgFolderCache(); @@ -57,25 +55,9 @@ public: NS_DECL_NSIMSGFOLDERCACHE protected: - void GetMDBFactory(nsIMdbFactory ** aMdbFactory); - nsresult AddCacheElement(const nsACString& key, nsIMdbRow *row, nsIMsgFolderCacheElement **result); - nsresult RowCellColumnToCharPtr(nsIMdbRow *hdrRow, mdb_token columnToken, nsACString& resultPtr); - nsresult InitMDBInfo(); - nsresult InitNewDB(); - nsresult InitExistingDB(); - nsresult OpenMDB(const nsACString& dbName, PRBool create); - nsIMdbEnv *GetEnv() {return m_mdbEnv;} - nsIMdbStore *GetStore() {return m_mdbStore;} + nsresult OpenSQL(nsIFile * file, PRBool create); nsInterfaceHashtable m_cacheElements; - // mdb stuff - nsIMdbEnv *m_mdbEnv; // to be used in all the db calls. - nsIMdbStore *m_mdbStore; - nsIMdbTable *m_mdbAllFoldersTable; - mdb_token m_folderRowScopeToken; - mdb_token m_folderTableKindToken; - nsCOMPtr mMdbFactory; - - struct mdbOid m_allFoldersTableOID; + nsCOMPtr m_dbConnection; }; #endif diff --git a/mozilla/mailnews/base/src/nsMsgFolderCacheElement.cpp b/mozilla/mailnews/base/src/nsMsgFolderCacheElement.cpp index b83adb6701f..66ec62df393 100644 --- a/mozilla/mailnews/base/src/nsMsgFolderCacheElement.cpp +++ b/mozilla/mailnews/base/src/nsMsgFolderCacheElement.cpp @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Joshua Cranmer * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -39,18 +40,17 @@ #include "nsMsgFolderCacheElement.h" #include "prmem.h" #include "nsISupportsObsolete.h" +#include "mozIStorageStatement.h" -nsMsgFolderCacheElement::nsMsgFolderCacheElement() +nsMsgFolderCacheElement::nsMsgFolderCacheElement(mozIStorageConnection *connection, + const nsACString &key) +: m_dbConnection(connection), + m_folderKey(key) { - m_mdbRow = nsnull; - m_owningCache = nsnull; } nsMsgFolderCacheElement::~nsMsgFolderCacheElement() { - NS_IF_RELEASE(m_mdbRow); - // circular reference, don't do it. - // NS_IF_RELEASE(m_owningCache); } NS_IMPL_ISUPPORTS1(nsMsgFolderCacheElement, nsIMsgFolderCacheElement) @@ -67,94 +67,92 @@ NS_IMETHODIMP nsMsgFolderCacheElement::SetKey(const nsACString& aFolderKey) return NS_OK; } -void nsMsgFolderCacheElement::SetOwningCache(nsMsgFolderCache *owningCache) -{ - m_owningCache = owningCache; - // circular reference, don't do it. - // if (owningCache) - // NS_ADDREF(owningCache); -} - NS_IMETHODIMP nsMsgFolderCacheElement::GetStringProperty(const char *propertyName, nsACString& result) { NS_ENSURE_ARG_POINTER(propertyName); - NS_ENSURE_TRUE(m_mdbRow && m_owningCache, NS_ERROR_FAILURE); + PRBool connReady; + m_dbConnection->GetConnectionReady(&connReady); + NS_ASSERTION(connReady, "The database was already closed!"); - mdb_token property_token; - nsresult ret = m_owningCache->GetStore()->StringToToken(m_owningCache->GetEnv(), propertyName, &property_token); - if (NS_SUCCEEDED(ret)) - ret = m_owningCache->RowCellColumnToCharPtr(m_mdbRow, property_token, result); - return ret; + nsCOMPtr statement; + nsresult rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT value FROM entries WHERE folderKey=?1 AND propertyName=?2"), + getter_AddRefs(statement)); + NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindUTF8StringParameter(0, m_folderKey); + NS_ENSURE_SUCCESS(rv,rv); + rv = statement->BindUTF8StringParameter(1, nsDependentCString(propertyName)); + NS_ENSURE_SUCCESS(rv,rv); + + PRBool hasKey; + rv = statement->ExecuteStep(&hasKey); + NS_ENSURE_SUCCESS(rv, rv); + if (hasKey) + return statement->GetUTF8String(0, result); + + result.Truncate(); + return NS_ERROR_FAILURE; } NS_IMETHODIMP nsMsgFolderCacheElement::GetInt32Property(const char *propertyName, PRInt32 *aResult) { NS_ENSURE_ARG_POINTER(propertyName); NS_ENSURE_ARG_POINTER(aResult); - NS_ENSURE_TRUE(m_mdbRow, NS_ERROR_FAILURE); - nsCString resultStr; + nsCAutoString resultStr; GetStringProperty(propertyName, resultStr); if (resultStr.IsEmpty()) return NS_ERROR_FAILURE; - PRInt32 result = 0; - for (PRUint32 index = 0; index < resultStr.Length(); index++) - { - char C = resultStr.CharAt(index); - PRInt8 unhex = ((C >= '0' && C <= '9') ? C - '0' : - ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : - ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : -1))); - if (unhex < 0) - break; - result = (result << 4) | unhex; - } - *aResult = result; - return NS_OK; + // eww, ToInteger wants a PRInt32 whereas nsresult is a PRUint32... + PRInt32 err; + *aResult = resultStr.ToInteger(&err); + return err; } NS_IMETHODIMP nsMsgFolderCacheElement::SetStringProperty(const char *propertyName, const nsACString& propertyValue) { NS_ENSURE_ARG_POINTER(propertyName); - NS_ENSURE_TRUE(m_mdbRow, NS_ERROR_FAILURE); - nsresult rv = NS_OK; - mdb_token property_token; + PRBool connReady; + m_dbConnection->GetConnectionReady(&connReady); + NS_ASSERTION(connReady, "The database was already closed!"); - if (m_owningCache) + nsCOMPtr statement; + + // Find the current property value + nsCString currentValue; + nsresult rv = GetStringProperty(propertyName, currentValue); + // Update it if it exists... + if (NS_SUCCEEDED(rv)) { - rv = m_owningCache->GetStore()->StringToToken(m_owningCache->GetEnv(), propertyName, &property_token); - if (NS_SUCCEEDED(rv)) - { - struct mdbYarn yarn; - - yarn.mYarn_Grow = NULL; - if (m_mdbRow) - { - nsCString propertyVal (propertyValue); - yarn.mYarn_Buf = (void *) propertyVal.get(); - yarn.mYarn_Size = strlen((const char *) yarn.mYarn_Buf) + 1; - yarn.mYarn_Fill = yarn.mYarn_Size - 1; - yarn.mYarn_Form = 0; // what to do with this? we're storing csid in the msg hdr... - rv = m_mdbRow->AddColumn(m_owningCache->GetEnv(), property_token, &yarn); - return rv; - } - } + // Commented out to prevent large spamming of output. + //NS_ASSERTION(!currentValue.Equals(propertyValue), "Should only set non-equal values"); + if (currentValue.Equals(propertyValue)) + return NS_OK; + rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE entries SET value=?3 WHERE folderKey=?1 AND propertyName=?2"), + getter_AddRefs(statement)); } - return rv; + else + rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO entries VALUES (?1, ?2, ?3)"), getter_AddRefs(statement)); + + NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindUTF8StringParameter(0, m_folderKey); + NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindUTF8StringParameter(1, nsCString(propertyName)); + NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindUTF8StringParameter(2, propertyValue); + NS_ENSURE_SUCCESS(rv, rv); + rv = statement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } NS_IMETHODIMP nsMsgFolderCacheElement::SetInt32Property(const char *propertyName, PRInt32 propertyValue) { NS_ENSURE_ARG_POINTER(propertyName); - NS_ENSURE_TRUE(m_mdbRow, NS_ERROR_FAILURE); nsCAutoString propertyStr; - propertyStr.AppendInt(propertyValue, 16); + propertyStr.AppendInt(propertyValue, 10); return SetStringProperty(propertyName, propertyStr); } - -void nsMsgFolderCacheElement::SetMDBRow(nsIMdbRow *row) -{ - if (m_mdbRow) - NS_RELEASE(m_mdbRow); - NS_IF_ADDREF(m_mdbRow = row); -} diff --git a/mozilla/mailnews/base/src/nsMsgFolderCacheElement.h b/mozilla/mailnews/base/src/nsMsgFolderCacheElement.h index bc679755114..7b127f614de 100644 --- a/mozilla/mailnews/base/src/nsMsgFolderCacheElement.h +++ b/mozilla/mailnews/base/src/nsMsgFolderCacheElement.h @@ -40,26 +40,18 @@ #include "nsIMsgFolderCacheElement.h" #include "nsMsgFolderCache.h" -#include "mdb.h" class nsMsgFolderCacheElement : public nsIMsgFolderCacheElement { public: - nsMsgFolderCacheElement(); + nsMsgFolderCacheElement(mozIStorageConnection *connection, const nsACString &key); virtual ~nsMsgFolderCacheElement(); - friend class nsMsgFolderCache; NS_DECL_ISUPPORTS NS_DECL_NSIMSGFOLDERCACHEELEMENT - void SetMDBRow(nsIMdbRow *row); - void SetOwningCache(nsMsgFolderCache *owningCache); protected: - nsIMdbRow *m_mdbRow; - - nsMsgFolderCache *m_owningCache; // this will be ref-counted. Is this going to be a problem? - // I want to avoid circular references, but since this is - // scriptable, I think I have to ref-count it. + nsCOMPtr m_dbConnection; nsCString m_folderKey; }; diff --git a/mozilla/mailnews/base/test/unit/test_nsMailDirProvider.js b/mozilla/mailnews/base/test/unit/test_nsMailDirProvider.js index 0ca8a25c8e2..94d6d31ab88 100644 --- a/mozilla/mailnews/base/test/unit/test_nsMailDirProvider.js +++ b/mozilla/mailnews/base/test/unit/test_nsMailDirProvider.js @@ -60,7 +60,7 @@ function run_test() { const items = [ { key: "MailD", value: "Mail" }, { key: "IMapMD", value: "ImapMail" }, { key: "NewsD", value: "News" }, - { key: "MFCaF", value: "panacea.dat" } ]; + { key: "MFCaF", value: "msgFolderCache.sqlite" } ]; items.forEach(function(item) { var dir = dirSvc.get(item.key, nsIFile); diff --git a/mozilla/mailnews/build/Makefile.in b/mozilla/mailnews/build/Makefile.in index 1c47a7d00a6..7400cfe0950 100644 --- a/mozilla/mailnews/build/Makefile.in +++ b/mozilla/mailnews/build/Makefile.in @@ -84,6 +84,7 @@ REQUIRES = xpcom \ msgdb \ mime \ mork \ + storage \ necko \ nkcache \ pref \