add ability to open mork db's asynchronously, r=asuth, sr=neil, bug 570582

This commit is contained in:
David Bienvenu 2010-06-22 08:57:11 -07:00
Родитель 864d6f9c38
Коммит 93d2b283df
5 изменённых файлов: 364 добавлений и 88 удалений

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

@ -137,7 +137,7 @@ interface nsMsgDBCommitType
* The contract ID for this component is
* <tt>\@mozilla.org/msgDatabase/msgDBService;1</tt>.
*/
[scriptable, uuid(e25d07fc-2a85-437f-9227-065c88a0b29f)]
[scriptable, uuid(6ddaeb28-0499-4be1-85f7-7a5d20f80256)]
interface nsIMsgDBService : nsISupports
{
/**
@ -170,6 +170,31 @@ interface nsIMsgDBService : nsISupports
nsIMsgDatabase openFolderDB(in nsIMsgFolder aFolder,
in boolean aLeaveInvalidDB);
/**
* This is the same as a synchronous open in terms of params and errors.
* But to finish opening the db, the caller must call
* nsIMsgDBService::OpenMore repeatedly until the open is finished.
* @see nsIMsgDBService::openFolderDB
* @see nsIMsgDBService::openMore
*/
nsIMsgDatabase asyncOpenFolderDB(in nsIMsgFolder aFolder,
in boolean aLeaveInvalidDB);
/**
* Continues the open process for a db opened with
* nsIMsgDBService::asyncOpenFolderDB. Returns true if the db is ready
* to use, false if openMore needs to be called again.
* This will throw the same kinds of exceptions as openFolderDB.
* @param aTimeHint approximate number of milliseconds to spend
* before returning. This is more of a floor than
a ceiling, since we can't guarantee that there
won't be one big chunk that we can't interrupt.
* @return true if db is ready to use, false if openMore needs to
* be called again.
* @see nsIMsgDBService::openFolderDB
*/
boolean openMore(in nsIMsgDatabase aDB, in long aTimeHint);
/**
* Creates a new database for the given folder.
*

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

@ -76,6 +76,7 @@ public:
~nsMsgDBService();
protected:
void HookupPendingListeners(nsIMsgDatabase *db, nsIMsgFolder *folder);
void FinishDBOpen(nsIMsgFolder *aFolder, nsMsgDatabase *aMsgDB);
nsCOMArray <nsIMsgFolder> m_foldersPendingListeners;
nsCOMArray <nsIDBChangeListener> m_pendingListeners;
@ -143,7 +144,10 @@ public:
virtual nsresult IsHeaderRead(nsIMsgDBHdr *hdr, PRBool *pRead);
virtual nsresult MarkHdrReadInDB(nsIMsgDBHdr *msgHdr, PRBool bRead,
nsIDBChangeListener *instigator);
virtual nsresult OpenMDB(const char *dbName, PRBool create);
nsresult OpenInternal(nsILocalFile *aFolderName, PRBool aCreate,
PRBool aLeaveInvalidDB, PRBool sync);
nsresult CheckForErrors(nsresult err, nsILocalFile *summaryFile);
virtual nsresult OpenMDB(const char *dbName, PRBool create, PRBool sync);
virtual nsresult CloseMDB(PRBool commit);
virtual nsresult CreateMsgHdr(nsIMdbRow* hdrRow, nsMsgKey key, nsIMsgDBHdr **result);
virtual nsresult GetThreadForMsgKey(nsMsgKey msgKey, nsIMsgThread **result);
@ -299,6 +303,13 @@ protected:
nsIMdbStore *m_mdbStore;
nsIMdbTable *m_mdbAllMsgHeadersTable;
nsIMdbTable *m_mdbAllThreadsTable;
// Used for asynchronous db opens. If non-null, we're still opening
// the underlying mork database. If null, the db has been completely opened.
nsCOMPtr<nsIMdbThumb> m_thumb;
// used to remember the args to Open for async open.
PRPackedBool m_create;
PRPackedBool m_leaveInvalidDB;
nsCString m_dbName;
nsTArray<nsMsgKey> m_newSet; // new messages since last open.
PRBool m_mdbTokensInitialized;

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

@ -718,7 +718,7 @@ nsresult nsMailDatabase::SetFolderInfoValid(nsILocalFile *folderName, int num, i
// ### this does later stuff (marks latered messages unread), which may be a problem
nsCString summaryNativePath;
summaryPath->GetNativePath(summaryNativePath);
err = pMessageDB->OpenMDB(summaryNativePath.get(), PR_FALSE);
err = pMessageDB->OpenMDB(summaryNativePath.get(), PR_FALSE, PR_TRUE);
if (err != NS_OK)
{
delete pMessageDB;
@ -726,15 +726,15 @@ nsresult nsMailDatabase::SetFolderInfoValid(nsILocalFile *folderName, int num, i
}
bOpenedDB = PR_TRUE;
}
if (pMessageDB == nsnull)
if (!pMessageDB)
{
#ifdef DEBUG
printf("Exception opening summary file\n");
#endif
return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
{
pMessageDB->m_folderFile = folderName;
PRUint32 actualFolderTimeStamp;

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

@ -119,6 +119,9 @@ NS_IMETHODIMP nsMsgDBService::OpenFolderDB(nsIMsgFolder *aFolder,
nsIMsgDatabase **_retval)
{
NS_ENSURE_ARG(aFolder);
nsCOMPtr <nsILocalFile> folderPath;
nsresult rv = aFolder->GetFilePath(getter_AddRefs(folderPath));
NS_ENSURE_SUCCESS(rv, rv);
nsMsgDatabase *cacheDB = (nsMsgDatabase *) nsMsgDatabase::FindInCache(aFolder);
if (cacheDB)
{
@ -127,11 +130,15 @@ NS_IMETHODIMP nsMsgDBService::OpenFolderDB(nsIMsgFolder *aFolder,
if (!cacheDB->m_folder)
cacheDB->m_folder = aFolder;
*_retval = cacheDB; // FindInCache already addRefed.
// if m_thumb is set, someone is asynchronously opening the db. But our
// caller wants to synchronously open it, so just do it.
if (cacheDB->m_thumb)
return cacheDB->Open(folderPath, PR_FALSE, aLeaveInvalidDB);
return NS_OK;
}
nsCOMPtr <nsIMsgIncomingServer> incomingServer;
nsresult rv = aFolder->GetServer(getter_AddRefs(incomingServer));
rv = aFolder->GetServer(getter_AddRefs(incomingServer));
NS_ENSURE_SUCCESS(rv, rv);
nsCString localStoreType;
incomingServer->GetLocalStoreType(localStoreType);
@ -139,16 +146,13 @@ NS_IMETHODIMP nsMsgDBService::OpenFolderDB(nsIMsgFolder *aFolder,
dbContractID.Append(localStoreType.get());
nsCOMPtr <nsIMsgDatabase> msgDB = do_CreateInstance(dbContractID.get(), &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsILocalFile> folderPath;
rv = aFolder->GetFilePath(getter_AddRefs(folderPath));
NS_ENSURE_SUCCESS(rv, rv);
// Don't try to create the database yet--let the createNewDB call do that.
rv = msgDB->Open(folderPath, PR_FALSE, aLeaveInvalidDB);
if (NS_FAILED(rv) && rv != NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
return rv;
NS_IF_ADDREF(*_retval = msgDB);
NS_ADDREF(*_retval = msgDB);
nsMsgDatabase *msgDatabase = static_cast<nsMsgDatabase *>(*_retval);
msgDatabase->m_folder = aFolder;
@ -163,30 +167,126 @@ NS_IMETHODIMP nsMsgDBService::OpenFolderDB(nsIMsgFolder *aFolder,
rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
return rv;
// If its not one of the expected errors, throw a warning.
// If it isn't one of the expected errors, throw a warning.
NS_ENSURE_SUCCESS(rv, rv);
#endif
return rv;
}
PRUint32 folderFlags;
aFolder->GetFlags(&folderFlags);
FinishDBOpen(aFolder, msgDatabase);
return rv;
}
if (NS_SUCCEEDED(rv) && ! (folderFlags & nsMsgFolderFlags::Virtual))
NS_IMETHODIMP nsMsgDBService::AsyncOpenFolderDB(nsIMsgFolder *aFolder,
PRBool aLeaveInvalidDB,
nsIMsgDatabase **_retval)
{
NS_ENSURE_ARG(aFolder);
nsMsgDatabase *cacheDB = (nsMsgDatabase *) nsMsgDatabase::FindInCache(aFolder);
nsCOMPtr <nsILocalFile> folderPath;
nsresult rv = aFolder->GetFilePath(getter_AddRefs(folderPath));
NS_ENSURE_SUCCESS(rv, rv);
if (cacheDB)
{
mdb_count numHdrsInTable = 0;
// this db could have ended up in the folder cache w/o an m_folder pointer via
// OpenMailDBFromFile. If so, take this chance to fix the folder.
if (!cacheDB->m_folder)
cacheDB->m_folder = aFolder;
*_retval = cacheDB; // FindInCache already addRefed.
// We don't care if an other consumer is thumbing the store. In that
// case, they'll both thumb the store.
return NS_OK;
}
if (msgDatabase->m_mdbAllMsgHeadersTable)
nsCOMPtr <nsIMsgIncomingServer> incomingServer;
rv = aFolder->GetServer(getter_AddRefs(incomingServer));
NS_ENSURE_SUCCESS(rv, rv);
nsCString localStoreType;
incomingServer->GetLocalStoreType(localStoreType);
nsCAutoString dbContractID(NS_MSGDB_CONTRACTID);
dbContractID.Append(localStoreType.get());
nsCOMPtr <nsIMsgDatabase> msgDB = do_CreateInstance(dbContractID.get(), &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsMsgDatabase *msgDatabase = static_cast<nsMsgDatabase *>(msgDB.get());
rv = msgDatabase->OpenInternal(folderPath, PR_FALSE, aLeaveInvalidDB,
PR_FALSE /* open asynchronously */);
NS_ADDREF(*_retval = msgDB);
msgDatabase->m_folder = aFolder;
if (NS_FAILED(rv))
{
#ifdef DEBUG
// Doing these checks for debug only as we don't want to report certain
// errors in debug mode, but in release mode we wouldn't report them either
// These errors are expected.
if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING ||
rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
return rv;
// If it isn't one of the expected errors, throw a warning.
NS_ENSURE_SUCCESS(rv, rv);
#endif
return rv;
}
FinishDBOpen(aFolder, msgDatabase);
return rv;
}
NS_IMETHODIMP nsMsgDBService::OpenMore(nsIMsgDatabase *aDB,
PRInt32 aTimeHint,
PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
nsMsgDatabase *msgDatabase = static_cast<nsMsgDatabase *>(aDB);
NS_ENSURE_TRUE(msgDatabase, NS_ERROR_INVALID_ARG);
// Check if this db has been opened.
if (!msgDatabase->m_thumb)
{
*_retval = PR_TRUE;
return NS_OK;
}
nsresult ret;
*_retval = PR_FALSE;
PRIntervalTime startTime = PR_IntervalNow();
do
{
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?
ret = msgDatabase->m_thumb->DoMore(msgDatabase->m_mdbEnv,
&outTotal, &outCurrent, &outDone,
&outBroken);
if (NS_FAILED(ret))
break;
if (outDone)
{
PRInt32 numMessages;
msgDatabase->m_mdbAllMsgHeadersTable->GetCount(msgDatabase->GetEnv(), &numHdrsInTable);
msgDatabase->m_dbFolderInfo->GetNumMessages(&numMessages);
if (numMessages != (PRInt32) numHdrsInTable)
msgDatabase->SyncCounts();
nsCOMPtr<nsIMdbFactory> mdbFactory;
msgDatabase->GetMDBFactory(getter_AddRefs(mdbFactory));
NS_ENSURE_TRUE(mdbFactory, NS_ERROR_FAILURE);
ret = mdbFactory->ThumbToOpenStore(msgDatabase->m_mdbEnv, msgDatabase->m_thumb, &msgDatabase->m_mdbStore);
msgDatabase->m_thumb = nsnull;
nsCOMPtr<nsILocalFile> folderPath;
nsresult rv = msgDatabase->m_folder->GetFilePath(getter_AddRefs(folderPath));
nsCOMPtr <nsILocalFile> summaryFile;
rv = GetSummaryFileLocation(folderPath, getter_AddRefs(summaryFile));
if (NS_SUCCEEDED(ret))
ret = (msgDatabase->m_mdbStore) ? msgDatabase->InitExistingDB() : NS_ERROR_FAILURE;
if (NS_SUCCEEDED(ret))
ret = msgDatabase->CheckForErrors(ret, summaryFile);
FinishDBOpen(msgDatabase->m_folder, msgDatabase);
break;
}
}
HookupPendingListeners(msgDB, aFolder);
return rv;
while (PR_IntervalToMilliseconds(PR_IntervalNow() - startTime) <= aTimeHint);
*_retval = !msgDatabase->m_thumb;
return ret;
}
/**
@ -208,6 +308,25 @@ void nsMsgDBService::HookupPendingListeners(nsIMsgDatabase *db,
}
}
void nsMsgDBService::FinishDBOpen(nsIMsgFolder *aFolder, nsMsgDatabase *aMsgDB)
{
PRUint32 folderFlags;
aFolder->GetFlags(&folderFlags);
if (! (folderFlags & nsMsgFolderFlags::Virtual) &&
aMsgDB->m_mdbAllMsgHeadersTable)
{
mdb_count numHdrsInTable = 0;
PRInt32 numMessages;
aMsgDB->m_mdbAllMsgHeadersTable->GetCount(aMsgDB->GetEnv(),
&numHdrsInTable);
aMsgDB->m_dbFolderInfo->GetNumMessages(&numMessages);
if (numMessages != (PRInt32) numHdrsInTable)
aMsgDB->SyncCounts();
}
HookupPendingListeners(aMsgDB, aFolder);
}
// This method is called when the caller is trying to create a db without
// having a corresponding nsIMsgFolder object. This happens in a few
// situations, including imap folder discovery, compacting local folders,
@ -260,7 +379,7 @@ NS_IMETHODIMP nsMsgDBService::CreateNewDB(nsIMsgFolder *aFolder,
rv = msgDB->Open(folderPath, PR_TRUE, PR_TRUE);
NS_ENSURE_TRUE(rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING, rv);
NS_IF_ADDREF(*_retval = msgDB);
NS_ADDREF(*_retval = msgDB);
nsMsgDatabase *msgDatabase = static_cast<nsMsgDatabase *>(*_retval);
msgDatabase->m_folder = aFolder;
@ -462,6 +581,7 @@ void nsMsgDatabase::ClearCachedObjects(PRBool dbGoingAway)
ClearUseHdrCache();
m_cachedThread = nsnull;
m_cachedThreadId = nsMsgKey_None;
m_thumb = nsnull;
}
nsresult nsMsgDatabase::ClearHdrCache(PRBool reInit)
@ -866,6 +986,8 @@ nsMsgDatabase::nsMsgDatabase()
m_nextPseudoMsgKey(kFirstPseudoKey),
m_mdbEnv(nsnull), m_mdbStore(nsnull),
m_mdbAllMsgHeadersTable(nsnull), m_mdbAllThreadsTable(nsnull),
m_create(PR_FALSE),
m_leaveInvalidDB(PR_FALSE),
m_dbName(""),
m_mdbTokensInitialized(PR_FALSE),
m_hdrRowScopeToken(0),
@ -990,9 +1112,13 @@ void nsMsgDatabase::GetMDBFactory(nsIMdbFactory ** aMdbFactory)
// then try to open the db again, prior to reparsing.
NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRBool aLeaveInvalidDB)
{
PRBool summaryFileExists;
PRBool newFile = PR_FALSE;
PRBool deleteInvalidDB = PR_FALSE;
return nsMsgDatabase::OpenInternal(aFolderName, aCreate, aLeaveInvalidDB,
PR_TRUE /* open synchronously */);
}
nsresult nsMsgDatabase::OpenInternal(nsILocalFile *aFolderName, PRBool aCreate,
PRBool aLeaveInvalidDB, PRBool sync)
{
if (!aFolderName)
return NS_ERROR_NULL_POINTER;
@ -1000,21 +1126,6 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
nsresult err = GetSummaryFileLocation(aFolderName, getter_AddRefs(summaryFile));
NS_ENSURE_SUCCESS(err, err);
nsIDBFolderInfo *folderInfo = nsnull;
PRBool exists;
PRInt64 fileSize;
summaryFile->Exists(&exists);
summaryFile->GetFileSize(&fileSize);
// if the old summary doesn't exist, we're creating a new one.
if ((!exists || !fileSize) && aCreate)
newFile = PR_TRUE;
// stat file before we open the db, because if we've latered
// any messages, handling latered will change time stamp on
// folder file.
summaryFileExists = exists && fileSize > 0;
nsCAutoString summaryFilePath;
summaryFile->GetNativePath(summaryFilePath);
@ -1023,7 +1134,7 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
this, aLeaveInvalidDB ? "TRUE":"FALSE"));
err = OpenMDB(summaryFilePath.get(), aCreate);
err = OpenMDB(summaryFilePath.get(), aCreate, sync);
if (NS_FAILED(err))
PR_LOG(DBLog, PR_LOG_ALWAYS, ("error opening db %lx", err));
@ -1033,10 +1144,38 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
if (err == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
return err;
m_create = aCreate;
m_leaveInvalidDB = aLeaveInvalidDB;
if (!sync && NS_SUCCEEDED(err))
{
AddToCache(this);
// remember open options for when the parsing is complete.
return err;
}
return CheckForErrors(err, summaryFile);
}
nsresult nsMsgDatabase::CheckForErrors(nsresult err,
nsILocalFile *summaryFile)
{
nsCOMPtr<nsIDBFolderInfo> folderInfo;
PRBool summaryFileExists;
PRBool newFile = PR_FALSE;
PRBool deleteInvalidDB = PR_FALSE;
PRBool exists;
PRInt64 fileSize;
summaryFile->Exists(&exists);
summaryFile->GetFileSize(&fileSize);
// if the old summary doesn't exist, we're creating a new one.
if ((!exists || !fileSize) && m_create)
newFile = PR_TRUE;
summaryFileExists = exists && fileSize > 0;
if (NS_SUCCEEDED(err))
{
GetDBFolderInfo(&folderInfo);
if (folderInfo == NULL)
if (!m_dbFolderInfo)
{
err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
@ -1051,12 +1190,11 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
}
// compare current version of db versus filed out version info.
PRUint32 version;
folderInfo->GetVersion(&version);
m_dbFolderInfo->GetVersion(&version);
if (GetCurVersion() != version)
err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
NS_RELEASE(folderInfo);
}
if (NS_FAILED(err) && !aLeaveInvalidDB)
if (NS_FAILED(err) && !m_leaveInvalidDB)
deleteInvalidDB = PR_TRUE;
}
else
@ -1068,7 +1206,7 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
if (deleteInvalidDB)
{
// this will make the db folder info release its ref to the mail db...
NS_IF_RELEASE(m_dbFolderInfo);
m_dbFolderInfo = nsnull;
ForceClosed();
if (err == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
summaryFile->Remove(PR_FALSE);
@ -1077,7 +1215,7 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
{
// if we couldn't open file, or we have a blank one, and we're supposed
// to upgrade, updgrade it.
if (newFile && !aLeaveInvalidDB) // caller is upgrading, and we have empty summary file,
if (newFile && !m_leaveInvalidDB) // caller is upgrading, and we have empty summary file,
{ // leave db around and open so caller can upgrade it.
err = NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
}
@ -1092,10 +1230,12 @@ NS_IMETHODIMP nsMsgDatabase::Open(nsILocalFile *aFolderName, PRBool aCreate, PRB
return (summaryFileExists) ? err : NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
}
// Open the MDB database synchronously. If successful, this routine
// will set up the m_mdbStore and m_mdbEnv of the database object
// so other database calls can work.
nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
/**
* Open the MDB database synchronously or async based on sync argument.
* If successful, this routine will set up the m_mdbStore and m_mdbEnv of
* the database object so other database calls can work.
*/
nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create, PRBool sync)
{
nsresult ret = NS_OK;
nsCOMPtr<nsIMdbFactory> mdbFactory;
@ -1105,7 +1245,6 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
ret = mdbFactory->MakeEnv(NULL, &m_mdbEnv);
if (NS_SUCCEEDED(ret))
{
nsIMdbThumb *thumb = nsnull;
struct stat st;
nsIMdbHeap* dbHeap = 0;
mdb_bool dbFrozen = mdbBool_kFalse; // not readonly, we want modifiable
@ -1114,8 +1253,11 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
m_mdbEnv->SetAutoClear(PR_TRUE);
m_dbName = dbName;
if (stat(dbName, &st))
{
ret = NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
else
}
// If m_thumb is set, we're asynchronously opening the db already.
else if (!m_thumb)
{
mdbOpenPolicy inOpenPolicy;
mdb_bool canOpen;
@ -1137,7 +1279,7 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
inOpenPolicy.mOpenPolicy_MaxLazy = 0;
ret = mdbFactory->OpenFileStore(m_mdbEnv, dbHeap,
oldFile, &inOpenPolicy, &thumb);
oldFile, &inOpenPolicy, getter_AddRefs(m_thumb));
}
else
ret = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
@ -1145,7 +1287,7 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
NS_RELEASE(oldFile); // always release our file ref, store has own
}
}
if (NS_SUCCEEDED(ret) && thumb)
if (NS_SUCCEEDED(ret) && m_thumb && sync)
{
mdb_count outTotal; // total somethings to do in operation
mdb_count outCurrent; // subportion of total completed so far
@ -1153,7 +1295,7 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
mdb_bool outBroken; // is operation irreparably dead and broken?
do
{
ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
ret = m_thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
if (ret != 0)
{// mork isn't really doing NS errors yet.
outDone = PR_TRUE;
@ -1165,13 +1307,14 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
// only 0 is a non-error return.
if (ret == 0 && outDone)
{
ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, m_thumb, &m_mdbStore);
if (ret == NS_OK)
ret = (m_mdbStore) ? InitExistingDB() : NS_ERROR_FAILURE;
}
#ifdef DEBUG_bienvenu1
DumpContents();
#endif
m_thumb = nsnull;
}
else if (create) // ### need error code saying why open file store failed
{
@ -1197,7 +1340,6 @@ nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
NS_RELEASE(newFile); // always release our file ref, store has own
}
}
NS_IF_RELEASE(thumb);
}
}
#ifdef DEBUG_David_Bienvenu
@ -1280,17 +1422,11 @@ if (m_mdbAllMsgHeadersTable)
// caller must Release result.
NS_IMETHODIMP nsMsgDatabase::GetDBFolderInfo(nsIDBFolderInfo **result)
{
*result = m_dbFolderInfo;
NS_ADDREF(*result = m_dbFolderInfo);
if (m_dbFolderInfo)
{
m_dbFolderInfo->AddRef();
return NS_OK;
}
else
{
NS_ASSERTION(PR_FALSE, "db must be corrupt");
return NS_ERROR_NULL_POINTER; // it's an error if we can't get this
}
NS_ERROR("db must be corrupt");
return NS_ERROR_NULL_POINTER; // it's an error if we can't get this
}
NS_IMETHODIMP nsMsgDatabase::Commit(nsMsgDBCommit commitType)

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

@ -3,23 +3,127 @@
* Test suite for msg database functions.
*/
load("../../mailnews/resources/messageGenerator.js");
var dbService;
var gTestFolder;
var gCurTestNum = 0;
const kNumTestMessages = 10;
const gTestArray =
[
function test_db_open() {
dbService = Components.classes["@mozilla.org/msgDatabase/msgDBService;1"]
.getService(Components.interfaces.nsIMsgDBService);
// Get the root folder
let root = gLocalIncomingServer.rootFolder;
root.createSubfolder("dbTest", null);
gTestFolder = root.getChildNamed("dbTest");
let db = dbService.openFolderDB(gTestFolder, true);
do_check_neq(db, null);
db.dBFolderInfo.highWater = 10;
db.Close(true);
db = dbService.openFolderDB(gTestFolder, true);
do_check_neq(db, null);
do_check_eq(db.dBFolderInfo.highWater, 10);
db.dBFolderInfo.onKeyAdded(15);
do_check_eq(db.dBFolderInfo.highWater, 15);
db.Close(true);
db.ForceClosed();
db = null;
doTest(++gCurTestNum);
},
function test_async_open() {
let messageGenerator = new MessageGenerator();
let localFolder = gTestFolder.QueryInterface(Ci.nsIMsgLocalMailFolder);
let gMessages = [];
// Add some messages to gTestFolder, close the db, and then test that opening
// asynchronously works.
for (let i = 0; i < kNumTestMessages; i++) {
let message = messageGenerator.makeMessage();
gMessages.push(message);
gTestFolder.addMessage(message.toMboxString());
}
gTestFolder.msgDatabase = null;
let db = dbService.asyncOpenFolderDB(gTestFolder, false);
openMore(db);
},
function test_invalid_db_async_open() {
// mark the summary invalid
gTestFolder.msgDatabase.summaryValid = false;
// clear the database so next time we have to reparse
gTestFolder.msgDatabase.ForceClosed();
let db = dbService.asyncOpenFolderDB(gTestFolder, false);
// this should eventually throw an error in one of the callbacks
openMoreAsync(db);
}
];
function doTest(test)
{
if (test <= gTestArray.length)
{
dump("Doing test " + test + "\n");
gCurTestNum = test;
var testFn = gTestArray[test-1];
// Set a limit of 10 seconds; if the notifications haven't arrived by then there's a problem.
do_timeout(10000, function(){
if (gCurTestNum == test)
do_throw("Notifications not received in 10000 ms for operation " + testFn.name +
", current status is " + gCurrStatus);
}
);
try {
testFn();
} catch(ex) {do_throw(ex);}
}
else
{
do_test_finished(); // for the one in run_test()
}
}
function run_test() {
loadLocalMailAccount();
// Get the root folder
var root = gLocalIncomingServer.rootFolder;
root.createSubfolder("dbTest", null);
var dbService = Components.classes["@mozilla.org/msgDatabase/msgDBService;1"]
.getService(Components.interfaces.nsIMsgDBService);
var folder = root.getChildNamed("dbTest");
var db = dbService.openFolderDB(folder, true);
do_check_neq(db, null);
db.dBFolderInfo.highWater = 10;
db.Close(true);
db = dbService.openFolderDB(folder, true);
do_check_neq(db, null);
do_check_eq(db.dBFolderInfo.highWater, 10);
db.dBFolderInfo.onKeyAdded(15);
do_check_eq(db.dBFolderInfo.highWater, 15);
do_test_pending();
doTest(1);
}
function openMore(db)
{
let done = dbService.openMore(db, 1);
dump("in openMore done = " + done + "\n");
if (!done)
do_timeout_function(0, openMore, null, [db]);
else {
// just check that we can get something out of the db.
do_check_eq(db.dBFolderInfo.numMessages, kNumTestMessages);
db.Close(true);
db.ForceClosed();
db = null;
doTest(++gCurTestNum);
}
}
function openMoreAsync(db)
{
let done = false;
try {
done = dbService.openMore(db, 100);
dump("in openMoreAsync done = " + done + "\n");
}
catch (ex) {
dump("got expected error opening corrupt db async\n");
db = null;
doTest(++gCurTestNum);
return;
};
if (!done)
do_timeout_function(0, openMoreAsync, null, [db]);
else
throw "Should have got an exception opening out of date db";
}