2011-06-16 16:27:08 -07:00
|
|
|
/* 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,
|
2011-06-23 19:07:04 -07:00
|
|
|
FIELDS,
|
2011-06-16 16:27:08 -07:00
|
|
|
|
|
|
|
|
DATE_FIELD,
|
2011-07-15 13:39:02 -07:00
|
|
|
DATE_TIME_T,
|
2011-06-16 16:27:08 -07:00
|
|
|
|
|
|
|
|
FROM_FIELD,
|
|
|
|
|
SENDER,
|
|
|
|
|
REPLY_TO,
|
|
|
|
|
|
|
|
|
|
TO_FIELD,
|
|
|
|
|
CC,
|
|
|
|
|
BCC,
|
|
|
|
|
|
|
|
|
|
MESSAGE_ID,
|
|
|
|
|
IN_REPLY_TO,
|
2011-10-17 18:56:17 -07:00
|
|
|
REFERENCES,
|
2011-06-16 16:27:08 -07:00
|
|
|
|
|
|
|
|
SUBJECT,
|
|
|
|
|
|
|
|
|
|
HEADER,
|
|
|
|
|
|
|
|
|
|
BODY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal MessageTable(Geary.Sqlite.Database gdb, SQLHeavy.Table table) {
|
|
|
|
|
base (gdb, table);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
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(
|
2011-06-16 16:27:08 -07:00
|
|
|
"INSERT INTO MessageTable "
|
2011-06-23 19:07:04 -07:00
|
|
|
+ "(fields, date_field, date_time_t, from_field, sender, reply_to, to_field, cc, bcc, "
|
2011-10-17 18:56:17 -07:00
|
|
|
+ "message_id, in_reply_to, reference_ids, subject, header, body) "
|
|
|
|
|
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
2011-06-23 19:07:04 -07:00
|
|
|
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);
|
2011-10-17 18:56:17 -07:00
|
|
|
query.bind_string(11, row.references);
|
|
|
|
|
query.bind_string(12, row.subject);
|
|
|
|
|
query.bind_string(13, row.header);
|
|
|
|
|
query.bind_string(14, row.body);
|
2011-06-16 16:27:08 -07:00
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
int64 id = yield query.execute_insert_async(cancellable);
|
|
|
|
|
locked.set_commit_required();
|
|
|
|
|
|
|
|
|
|
yield release_lock_async(transaction, locked, cancellable);
|
|
|
|
|
|
|
|
|
|
return id;
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
|
|
|
|
|
2011-11-14 12:09:52 -08:00
|
|
|
// TODO: This could be improved greatly, in particular making this a single SQL command or
|
|
|
|
|
// parallelizing the commands.
|
2011-10-12 16:46:23 -07:00
|
|
|
public async void merge_async(Transaction? transaction, MessageRow row,
|
|
|
|
|
Cancellable? cancellable) throws Error {
|
|
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.merge_async",
|
|
|
|
|
cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
|
|
|
|
|
// merge the valid fields in the row
|
2011-10-12 16:46:23 -07:00
|
|
|
SQLHeavy.Query query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"UPDATE MessageTable SET fields = fields | ? WHERE id=?");
|
|
|
|
|
query.bind_int(0, row.fields);
|
|
|
|
|
query.bind_int64(1, row.id);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-10-12 16:46:23 -07:00
|
|
|
locked.set_commit_required();
|
2011-06-23 19:07:04 -07:00
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.DATE)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"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);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.ORIGINATORS)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"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);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.RECEIVERS)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"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);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.REFERENCES)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-10-21 17:04:33 -07:00
|
|
|
"UPDATE MessageTable SET message_id=?, in_reply_to=?, reference_ids=? WHERE id=?");
|
2011-06-23 19:07:04 -07:00
|
|
|
query.bind_string(0, row.message_id);
|
|
|
|
|
query.bind_string(1, row.in_reply_to);
|
2011-10-17 18:56:17 -07:00
|
|
|
query.bind_string(2, row.references);
|
|
|
|
|
query.bind_int64(3, row.id);
|
2011-06-23 19:07:04 -07:00
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.SUBJECT)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"UPDATE MessageTable SET subject=? WHERE id=?");
|
|
|
|
|
query.bind_string(0, row.subject);
|
|
|
|
|
query.bind_int64(1, row.id);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.HEADER)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"UPDATE MessageTable SET header=? WHERE id=?");
|
|
|
|
|
query.bind_string(0, row.header);
|
|
|
|
|
query.bind_int64(1, row.id);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 13:39:02 -07:00
|
|
|
if (row.fields.is_any_set(Geary.Email.Field.BODY)) {
|
2011-10-12 16:46:23 -07:00
|
|
|
query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"UPDATE MessageTable SET body=? WHERE id=?");
|
|
|
|
|
query.bind_string(0, row.body);
|
|
|
|
|
query.bind_int64(1, row.id);
|
|
|
|
|
|
2011-07-18 12:26:43 -07:00
|
|
|
yield query.execute_async(cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
// only commit if internally atomic
|
|
|
|
|
if (transaction == null)
|
|
|
|
|
yield locked.commit_async(cancellable);
|
|
|
|
|
|
|
|
|
|
yield release_lock_async(transaction, locked, cancellable);
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
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 {
|
2011-06-16 16:27:08 -07:00
|
|
|
assert(fields != Geary.Email.Field.NONE);
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.list_by_message_id_async",
|
|
|
|
|
cancellable);
|
|
|
|
|
|
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"SELECT %s FROM MessageTable WHERE message_id=?".printf(fields_to_columns(fields)));
|
2011-06-16 16:27:08 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
public async MessageRow? fetch_async(Transaction? transaction, int64 id,
|
|
|
|
|
Geary.Email.Field requested_fields, Cancellable? cancellable = null) throws Error {
|
2011-06-23 19:07:04 -07:00
|
|
|
assert(requested_fields != Geary.Email.Field.NONE);
|
2011-11-10 13:20:48 -08:00
|
|
|
// PROPERTIES are handled by the appropriate PropertiesTable
|
|
|
|
|
assert(requested_fields != Geary.Email.Field.PROPERTIES);
|
2011-06-16 16:27:08 -07:00
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.fetch_async",
|
|
|
|
|
cancellable);
|
|
|
|
|
|
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"SELECT %s FROM MessageTable WHERE id=?".printf(fields_to_columns(requested_fields)));
|
2011-06-16 16:27:08 -07:00
|
|
|
query.bind_int64(0, id);
|
|
|
|
|
|
|
|
|
|
SQLHeavy.QueryResult results = yield query.execute_async(cancellable);
|
|
|
|
|
if (results.finished)
|
|
|
|
|
return null;
|
|
|
|
|
|
2011-06-23 19:07:04 -07:00
|
|
|
MessageRow row = new MessageRow.from_query_result(this, requested_fields, results);
|
2011-06-16 16:27:08 -07:00
|
|
|
|
|
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
public async bool fetch_fields_async(Transaction? transaction, int64 id,
|
|
|
|
|
out Geary.Email.Field available_fields, Cancellable? cancellable) throws Error {
|
2011-06-23 19:07:04 -07:00
|
|
|
available_fields = Geary.Email.Field.NONE;
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
Transaction locked = yield obtain_lock_async(transaction, "MessageTable.fetch_fields_async",
|
|
|
|
|
cancellable);
|
|
|
|
|
|
|
|
|
|
SQLHeavy.Query query = locked.prepare(
|
2011-06-23 19:07:04 -07:00
|
|
|
"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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-16 16:27:08 -07:00
|
|
|
private static string fields_to_columns(Geary.Email.Field fields) {
|
2011-06-23 19:07:04 -07:00
|
|
|
StringBuilder builder = new StringBuilder("id, fields");
|
2011-06-16 16:27:08 -07:00
|
|
|
foreach (Geary.Email.Field field in Geary.Email.Field.all()) {
|
|
|
|
|
string? append = null;
|
2011-06-21 17:48:40 -07:00
|
|
|
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:
|
2011-10-17 18:56:17 -07:00
|
|
|
append = "message_id, in_reply_to, reference_ids";
|
2011-06-21 17:48:40 -07:00
|
|
|
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;
|
|
|
|
|
}
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (append != null) {
|
|
|
|
|
if (!String.is_empty(builder.str))
|
|
|
|
|
builder.append(", ");
|
|
|
|
|
|
|
|
|
|
builder.append(append);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return builder.str;
|
|
|
|
|
}
|
2011-06-23 19:07:04 -07:00
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
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(
|
2011-06-23 19:07:04 -07:00
|
|
|
"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);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:46:23 -07:00
|
|
|
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(
|
2011-06-23 19:07:04 -07:00
|
|
|
"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;
|
|
|
|
|
}
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
|
|
|
|
|