Before we were fetching the entire message body (including attachments) to get the preview text. This patch now offers the ability to fetch a small (128 byte) preview of the email. Also, since this ticket is about speeding up performance, I've introduced NonblockingBatch, which allows for multiple async operations to be executed in parallel easily. I've added its use in a few places to speed up operations, including one that was causing the lag in #3799, which is why this commit closes that ticket.
330 lines
12 KiB
Vala
330 lines
12 KiB
Vala
/* Copyright 2011 Yorba Foundation
|
|
*
|
|
* This software is licensed under the GNU Lesser General Public License
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
|
*/
|
|
|
|
public class Geary.Sqlite.MessageTable : Geary.Sqlite.Table {
|
|
// This *must* match the column order in the database
|
|
public enum Column {
|
|
ID,
|
|
FIELDS,
|
|
|
|
DATE_FIELD,
|
|
DATE_TIME_T,
|
|
|
|
FROM_FIELD,
|
|
SENDER,
|
|
REPLY_TO,
|
|
|
|
TO_FIELD,
|
|
CC,
|
|
BCC,
|
|
|
|
MESSAGE_ID,
|
|
IN_REPLY_TO,
|
|
REFERENCES,
|
|
|
|
SUBJECT,
|
|
|
|
HEADER,
|
|
|
|
BODY,
|
|
|
|
PREVIEW;
|
|
}
|
|
|
|
internal MessageTable(Geary.Sqlite.Database gdb, SQLHeavy.Table table) {
|
|
base (gdb, table);
|
|
}
|
|
|
|
public async int64 create_async(Transaction? transaction, MessageRow row,
|
|
Cancellable? cancellable) throws Error {
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.create_async",
|
|
cancellable);
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"INSERT INTO MessageTable "
|
|
+ "(fields, date_field, date_time_t, from_field, sender, reply_to, to_field, cc, bcc, "
|
|
+ "message_id, in_reply_to, reference_ids, subject, header, body, preview) "
|
|
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
query.bind_int(0, row.fields);
|
|
query.bind_string(1, row.date);
|
|
query.bind_int64(2, row.date_time_t);
|
|
query.bind_string(3, row.from);
|
|
query.bind_string(4, row.sender);
|
|
query.bind_string(5, row.reply_to);
|
|
query.bind_string(6, row.to);
|
|
query.bind_string(7, row.cc);
|
|
query.bind_string(8, row.bcc);
|
|
query.bind_string(9, row.message_id);
|
|
query.bind_string(10, row.in_reply_to);
|
|
query.bind_string(11, row.references);
|
|
query.bind_string(12, row.subject);
|
|
query.bind_string(13, row.header);
|
|
query.bind_string(14, row.body);
|
|
query.bind_string(15, row.preview);
|
|
|
|
int64 id = yield query.execute_insert_async(cancellable);
|
|
locked.set_commit_required();
|
|
|
|
yield release_lock_async(transaction, locked, cancellable);
|
|
|
|
return id;
|
|
}
|
|
|
|
// TODO: This could be improved greatly, in particular making this a single SQL command or
|
|
// parallelizing the commands.
|
|
public async void merge_async(Transaction? transaction, MessageRow row,
|
|
Cancellable? cancellable) throws Error {
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.merge_async",
|
|
cancellable);
|
|
|
|
// merge the valid fields in the row
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"UPDATE MessageTable SET fields = fields | ? WHERE id=?");
|
|
query.bind_int(0, row.fields);
|
|
query.bind_int64(1, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
locked.set_commit_required();
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.DATE)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET date_field=?, date_time_t=? WHERE id=?");
|
|
query.bind_string(0, row.date);
|
|
query.bind_int64(1, row.date_time_t);
|
|
query.bind_int64(2, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.ORIGINATORS)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET from_field=?, sender=?, reply_to=? WHERE id=?");
|
|
query.bind_string(0, row.from);
|
|
query.bind_string(1, row.sender);
|
|
query.bind_string(2, row.reply_to);
|
|
query.bind_int64(3, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.RECEIVERS)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET to_field=?, cc=?, bcc=? WHERE id=?");
|
|
query.bind_string(0, row.to);
|
|
query.bind_string(1, row.cc);
|
|
query.bind_string(2, row.bcc);
|
|
query.bind_int64(3, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.REFERENCES)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET message_id=?, in_reply_to=?, reference_ids=? WHERE id=?");
|
|
query.bind_string(0, row.message_id);
|
|
query.bind_string(1, row.in_reply_to);
|
|
query.bind_string(2, row.references);
|
|
query.bind_int64(3, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.SUBJECT)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET subject=? WHERE id=?");
|
|
query.bind_string(0, row.subject);
|
|
query.bind_int64(1, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.HEADER)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET header=? WHERE id=?");
|
|
query.bind_string(0, row.header);
|
|
query.bind_int64(1, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.BODY)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET body=? WHERE id=?");
|
|
query.bind_string(0, row.body);
|
|
query.bind_int64(1, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.PREVIEW)) {
|
|
query = locked.prepare(
|
|
"UPDATE MessageTable SET preview=? WHERE id=?");
|
|
query.bind_string(0, row.preview);
|
|
query.bind_int64(1, row.id);
|
|
|
|
yield query.execute_async(cancellable);
|
|
}
|
|
|
|
// only commit if internally atomic
|
|
if (transaction == null)
|
|
yield locked.commit_async(cancellable);
|
|
|
|
yield release_lock_async(transaction, locked, cancellable);
|
|
}
|
|
|
|
public async Gee.List<MessageRow>? list_by_message_id_async(Transaction? transaction,
|
|
Geary.RFC822.MessageID message_id, Geary.Email.Field fields, Cancellable? cancellable)
|
|
throws Error {
|
|
assert(fields != Geary.Email.Field.NONE);
|
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.list_by_message_id_async",
|
|
cancellable);
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"SELECT %s FROM MessageTable WHERE message_id=?".printf(fields_to_columns(fields)));
|
|
query.bind_string(0, message_id.value);
|
|
|
|
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
|
if (results.finished)
|
|
return null;
|
|
|
|
Gee.List<MessageRow> list = new Gee.ArrayList<MessageRow>();
|
|
do {
|
|
list.add(new MessageRow.from_query_result(this, fields, results));
|
|
yield results.next_async(cancellable);
|
|
} while (!results.finished);
|
|
|
|
return (list.size > 0) ? list : null;
|
|
}
|
|
|
|
public async MessageRow? fetch_async(Transaction? transaction, int64 id,
|
|
Geary.Email.Field requested_fields, Cancellable? cancellable = null) throws Error {
|
|
assert(requested_fields != Geary.Email.Field.NONE);
|
|
// PROPERTIES are handled by the appropriate PropertiesTable
|
|
assert(requested_fields != Geary.Email.Field.PROPERTIES);
|
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.fetch_async",
|
|
cancellable);
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"SELECT %s FROM MessageTable WHERE id=?".printf(fields_to_columns(requested_fields)));
|
|
query.bind_int64(0, id);
|
|
|
|
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
|
if (results.finished)
|
|
return null;
|
|
|
|
MessageRow row = new MessageRow.from_query_result(this, requested_fields, results);
|
|
|
|
return row;
|
|
}
|
|
|
|
public async bool fetch_fields_async(Transaction? transaction, int64 id,
|
|
out Geary.Email.Field available_fields, Cancellable? cancellable) throws Error {
|
|
available_fields = Geary.Email.Field.NONE;
|
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.fetch_fields_async",
|
|
cancellable);
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"SELECT fields FROM MessageTable WHERE id=?");
|
|
query.bind_int64(0, id);
|
|
|
|
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
|
if (result.finished)
|
|
return false;
|
|
|
|
available_fields = (Geary.Email.Field) result.fetch_int(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static string fields_to_columns(Geary.Email.Field fields) {
|
|
StringBuilder builder = new StringBuilder("id, fields");
|
|
foreach (Geary.Email.Field field in Geary.Email.Field.all()) {
|
|
string? append = null;
|
|
if ((fields & field) != 0) {
|
|
switch (field) {
|
|
case Geary.Email.Field.DATE:
|
|
append = "date_field, date_time_t";
|
|
break;
|
|
|
|
case Geary.Email.Field.ORIGINATORS:
|
|
append = "from_field, sender, reply_to";
|
|
break;
|
|
|
|
case Geary.Email.Field.RECEIVERS:
|
|
append = "to_field, cc, bcc";
|
|
break;
|
|
|
|
case Geary.Email.Field.REFERENCES:
|
|
append = "message_id, in_reply_to, reference_ids";
|
|
break;
|
|
|
|
case Geary.Email.Field.SUBJECT:
|
|
append = "subject";
|
|
break;
|
|
|
|
case Geary.Email.Field.HEADER:
|
|
append = "header";
|
|
break;
|
|
|
|
case Geary.Email.Field.BODY:
|
|
append = "body";
|
|
break;
|
|
|
|
case Geary.Email.Field.PREVIEW:
|
|
append = "preview";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (append != null) {
|
|
builder.append(", ");
|
|
builder.append(append);
|
|
}
|
|
}
|
|
|
|
return builder.str;
|
|
}
|
|
|
|
public async int search_message_id_count_async(Transaction? transaction,
|
|
Geary.RFC822.MessageID message_id, Cancellable? cancellable) throws Error {
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.search_message_id_count",
|
|
cancellable);
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"SELECT COUNT(*) FROM MessageTable WHERE message_id=?");
|
|
query.bind_string(0, message_id.value);
|
|
|
|
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
|
|
|
return (result.finished) ? 0 : result.fetch_int(0);
|
|
}
|
|
|
|
public async Gee.List<int64?>? search_message_id_async(Transaction? transaction,
|
|
Geary.RFC822.MessageID message_id, Cancellable? cancellable) throws Error {
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.search_message_id_async",
|
|
cancellable);
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
|
"SELECT id FROM MessageTable WHERE message_id=?");
|
|
query.bind_string(0, message_id.value);
|
|
|
|
SQLHeavy.QueryResult result = yield query.execute_async(cancellable);
|
|
if (result.finished)
|
|
return null;
|
|
|
|
Gee.List<int64?> list = new Gee.ArrayList<int64?>();
|
|
do {
|
|
list.add(result.fetch_int64(0));
|
|
yield result.next_async(cancellable);
|
|
} while (!result.finished);
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|