This commit is contained in:
jminta%gmail.com 2008-04-09 23:48:19 +00:00
Родитель c1f79daa1a
Коммит 7f6bcf8a0b
11 изменённых файлов: 390 добавлений и 307 удалений

Просмотреть файл

@ -72,7 +72,6 @@ REQUIRES = xpcom \
mailnews \
addrbook \
mork \
storage \
txmgr \
pref \
msgcompose \

Просмотреть файл

@ -20,7 +20,6 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joshua Cranmer <Pidgeot18@gmail.com>
*
* 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"),
@ -42,65 +41,13 @@
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:
* <code>
* CREATE TABLE entries (
* folderKey CHAR,
* propertyName CHAR,
* value CHAR
* );
* </code>
*
* 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);
/**
* 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.
*/
nsIMsgFolderCacheElement GetCacheElement(in ACString key, in boolean createIfMissing);
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);
};

Просмотреть файл

@ -20,7 +20,6 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joshua Cranmer <Pidgeot18@gmail.com>
*
* 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,61 +38,12 @@
#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);
};

Просмотреть файл

@ -77,7 +77,7 @@ REQUIRES = xpcom \
pref \
msglocal \
msgimap \
storage \
mork \
msgnews \
addrbook \
mime \

Просмотреть файл

@ -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 "msgFolderCache.sqlite"
#define MSG_FOLDER_CACHE_DIR_50_NAME "panacea.dat"
nsresult
nsMailDirProvider::EnsureDirectory(nsIFile *aDirectory)

Просмотреть файл

@ -21,7 +21,6 @@
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Joshua Cranmer <Pidgeot18@gmail.com>
*
* 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,96 +40,224 @@
#include "nsIMsgAccountManager.h"
#include "nsMsgFolderCacheElement.h"
#include "nsMsgFolderCache.h"
#include "nsMorkCID.h"
#include "nsIMdbFactoryFactory.h"
#include "nsMsgBaseCID.h"
#include "mozStorageCID.h"
#include "mozIStorageService.h"
#include "mozIStorageStatement.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";
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()
{
// 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();
}
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();
}
NS_IMPL_ISUPPORTS1(nsMsgFolderCache, nsIMsgFolderCache)
nsresult nsMsgFolderCache::OpenSQL(nsIFile * dbFile, PRBool create)
void nsMsgFolderCache::GetMDBFactory(nsIMdbFactory ** aMdbFactory)
{
// 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)
if (!mMdbFactory)
{
m_dbConnection->RollbackTransaction();
m_dbConnection->Close();
nsresult rv;
nsCOMPtr <nsIMdbFactoryService> mdbFactoryService = do_GetService(NS_MORK_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && mdbFactoryService)
rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory));
}
NS_IF_ADDREF(*aMdbFactory = mMdbFactory);
}
nsresult rv;
nsCOMPtr<mozIStorageService> 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)
// initialize the various tokens and tables in our db's env
nsresult nsMsgFolderCache::InitMDBInfo()
{
nsresult err = NS_OK;
if (GetStore())
{
// 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);
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();
}
}
else
{
// Get the schema version
PRInt32 schemaVersion;
rv = m_dbConnection->GetSchemaVersion(&schemaVersion);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(schemaVersion == 0, NS_ERROR_FAILURE);
}
err = NS_ERROR_FAILURE;
// Preload all cache entries...
nsCOMPtr<mozIStorageStatement> folderQuery;
rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT DISTINCT folderKey FROM entries"), getter_AddRefs(folderQuery));
NS_ENSURE_SUCCESS(rv, rv);
return err;
}
PRBool hasMore;
nsCString value;
while (NS_SUCCEEDED(folderQuery->ExecuteStep(&hasMore)) && hasMore)
nsresult nsMsgFolderCache::OpenMDB(const nsACString& dbName, PRBool exists)
{
nsresult ret=NS_OK;
nsCOMPtr<nsIMdbFactory> mdbFactory;
GetMDBFactory(getter_AddRefs(mdbFactory));
if (mdbFactory)
{
folderQuery->GetUTF8String(0, value);
nsMsgFolderCacheElement* element = new nsMsgFolderCacheElement(
this->m_dbConnection, value);
m_cacheElements.Put(value, element);
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);
}
}
m_dbConnection->BeginTransaction();
return NS_OK;
return ret;
}
NS_IMETHODIMP nsMsgFolderCache::Init(nsIFile *aFile)
@ -142,36 +269,26 @@ NS_IMETHODIMP nsMsgFolderCache::Init(nsIFile *aFile)
PRBool exists;
aFile->Exists(&exists);
#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...
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
if (NS_FAILED(rv) && exists)
{
// If we got halfway, close it.
if (m_dbConnection)
{
m_dbConnection->Close();
m_dbConnection = nsnull;
}
#ifdef DEBUG
printf("Initialization failed, recreating database\n");
#endif
if (m_mdbStore)
m_mdbStore->Release();
aFile->Remove(PR_FALSE);
rv = OpenSQL(aFile, PR_TRUE);
rv = OpenMDB(dbPath, PR_FALSE);
}
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<nsIMsgFolderCacheElement> folderCacheEl;
m_cacheElements.Get(pathKey, getter_AddRefs(folderCacheEl));
@ -179,72 +296,115 @@ NS_IMETHODIMP nsMsgFolderCache::GetCacheElement(const nsACString& pathKey,
if (*result)
return NS_OK;
if (createIfMissing)
else if (createIfMissing)
{
folderCacheEl = new nsMsgFolderCacheElement(this->m_dbConnection, pathKey);
nsIMdbRow* hdrRow;
// Copy a new hash key for storage purposes
m_cacheElements.Put(nsDependentCString(pathKey), folderCacheEl);
folderCacheEl.swap(*result);
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;
}
}
}
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<nsIMsgFolderCacheElement> folderCacheEl;
if (!m_cacheElements.Get(key, getter_AddRefs(folderCacheEl)))
m_cacheElements.Get(key, getter_AddRefs(folderCacheEl));
if (!folderCacheEl)
return NS_ERROR_FAILURE;
nsMsgFolderCacheElement *element = static_cast<nsMsgFolderCacheElement *>(static_cast<nsISupports *>(folderCacheEl.get())); // why the double cast??
m_mdbAllFoldersTable->CutRow(GetEnv(), element->m_mdbRow);
m_cacheElements.Remove(key);
nsCOMPtr<mozIStorageStatement> 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();
return NS_OK;
}
NS_IMETHODIMP nsMsgFolderCache::Clear()
{
NS_ENSURE_TRUE(m_dbConnection, NS_ERROR_NOT_INITIALIZED);
#ifdef DEBUG
printf("Clearing cache\n");
#endif
m_cacheElements.Clear();
return m_dbConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"DELETE FROM entries"));
if (m_mdbAllFoldersTable)
m_mdbAllFoldersTable->CutAllRows(GetEnv());
return NS_OK;
}
NS_IMETHODIMP nsMsgFolderCache::Close()
{
nsresult rv = Commit(PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
rv = m_dbConnection->Close();
m_dbConnection = nsnull;
return rv;
return Commit(PR_TRUE);
}
NS_IMETHODIMP nsMsgFolderCache::Commit(PRBool compress)
{
NS_ENSURE_TRUE(m_dbConnection, NS_ERROR_NOT_INITIALIZED);
nsresult rv = m_dbConnection->CommitTransaction();
NS_ENSURE_SUCCESS(rv, rv);
if (compress)
nsresult ret = NS_OK;
nsIMdbThumb *commitThumb = nsnull;
if (m_mdbStore)
{
rv = m_dbConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM"));
NS_ENSURE_SUCCESS(rv, rv);
if (compress)
ret = m_mdbStore->CompressCommit(GetEnv(), &commitThumb);
else
ret = m_mdbStore->LargeCommit(GetEnv(), &commitThumb);
}
// Reinitiate our transaction
return m_dbConnection->BeginTransaction();
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<nsIMsgFolderCacheElement> 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;
}

Просмотреть файл

@ -42,12 +42,14 @@
#include "nsIMsgFolderCacheElement.h"
#include "nsInterfaceHashtable.h"
#include "nsCOMPtr.h"
#include "mozIStorageConnection.h"
#include "mdb.h"
class nsMsgFolderCache : public nsIMsgFolderCache
{
public:
friend class nsMsgFolderCacheElement;
nsMsgFolderCache();
virtual ~nsMsgFolderCache();
@ -55,9 +57,25 @@ public:
NS_DECL_NSIMSGFOLDERCACHE
protected:
nsresult OpenSQL(nsIFile * file, PRBool create);
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;}
nsInterfaceHashtable<nsCStringHashKey, nsIMsgFolderCacheElement> m_cacheElements;
nsCOMPtr<mozIStorageConnection> m_dbConnection;
// 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<nsIMdbFactory> mMdbFactory;
struct mdbOid m_allFoldersTableOID;
};
#endif

Просмотреть файл

@ -20,7 +20,6 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joshua Cranmer <Pidgeot18@gmail.com>
*
* 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,17 +39,18 @@
#include "nsMsgFolderCacheElement.h"
#include "prmem.h"
#include "nsISupportsObsolete.h"
#include "mozIStorageStatement.h"
nsMsgFolderCacheElement::nsMsgFolderCacheElement(mozIStorageConnection *connection,
const nsACString &key)
: m_dbConnection(connection),
m_folderKey(key)
nsMsgFolderCacheElement::nsMsgFolderCacheElement()
{
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,92 +67,94 @@ 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);
PRBool connReady;
m_dbConnection->GetConnectionReady(&connReady);
NS_ASSERTION(connReady, "The database was already closed!");
NS_ENSURE_TRUE(m_mdbRow && m_owningCache, NS_ERROR_FAILURE);
nsCOMPtr<mozIStorageStatement> 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;
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;
}
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);
nsCAutoString resultStr;
nsCString resultStr;
GetStringProperty(propertyName, resultStr);
if (resultStr.IsEmpty())
return NS_ERROR_FAILURE;
// eww, ToInteger wants a PRInt32 whereas nsresult is a PRUint32...
PRInt32 err;
*aResult = resultStr.ToInteger(&err);
return err;
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;
}
NS_IMETHODIMP nsMsgFolderCacheElement::SetStringProperty(const char *propertyName, const nsACString& propertyValue)
{
NS_ENSURE_ARG_POINTER(propertyName);
PRBool connReady;
m_dbConnection->GetConnectionReady(&connReady);
NS_ASSERTION(connReady, "The database was already closed!");
NS_ENSURE_TRUE(m_mdbRow, NS_ERROR_FAILURE);
nsresult rv = NS_OK;
mdb_token property_token;
nsCOMPtr<mozIStorageStatement> statement;
// Find the current property value
nsCString currentValue;
nsresult rv = GetStringProperty(propertyName, currentValue);
// Update it if it exists...
if (NS_SUCCEEDED(rv))
if (m_owningCache)
{
// 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));
}
else
rv = m_dbConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO entries VALUES (?1, ?2, ?3)"), getter_AddRefs(statement));
rv = m_owningCache->GetStore()->StringToToken(m_owningCache->GetEnv(), propertyName, &property_token);
if (NS_SUCCEEDED(rv))
{
struct mdbYarn yarn;
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;
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;
}
}
}
return rv;
}
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, 10);
propertyStr.AppendInt(propertyValue, 16);
return SetStringProperty(propertyName, propertyStr);
}
void nsMsgFolderCacheElement::SetMDBRow(nsIMdbRow *row)
{
if (m_mdbRow)
NS_RELEASE(m_mdbRow);
NS_IF_ADDREF(m_mdbRow = row);
}

Просмотреть файл

@ -40,18 +40,26 @@
#include "nsIMsgFolderCacheElement.h"
#include "nsMsgFolderCache.h"
#include "mdb.h"
class nsMsgFolderCacheElement : public nsIMsgFolderCacheElement
{
public:
nsMsgFolderCacheElement(mozIStorageConnection *connection, const nsACString &key);
nsMsgFolderCacheElement();
virtual ~nsMsgFolderCacheElement();
friend class nsMsgFolderCache;
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGFOLDERCACHEELEMENT
void SetMDBRow(nsIMdbRow *row);
void SetOwningCache(nsMsgFolderCache *owningCache);
protected:
nsCOMPtr<mozIStorageConnection> m_dbConnection;
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.
nsCString m_folderKey;
};

Просмотреть файл

@ -60,7 +60,7 @@ function run_test() {
const items = [ { key: "MailD", value: "Mail" },
{ key: "IMapMD", value: "ImapMail" },
{ key: "NewsD", value: "News" },
{ key: "MFCaF", value: "msgFolderCache.sqlite" } ];
{ key: "MFCaF", value: "panacea.dat" } ];
items.forEach(function(item) {
var dir = dirSvc.get(item.key, nsIFile);

Просмотреть файл

@ -84,7 +84,6 @@ REQUIRES = xpcom \
msgdb \
mime \
mork \
storage \
necko \
nkcache \
pref \