Transactions added to database code: Closes #4235

The entire database module now uses Transactions in order to guarantee atomicity of all
operations.  However, due to limitations in SQLHeavy and its implementation of async, we
can't use it (and SQLite's) transactional models.  This patch introduces a rather hamhanded
transactional model where the entire database is locked for each request using a
NonblockingMutex.  This is the safest approach, but certainly not an optimal one.  When
SQLHeavy's code is in place, hopefully we can rip this out with a minimum of fuss.
This commit is contained in:
Jim Nelson 2011-10-12 16:46:23 -07:00
parent 03d63ecc1a
commit 7e8edcb355
19 changed files with 547 additions and 219 deletions

View file

@ -40,18 +40,20 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
return Geary.Email.Field.NONE;
}
private async int64 fetch_id_async(Geary.FolderPath path, Cancellable? cancellable = null)
throws Error {
FolderRow? row = yield folder_table.fetch_descend_async(path.as_list(), cancellable);
private async int64 fetch_id_async(Transaction? transaction, Geary.FolderPath path,
Cancellable? cancellable = null) throws Error {
FolderRow? row = yield folder_table.fetch_descend_async(transaction, path.as_list(),
cancellable);
if (row == null)
throw new EngineError.NOT_FOUND("Cannot find local path to %s", path.to_string());
return row.id;
}
private async int64 fetch_parent_id_async(Geary.FolderPath path, Cancellable? cancellable = null)
throws Error {
return path.is_root() ? Row.INVALID_ID : yield fetch_id_async(path.get_parent(), cancellable);
private async int64 fetch_parent_id_async(Transaction? transaction, Geary.FolderPath path,
Cancellable? cancellable = null) throws Error {
return path.is_root() ? Row.INVALID_ID : yield fetch_id_async(transaction, path.get_parent(),
cancellable);
}
public async void clone_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
@ -63,14 +65,19 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
// properties *must* be available to perform a clone
assert(imap_folder_properties != null);
int64 parent_id = yield fetch_parent_id_async(folder.get_path(), cancellable);
Transaction transaction = yield db.begin_transaction_async("Account.clone_folder_async",
cancellable);
int64 folder_id = yield folder_table.create_async(new FolderRow(folder_table,
int64 parent_id = yield fetch_parent_id_async(transaction, folder.get_path(), cancellable);
int64 folder_id = yield folder_table.create_async(transaction, new FolderRow(folder_table,
imap_folder.get_path().basename, parent_id), cancellable);
yield folder_properties_table.create_async(
yield folder_properties_table.create_async(transaction,
new ImapFolderPropertiesRow.from_imap_properties(folder_properties_table, folder_id,
imap_folder_properties));
imap_folder_properties), cancellable);
yield transaction.commit_async(cancellable);
}
public async void update_folder_async(Geary.Folder folder, Cancellable? cancellable = null)
@ -82,32 +89,40 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
// properties *must* be available
assert(imap_folder_properties != null);
int64 parent_id = yield fetch_parent_id_async(folder.get_path(), cancellable);
FolderRow? row = yield folder_table.fetch_async(parent_id, folder.get_path().basename,
Transaction transaction = yield db.begin_transaction_async("Account.update_folder_async",
cancellable);
int64 parent_id = yield fetch_parent_id_async(transaction, folder.get_path(), cancellable);
FolderRow? row = yield folder_table.fetch_async(transaction, parent_id,
folder.get_path().basename, cancellable);
if (row == null)
throw new EngineError.NOT_FOUND("Can't find in local store %s", folder.get_path().to_string());
yield folder_properties_table.update_async(row.id,
yield folder_properties_table.update_async(transaction, row.id,
new ImapFolderPropertiesRow.from_imap_properties(folder_properties_table, row.id,
imap_folder_properties));
imap_folder_properties), cancellable);
FolderReference? folder_ref = folder_refs.get(folder.get_path());
if (folder_ref != null)
((Geary.Sqlite.Folder) folder_ref.get_reference()).update_properties(imap_folder_properties);
yield transaction.commit_async(cancellable);
}
public override async Gee.Collection<Geary.Folder> list_folders_async(Geary.FolderPath? parent,
Cancellable? cancellable = null) throws Error {
Transaction transaction = yield db.begin_transaction_async("Account.list_folders_async",
cancellable);
int64 parent_id = (parent != null)
? yield fetch_id_async(parent, cancellable)
? yield fetch_id_async(transaction, parent, cancellable)
: Row.INVALID_ID;
if (parent != null)
assert(parent_id != Row.INVALID_ID);
Gee.List<FolderRow> rows = yield folder_table.list_async(parent_id, cancellable);
Gee.List<FolderRow> rows = yield folder_table.list_async(transaction, parent_id, cancellable);
if (rows.size == 0) {
throw new EngineError.NOT_FOUND("No local folders in %s",
(parent != null) ? parent.get_fullpath() : "root");
@ -115,8 +130,8 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
Gee.Collection<Geary.Folder> folders = new Gee.ArrayList<Geary.Sqlite.Folder>();
foreach (FolderRow row in rows) {
ImapFolderPropertiesRow? properties = yield folder_properties_table.fetch_async(row.id,
cancellable);
ImapFolderPropertiesRow? properties = yield folder_properties_table.fetch_async(
transaction, row.id, cancellable);
Geary.FolderPath path = (parent != null)
? parent.get_child(row.name)
@ -136,7 +151,7 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
public override async bool folder_exists_async(Geary.FolderPath path,
Cancellable? cancellable = null) throws Error {
try {
int64 id = yield fetch_id_async(path, cancellable);
int64 id = yield fetch_id_async(null, path, cancellable);
return (id != Row.INVALID_ID);
} catch (EngineError err) {
@ -154,14 +169,18 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
if (folder != null)
return folder;
Transaction transaction = yield db.begin_transaction_async("Account.fetch_folder_async",
cancellable);
// locate in database
FolderRow? row = yield folder_table.fetch_descend_async(path.as_list(), cancellable);
FolderRow? row = yield folder_table.fetch_descend_async(transaction, path.as_list(),
cancellable);
if (row == null)
throw new EngineError.NOT_FOUND("%s not found in local database", path.to_string());
// fetch it's IMAP-specific properties
ImapFolderPropertiesRow? properties = yield folder_properties_table.fetch_async(row.id,
cancellable);
ImapFolderPropertiesRow? properties = yield folder_properties_table.fetch_async(
transaction, row.id, cancellable);
return create_sqlite_folder(row,
(properties != null) ? properties.get_imap_folder_properties() : null, path);
@ -169,7 +188,7 @@ public class Geary.Sqlite.Account : Geary.AbstractAccount, Geary.LocalAccount {
public async bool has_message_id_async(Geary.RFC822.MessageID message_id, out int count,
Cancellable? cancellable = null) throws Error {
count = yield message_table.search_message_id_count_async(message_id);
count = yield message_table.search_message_id_count_async(null, message_id, cancellable);
return (count > 0);
}