mirror of
https://github.com/moparisthebest/k-9
synced 2025-02-19 20:21:45 -05:00
Extracting Database Setup Schema definition form LocalStore.
This commit is contained in:
parent
91ef5fa816
commit
eced036d69
@ -20,14 +20,12 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.R;
|
|
||||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||||
import com.fsck.k9.helper.StringUtils;
|
import com.fsck.k9.helper.StringUtils;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
@ -43,7 +41,6 @@ import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
|||||||
import com.fsck.k9.mail.store.LockableDatabase.SchemaDefinition;
|
import com.fsck.k9.mail.store.LockableDatabase.SchemaDefinition;
|
||||||
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
||||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
||||||
import com.fsck.k9.provider.AttachmentProvider;
|
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||||
import com.fsck.k9.search.LocalSearch;
|
import com.fsck.k9.search.LocalSearch;
|
||||||
@ -157,7 +154,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
*/
|
*/
|
||||||
public LocalStore(final Account account, final Application application) throws MessagingException {
|
public LocalStore(final Account account, final Application application) throws MessagingException {
|
||||||
super(account);
|
super(account);
|
||||||
database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition());
|
database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this));
|
||||||
|
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
mContentResolver = application.getContentResolver();
|
mContentResolver = application.getContentResolver();
|
||||||
@ -175,572 +172,6 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
return Preferences.getPreferences(mApplication).getPreferences();
|
return Preferences.getPreferences(mApplication).getPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
|
|
||||||
@Override
|
|
||||||
public int getVersion() {
|
|
||||||
return DB_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doDbUpgrade(final SQLiteDatabase db) {
|
|
||||||
try {
|
|
||||||
upgradeDatabase(db);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Exception while upgrading database. Resetting the DB to v0", e);
|
|
||||||
db.setVersion(0);
|
|
||||||
upgradeDatabase(db);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void upgradeDatabase(final SQLiteDatabase db) {
|
|
||||||
Log.i(K9.LOG_TAG, String.format(Locale.US, "Upgrading database from version %d to version %d",
|
|
||||||
db.getVersion(), DB_VERSION));
|
|
||||||
|
|
||||||
AttachmentProvider.clear(mApplication);
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
try {
|
|
||||||
// schema version 29 was when we moved to incremental updates
|
|
||||||
// in the case of a new db or a < v29 db, we blow away and start from scratch
|
|
||||||
if (db.getVersion() < 29) {
|
|
||||||
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS folders");
|
|
||||||
db.execSQL("CREATE TABLE folders (id INTEGER PRIMARY KEY, name TEXT, "
|
|
||||||
+ "last_updated INTEGER, unread_count INTEGER, visible_limit INTEGER, status TEXT, "
|
|
||||||
+ "push_state TEXT, last_pushed INTEGER, flagged_count INTEGER default 0, "
|
|
||||||
+ "integrate INTEGER, top_group INTEGER, poll_class TEXT, push_class TEXT, display_class TEXT, notify_class TEXT"
|
|
||||||
+ ")");
|
|
||||||
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS folder_name ON folders (name)");
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS messages");
|
|
||||||
db.execSQL("CREATE TABLE messages (" +
|
|
||||||
"id INTEGER PRIMARY KEY, " +
|
|
||||||
"deleted INTEGER default 0, " +
|
|
||||||
"folder_id INTEGER, " +
|
|
||||||
"uid TEXT, " +
|
|
||||||
"subject TEXT, " +
|
|
||||||
"date INTEGER, " +
|
|
||||||
"flags TEXT, " +
|
|
||||||
"sender_list TEXT, " +
|
|
||||||
"to_list TEXT, " +
|
|
||||||
"cc_list TEXT, " +
|
|
||||||
"bcc_list TEXT, " +
|
|
||||||
"reply_to_list TEXT, " +
|
|
||||||
"html_content TEXT, " +
|
|
||||||
"text_content TEXT, " +
|
|
||||||
"attachment_count INTEGER, " +
|
|
||||||
"internal_date INTEGER, " +
|
|
||||||
"message_id TEXT, " +
|
|
||||||
"preview TEXT, " +
|
|
||||||
"mime_type TEXT, "+
|
|
||||||
"normalized_subject_hash INTEGER, " +
|
|
||||||
"empty INTEGER, " +
|
|
||||||
"read INTEGER default 0, " +
|
|
||||||
"flagged INTEGER default 0, " +
|
|
||||||
"answered INTEGER default 0, " +
|
|
||||||
"forwarded INTEGER default 0" +
|
|
||||||
")");
|
|
||||||
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS headers");
|
|
||||||
db.execSQL("CREATE TABLE headers (id INTEGER PRIMARY KEY, message_id INTEGER, name TEXT, value TEXT)");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS header_folder ON headers (message_id)");
|
|
||||||
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_uid ON messages (uid, folder_id)");
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_folder_id");
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_read");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_flagged");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_composite");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS threads");
|
|
||||||
db.execSQL("CREATE TABLE threads (" +
|
|
||||||
"id INTEGER PRIMARY KEY, " +
|
|
||||||
"message_id INTEGER, " +
|
|
||||||
"root INTEGER, " +
|
|
||||||
"parent INTEGER" +
|
|
||||||
")");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
|
||||||
|
|
||||||
db.execSQL("DROP TRIGGER IF EXISTS set_thread_root");
|
|
||||||
db.execSQL("CREATE TRIGGER set_thread_root " +
|
|
||||||
"AFTER INSERT ON threads " +
|
|
||||||
"BEGIN " +
|
|
||||||
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
|
||||||
"END");
|
|
||||||
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS attachments");
|
|
||||||
db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
|
|
||||||
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
|
|
||||||
+ "mime_type TEXT, content_id TEXT, content_disposition TEXT)");
|
|
||||||
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
|
||||||
db.execSQL("CREATE TABLE pending_commands " +
|
|
||||||
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
|
|
||||||
|
|
||||||
db.execSQL("DROP TRIGGER IF EXISTS delete_folder");
|
|
||||||
db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
|
|
||||||
|
|
||||||
db.execSQL("DROP TRIGGER IF EXISTS delete_message");
|
|
||||||
db.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; "
|
|
||||||
+ "DELETE FROM headers where old.id = message_id; END;");
|
|
||||||
} else {
|
|
||||||
// in the case that we're starting out at 29 or newer, run all the needed updates
|
|
||||||
|
|
||||||
if (db.getVersion() < 30) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE messages ADD deleted INTEGER default 0");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.toString().startsWith("duplicate column name: deleted")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 31) {
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 32) {
|
|
||||||
db.execSQL("UPDATE messages SET deleted = 1 WHERE flags LIKE '%DELETED%'");
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 33) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE messages ADD preview TEXT");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.toString().startsWith("duplicate column name: preview")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 34) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE folders ADD flagged_count INTEGER default 0");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.getMessage().startsWith("duplicate column name: flagged_count")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 35) {
|
|
||||||
try {
|
|
||||||
db.execSQL("update messages set flags = replace(flags, 'X_NO_SEEN_INFO', 'X_BAD_FLAG')");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Unable to get rid of obsolete flag X_NO_SEEN_INFO", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 36) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE attachments ADD content_id TEXT");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Unable to add content_id column to attachments");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 37) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE attachments ADD content_disposition TEXT");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Unable to add content_disposition column to attachments");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database version 38 is solely to prune cached attachments now that we clear them better
|
|
||||||
if (db.getVersion() < 39) {
|
|
||||||
try {
|
|
||||||
db.execSQL("DELETE FROM headers WHERE id in (SELECT headers.id FROM headers LEFT JOIN messages ON headers.message_id = messages.id WHERE messages.id IS NULL)");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Unable to remove extra header data from the database");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// V40: Store the MIME type for a message.
|
|
||||||
if (db.getVersion() < 40) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE messages ADD mime_type TEXT");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Unable to add mime_type column to messages");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db.getVersion() < 41) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE folders ADD integrate INTEGER");
|
|
||||||
db.execSQL("ALTER TABLE folders ADD top_group INTEGER");
|
|
||||||
db.execSQL("ALTER TABLE folders ADD poll_class TEXT");
|
|
||||||
db.execSQL("ALTER TABLE folders ADD push_class TEXT");
|
|
||||||
db.execSQL("ALTER TABLE folders ADD display_class TEXT");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cursor cursor = null;
|
|
||||||
try {
|
|
||||||
SharedPreferences prefs = getPreferences();
|
|
||||||
cursor = db.rawQuery("SELECT id, name FROM folders", null);
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
try {
|
|
||||||
int id = cursor.getInt(0);
|
|
||||||
String name = cursor.getString(1);
|
|
||||||
update41Metadata(db, prefs, id, name);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(K9.LOG_TAG, " error trying to ugpgrade a folder class", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Exception while upgrading database to v41. folder classes may have vanished", e);
|
|
||||||
} finally {
|
|
||||||
Utility.closeQuietly(cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() == 41) {
|
|
||||||
try {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
SharedPreferences.Editor editor = getPreferences().edit();
|
|
||||||
|
|
||||||
List <? extends Folder > folders = getPersonalNamespaces(true);
|
|
||||||
for (Folder folder : folders) {
|
|
||||||
if (folder instanceof LocalFolder) {
|
|
||||||
LocalFolder lFolder = (LocalFolder)folder;
|
|
||||||
lFolder.save(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.commit();
|
|
||||||
long endTime = System.currentTimeMillis();
|
|
||||||
Log.i(K9.LOG_TAG, "Putting folder preferences for " + folders.size() + " folders back into Preferences took " + (endTime - startTime) + " ms");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Could not replace Preferences in upgrade from DB_VERSION 41", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 43) {
|
|
||||||
try {
|
|
||||||
// If folder "OUTBOX" (old, v3.800 - v3.802) exists, rename it to
|
|
||||||
// "K9MAIL_INTERNAL_OUTBOX" (new)
|
|
||||||
LocalFolder oldOutbox = new LocalFolder(LocalStore.this, "OUTBOX");
|
|
||||||
if (oldOutbox.exists()) {
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
cv.put("name", Account.OUTBOX);
|
|
||||||
db.update("folders", cv, "name = ?", new String[] { "OUTBOX" });
|
|
||||||
Log.i(K9.LOG_TAG, "Renamed folder OUTBOX to " + Account.OUTBOX);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if old (pre v3.800) localized outbox folder exists
|
|
||||||
String localizedOutbox = K9.app.getString(R.string.special_mailbox_name_outbox);
|
|
||||||
LocalFolder obsoleteOutbox = new LocalFolder(LocalStore.this, localizedOutbox);
|
|
||||||
if (obsoleteOutbox.exists()) {
|
|
||||||
// Get all messages from the localized outbox ...
|
|
||||||
Message[] messages = obsoleteOutbox.getMessages(null, false);
|
|
||||||
|
|
||||||
if (messages.length > 0) {
|
|
||||||
// ... and move them to the drafts folder (we don't want to
|
|
||||||
// surprise the user by sending potentially very old messages)
|
|
||||||
LocalFolder drafts = new LocalFolder(LocalStore.this, mAccount.getDraftsFolderName());
|
|
||||||
obsoleteOutbox.moveMessages(messages, drafts);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now get rid of the localized outbox
|
|
||||||
obsoleteOutbox.delete();
|
|
||||||
obsoleteOutbox.delete(true);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(K9.LOG_TAG, "Error trying to fix the outbox folders", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 44) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE messages ADD thread_root INTEGER");
|
|
||||||
db.execSQL("ALTER TABLE messages ADD thread_parent INTEGER");
|
|
||||||
db.execSQL("ALTER TABLE messages ADD normalized_subject_hash INTEGER");
|
|
||||||
db.execSQL("ALTER TABLE messages ADD empty INTEGER");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 45) {
|
|
||||||
try {
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_root ON messages (thread_root)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_parent ON messages (thread_parent)");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 46) {
|
|
||||||
db.execSQL("ALTER TABLE messages ADD read INTEGER default 0");
|
|
||||||
db.execSQL("ALTER TABLE messages ADD flagged INTEGER default 0");
|
|
||||||
db.execSQL("ALTER TABLE messages ADD answered INTEGER default 0");
|
|
||||||
db.execSQL("ALTER TABLE messages ADD forwarded INTEGER default 0");
|
|
||||||
|
|
||||||
String[] projection = { "id", "flags" };
|
|
||||||
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
List<Flag> extraFlags = new ArrayList<Flag>();
|
|
||||||
|
|
||||||
Cursor cursor = db.query("messages", projection, null, null, null, null, null);
|
|
||||||
try {
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
long id = cursor.getLong(0);
|
|
||||||
String flagList = cursor.getString(1);
|
|
||||||
|
|
||||||
boolean read = false;
|
|
||||||
boolean flagged = false;
|
|
||||||
boolean answered = false;
|
|
||||||
boolean forwarded = false;
|
|
||||||
|
|
||||||
if (flagList != null && flagList.length() > 0) {
|
|
||||||
String[] flags = flagList.split(",");
|
|
||||||
|
|
||||||
for (String flagStr : flags) {
|
|
||||||
try {
|
|
||||||
Flag flag = Flag.valueOf(flagStr);
|
|
||||||
|
|
||||||
switch (flag) {
|
|
||||||
case ANSWERED: {
|
|
||||||
answered = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DELETED: {
|
|
||||||
// Don't store this in column 'flags'
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FLAGGED: {
|
|
||||||
flagged = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FORWARDED: {
|
|
||||||
forwarded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SEEN: {
|
|
||||||
read = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DRAFT:
|
|
||||||
case RECENT:
|
|
||||||
case X_DESTROYED:
|
|
||||||
case X_DOWNLOADED_FULL:
|
|
||||||
case X_DOWNLOADED_PARTIAL:
|
|
||||||
case X_GOT_ALL_HEADERS:
|
|
||||||
case X_REMOTE_COPY_STARTED:
|
|
||||||
case X_SEND_FAILED:
|
|
||||||
case X_SEND_IN_PROGRESS: {
|
|
||||||
extraFlags.add(flag);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Ignore bad flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
cv.put("flags", serializeFlags(extraFlags.toArray(EMPTY_FLAG_ARRAY)));
|
|
||||||
cv.put("read", read);
|
|
||||||
cv.put("flagged", flagged);
|
|
||||||
cv.put("answered", answered);
|
|
||||||
cv.put("forwarded", forwarded);
|
|
||||||
|
|
||||||
db.update("messages", cv, "id = ?", new String[] { Long.toString(id) });
|
|
||||||
|
|
||||||
cv.clear();
|
|
||||||
extraFlags.clear();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db.getVersion() < 47) {
|
|
||||||
// Create new 'threads' table
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS threads");
|
|
||||||
db.execSQL("CREATE TABLE threads (" +
|
|
||||||
"id INTEGER PRIMARY KEY, " +
|
|
||||||
"message_id INTEGER, " +
|
|
||||||
"root INTEGER, " +
|
|
||||||
"parent INTEGER" +
|
|
||||||
")");
|
|
||||||
|
|
||||||
// Create indices for new table
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
|
||||||
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
|
||||||
|
|
||||||
// Create entries for all messages in 'threads' table
|
|
||||||
db.execSQL("INSERT INTO threads (message_id) SELECT id FROM messages");
|
|
||||||
|
|
||||||
// Copy thread structure from 'messages' table to 'threads'
|
|
||||||
Cursor cursor = db.query("messages",
|
|
||||||
new String[] { "id", "thread_root", "thread_parent" },
|
|
||||||
null, null, null, null, null);
|
|
||||||
try {
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
cv.clear();
|
|
||||||
long messageId = cursor.getLong(0);
|
|
||||||
|
|
||||||
if (!cursor.isNull(1)) {
|
|
||||||
long threadRootMessageId = cursor.getLong(1);
|
|
||||||
db.execSQL("UPDATE threads SET root = (SELECT t.id FROM " +
|
|
||||||
"threads t WHERE t.message_id = ?) " +
|
|
||||||
"WHERE message_id = ?",
|
|
||||||
new String[] {
|
|
||||||
Long.toString(threadRootMessageId),
|
|
||||||
Long.toString(messageId)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cursor.isNull(2)) {
|
|
||||||
long threadParentMessageId = cursor.getLong(2);
|
|
||||||
db.execSQL("UPDATE threads SET parent = (SELECT t.id FROM " +
|
|
||||||
"threads t WHERE t.message_id = ?) " +
|
|
||||||
"WHERE message_id = ?",
|
|
||||||
new String[] {
|
|
||||||
Long.toString(threadParentMessageId),
|
|
||||||
Long.toString(messageId)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove indices for old thread-related columns in 'messages' table
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
|
||||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
|
||||||
|
|
||||||
// Clear out old thread-related columns in 'messages'
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
cv.putNull("thread_root");
|
|
||||||
cv.putNull("thread_parent");
|
|
||||||
db.update("messages", cv, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db.getVersion() < 48) {
|
|
||||||
db.execSQL("UPDATE threads SET root=id WHERE root IS NULL");
|
|
||||||
|
|
||||||
db.execSQL("CREATE TRIGGER set_thread_root " +
|
|
||||||
"AFTER INSERT ON threads " +
|
|
||||||
"BEGIN " +
|
|
||||||
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
|
||||||
"END");
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 49) {
|
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
|
||||||
|
|
||||||
}
|
|
||||||
if (db.getVersion() < 50) {
|
|
||||||
try {
|
|
||||||
db.execSQL("ALTER TABLE folders ADD notify_class TEXT default '" +
|
|
||||||
Folder.FolderClass.INHERITED.name() + "'");
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
cv.put("notify_class", Folder.FolderClass.FIRST_CLASS.name());
|
|
||||||
|
|
||||||
db.update("folders", cv, "name = ?",
|
|
||||||
new String[] { getAccount().getInboxFolderName() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.setVersion(DB_VERSION);
|
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (db.getVersion() != DB_VERSION) {
|
|
||||||
throw new RuntimeException("Database upgrade failed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update41Metadata(final SQLiteDatabase db, SharedPreferences prefs, int id, String name) {
|
|
||||||
|
|
||||||
|
|
||||||
Folder.FolderClass displayClass = Folder.FolderClass.NO_CLASS;
|
|
||||||
Folder.FolderClass syncClass = Folder.FolderClass.INHERITED;
|
|
||||||
Folder.FolderClass pushClass = Folder.FolderClass.SECOND_CLASS;
|
|
||||||
boolean inTopGroup = false;
|
|
||||||
boolean integrate = false;
|
|
||||||
if (mAccount.getInboxFolderName().equals(name)) {
|
|
||||||
displayClass = Folder.FolderClass.FIRST_CLASS;
|
|
||||||
syncClass = Folder.FolderClass.FIRST_CLASS;
|
|
||||||
pushClass = Folder.FolderClass.FIRST_CLASS;
|
|
||||||
inTopGroup = true;
|
|
||||||
integrate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
displayClass = Folder.FolderClass.valueOf(prefs.getString(uUid + "." + name + ".displayMode", displayClass.name()));
|
|
||||||
syncClass = Folder.FolderClass.valueOf(prefs.getString(uUid + "." + name + ".syncMode", syncClass.name()));
|
|
||||||
pushClass = Folder.FolderClass.valueOf(prefs.getString(uUid + "." + name + ".pushMode", pushClass.name()));
|
|
||||||
inTopGroup = prefs.getBoolean(uUid + "." + name + ".inTopGroup", inTopGroup);
|
|
||||||
integrate = prefs.getBoolean(uUid + "." + name + ".integrate", integrate);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(K9.LOG_TAG, " Throwing away an error while trying to upgrade folder metadata", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayClass == Folder.FolderClass.NONE) {
|
|
||||||
displayClass = Folder.FolderClass.NO_CLASS;
|
|
||||||
}
|
|
||||||
if (syncClass == Folder.FolderClass.NONE) {
|
|
||||||
syncClass = Folder.FolderClass.INHERITED;
|
|
||||||
}
|
|
||||||
if (pushClass == Folder.FolderClass.NONE) {
|
|
||||||
pushClass = Folder.FolderClass.INHERITED;
|
|
||||||
}
|
|
||||||
|
|
||||||
db.execSQL("UPDATE folders SET integrate = ?, top_group = ?, poll_class=?, push_class =?, display_class = ? WHERE id = ?",
|
|
||||||
new Object[] { integrate, inTopGroup, syncClass, pushClass, displayClass, id });
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public long getSize() throws UnavailableStorageException {
|
public long getSize() throws UnavailableStorageException {
|
||||||
|
|
||||||
final StorageManager storageManager = StorageManager.getInstance(mApplication);
|
final StorageManager storageManager = StorageManager.getInstance(mApplication);
|
||||||
|
599
src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java
Normal file
599
src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.Account;
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
import com.fsck.k9.R;
|
||||||
|
import com.fsck.k9.helper.Utility;
|
||||||
|
import com.fsck.k9.mail.Flag;
|
||||||
|
import com.fsck.k9.mail.Folder;
|
||||||
|
import com.fsck.k9.mail.Message;
|
||||||
|
import com.fsck.k9.mail.store.LockableDatabase;
|
||||||
|
import com.fsck.k9.provider.AttachmentProvider;
|
||||||
|
|
||||||
|
class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private final LocalStore localStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param localStore
|
||||||
|
*/
|
||||||
|
StoreSchemaDefinition(LocalStore localStore) {
|
||||||
|
this.localStore = localStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return LocalStore.DB_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doDbUpgrade(final SQLiteDatabase db) {
|
||||||
|
try {
|
||||||
|
upgradeDatabase(db);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Exception while upgrading database. Resetting the DB to v0", e);
|
||||||
|
db.setVersion(0);
|
||||||
|
upgradeDatabase(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upgradeDatabase(final SQLiteDatabase db) {
|
||||||
|
Log.i(K9.LOG_TAG, String.format(Locale.US, "Upgrading database from version %d to version %d",
|
||||||
|
db.getVersion(), LocalStore.DB_VERSION));
|
||||||
|
|
||||||
|
AttachmentProvider.clear(this.localStore.mApplication);
|
||||||
|
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
// schema version 29 was when we moved to incremental updates
|
||||||
|
// in the case of a new db or a < v29 db, we blow away and start from scratch
|
||||||
|
if (db.getVersion() < 29) {
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS folders");
|
||||||
|
db.execSQL("CREATE TABLE folders (id INTEGER PRIMARY KEY, name TEXT, "
|
||||||
|
+ "last_updated INTEGER, unread_count INTEGER, visible_limit INTEGER, status TEXT, "
|
||||||
|
+ "push_state TEXT, last_pushed INTEGER, flagged_count INTEGER default 0, "
|
||||||
|
+ "integrate INTEGER, top_group INTEGER, poll_class TEXT, push_class TEXT, display_class TEXT, notify_class TEXT"
|
||||||
|
+ ")");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS folder_name ON folders (name)");
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS messages");
|
||||||
|
db.execSQL("CREATE TABLE messages (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"deleted INTEGER default 0, " +
|
||||||
|
"folder_id INTEGER, " +
|
||||||
|
"uid TEXT, " +
|
||||||
|
"subject TEXT, " +
|
||||||
|
"date INTEGER, " +
|
||||||
|
"flags TEXT, " +
|
||||||
|
"sender_list TEXT, " +
|
||||||
|
"to_list TEXT, " +
|
||||||
|
"cc_list TEXT, " +
|
||||||
|
"bcc_list TEXT, " +
|
||||||
|
"reply_to_list TEXT, " +
|
||||||
|
"html_content TEXT, " +
|
||||||
|
"text_content TEXT, " +
|
||||||
|
"attachment_count INTEGER, " +
|
||||||
|
"internal_date INTEGER, " +
|
||||||
|
"message_id TEXT, " +
|
||||||
|
"preview TEXT, " +
|
||||||
|
"mime_type TEXT, "+
|
||||||
|
"normalized_subject_hash INTEGER, " +
|
||||||
|
"empty INTEGER, " +
|
||||||
|
"read INTEGER default 0, " +
|
||||||
|
"flagged INTEGER default 0, " +
|
||||||
|
"answered INTEGER default 0, " +
|
||||||
|
"forwarded INTEGER default 0" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS headers");
|
||||||
|
db.execSQL("CREATE TABLE headers (id INTEGER PRIMARY KEY, message_id INTEGER, name TEXT, value TEXT)");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS header_folder ON headers (message_id)");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_uid ON messages (uid, folder_id)");
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_folder_id");
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_read");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_flagged");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_composite");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS threads");
|
||||||
|
db.execSQL("CREATE TABLE threads (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"message_id INTEGER, " +
|
||||||
|
"root INTEGER, " +
|
||||||
|
"parent INTEGER" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
||||||
|
|
||||||
|
db.execSQL("DROP TRIGGER IF EXISTS set_thread_root");
|
||||||
|
db.execSQL("CREATE TRIGGER set_thread_root " +
|
||||||
|
"AFTER INSERT ON threads " +
|
||||||
|
"BEGIN " +
|
||||||
|
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
||||||
|
"END");
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS attachments");
|
||||||
|
db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
|
||||||
|
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
|
||||||
|
+ "mime_type TEXT, content_id TEXT, content_disposition TEXT)");
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
||||||
|
db.execSQL("CREATE TABLE pending_commands " +
|
||||||
|
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
|
||||||
|
|
||||||
|
db.execSQL("DROP TRIGGER IF EXISTS delete_folder");
|
||||||
|
db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
|
||||||
|
|
||||||
|
db.execSQL("DROP TRIGGER IF EXISTS delete_message");
|
||||||
|
db.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; "
|
||||||
|
+ "DELETE FROM headers where old.id = message_id; END;");
|
||||||
|
} else {
|
||||||
|
// in the case that we're starting out at 29 or newer, run all the needed updates
|
||||||
|
|
||||||
|
if (db.getVersion() < 30) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD deleted INTEGER default 0");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.toString().startsWith("duplicate column name: deleted")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 31) {
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 32) {
|
||||||
|
db.execSQL("UPDATE messages SET deleted = 1 WHERE flags LIKE '%DELETED%'");
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 33) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD preview TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.toString().startsWith("duplicate column name: preview")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 34) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE folders ADD flagged_count INTEGER default 0");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name: flagged_count")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 35) {
|
||||||
|
try {
|
||||||
|
db.execSQL("update messages set flags = replace(flags, 'X_NO_SEEN_INFO', 'X_BAD_FLAG')");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to get rid of obsolete flag X_NO_SEEN_INFO", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 36) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE attachments ADD content_id TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to add content_id column to attachments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 37) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE attachments ADD content_disposition TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to add content_disposition column to attachments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database version 38 is solely to prune cached attachments now that we clear them better
|
||||||
|
if (db.getVersion() < 39) {
|
||||||
|
try {
|
||||||
|
db.execSQL("DELETE FROM headers WHERE id in (SELECT headers.id FROM headers LEFT JOIN messages ON headers.message_id = messages.id WHERE messages.id IS NULL)");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to remove extra header data from the database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// V40: Store the MIME type for a message.
|
||||||
|
if (db.getVersion() < 40) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD mime_type TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to add mime_type column to messages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() < 41) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE folders ADD integrate INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD top_group INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD poll_class TEXT");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD push_class TEXT");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD display_class TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
SharedPreferences prefs = this.localStore.getPreferences();
|
||||||
|
cursor = db.rawQuery("SELECT id, name FROM folders", null);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
try {
|
||||||
|
int id = cursor.getInt(0);
|
||||||
|
String name = cursor.getString(1);
|
||||||
|
update41Metadata(db, prefs, id, name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, " error trying to ugpgrade a folder class", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Exception while upgrading database to v41. folder classes may have vanished", e);
|
||||||
|
} finally {
|
||||||
|
Utility.closeQuietly(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() == 41) {
|
||||||
|
try {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
SharedPreferences.Editor editor = this.localStore.getPreferences().edit();
|
||||||
|
|
||||||
|
List <? extends Folder > folders = this.localStore.getPersonalNamespaces(true);
|
||||||
|
for (Folder folder : folders) {
|
||||||
|
if (folder instanceof LocalFolder) {
|
||||||
|
LocalFolder lFolder = (LocalFolder)folder;
|
||||||
|
lFolder.save(editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.commit();
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
Log.i(K9.LOG_TAG, "Putting folder preferences for " + folders.size() + " folders back into Preferences took " + (endTime - startTime) + " ms");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Could not replace Preferences in upgrade from DB_VERSION 41", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 43) {
|
||||||
|
try {
|
||||||
|
// If folder "OUTBOX" (old, v3.800 - v3.802) exists, rename it to
|
||||||
|
// "K9MAIL_INTERNAL_OUTBOX" (new)
|
||||||
|
LocalFolder oldOutbox = new LocalFolder(this.localStore, "OUTBOX");
|
||||||
|
if (oldOutbox.exists()) {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("name", Account.OUTBOX);
|
||||||
|
db.update("folders", cv, "name = ?", new String[] { "OUTBOX" });
|
||||||
|
Log.i(K9.LOG_TAG, "Renamed folder OUTBOX to " + Account.OUTBOX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if old (pre v3.800) localized outbox folder exists
|
||||||
|
String localizedOutbox = K9.app.getString(R.string.special_mailbox_name_outbox);
|
||||||
|
LocalFolder obsoleteOutbox = new LocalFolder(this.localStore, localizedOutbox);
|
||||||
|
if (obsoleteOutbox.exists()) {
|
||||||
|
// Get all messages from the localized outbox ...
|
||||||
|
Message[] messages = obsoleteOutbox.getMessages(null, false);
|
||||||
|
|
||||||
|
if (messages.length > 0) {
|
||||||
|
// ... and move them to the drafts folder (we don't want to
|
||||||
|
// surprise the user by sending potentially very old messages)
|
||||||
|
LocalFolder drafts = new LocalFolder(this.localStore, this.localStore.getAccount().getDraftsFolderName());
|
||||||
|
obsoleteOutbox.moveMessages(messages, drafts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get rid of the localized outbox
|
||||||
|
obsoleteOutbox.delete();
|
||||||
|
obsoleteOutbox.delete(true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Error trying to fix the outbox folders", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 44) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD thread_root INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD thread_parent INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD normalized_subject_hash INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD empty INTEGER");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 45) {
|
||||||
|
try {
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_root ON messages (thread_root)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_parent ON messages (thread_parent)");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 46) {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD read INTEGER default 0");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD flagged INTEGER default 0");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD answered INTEGER default 0");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD forwarded INTEGER default 0");
|
||||||
|
|
||||||
|
String[] projection = { "id", "flags" };
|
||||||
|
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
List<Flag> extraFlags = new ArrayList<Flag>();
|
||||||
|
|
||||||
|
Cursor cursor = db.query("messages", projection, null, null, null, null, null);
|
||||||
|
try {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
long id = cursor.getLong(0);
|
||||||
|
String flagList = cursor.getString(1);
|
||||||
|
|
||||||
|
boolean read = false;
|
||||||
|
boolean flagged = false;
|
||||||
|
boolean answered = false;
|
||||||
|
boolean forwarded = false;
|
||||||
|
|
||||||
|
if (flagList != null && flagList.length() > 0) {
|
||||||
|
String[] flags = flagList.split(",");
|
||||||
|
|
||||||
|
for (String flagStr : flags) {
|
||||||
|
try {
|
||||||
|
Flag flag = Flag.valueOf(flagStr);
|
||||||
|
|
||||||
|
switch (flag) {
|
||||||
|
case ANSWERED: {
|
||||||
|
answered = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DELETED: {
|
||||||
|
// Don't store this in column 'flags'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FLAGGED: {
|
||||||
|
flagged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FORWARDED: {
|
||||||
|
forwarded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SEEN: {
|
||||||
|
read = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DRAFT:
|
||||||
|
case RECENT:
|
||||||
|
case X_DESTROYED:
|
||||||
|
case X_DOWNLOADED_FULL:
|
||||||
|
case X_DOWNLOADED_PARTIAL:
|
||||||
|
case X_GOT_ALL_HEADERS:
|
||||||
|
case X_REMOTE_COPY_STARTED:
|
||||||
|
case X_SEND_FAILED:
|
||||||
|
case X_SEND_IN_PROGRESS: {
|
||||||
|
extraFlags.add(flag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore bad flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cv.put("flags", this.localStore.serializeFlags(extraFlags.toArray(LocalStore.EMPTY_FLAG_ARRAY)));
|
||||||
|
cv.put("read", read);
|
||||||
|
cv.put("flagged", flagged);
|
||||||
|
cv.put("answered", answered);
|
||||||
|
cv.put("forwarded", forwarded);
|
||||||
|
|
||||||
|
db.update("messages", cv, "id = ?", new String[] { Long.toString(id) });
|
||||||
|
|
||||||
|
cv.clear();
|
||||||
|
extraFlags.clear();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() < 47) {
|
||||||
|
// Create new 'threads' table
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS threads");
|
||||||
|
db.execSQL("CREATE TABLE threads (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"message_id INTEGER, " +
|
||||||
|
"root INTEGER, " +
|
||||||
|
"parent INTEGER" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
// Create indices for new table
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
||||||
|
|
||||||
|
// Create entries for all messages in 'threads' table
|
||||||
|
db.execSQL("INSERT INTO threads (message_id) SELECT id FROM messages");
|
||||||
|
|
||||||
|
// Copy thread structure from 'messages' table to 'threads'
|
||||||
|
Cursor cursor = db.query("messages",
|
||||||
|
new String[] { "id", "thread_root", "thread_parent" },
|
||||||
|
null, null, null, null, null);
|
||||||
|
try {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
cv.clear();
|
||||||
|
long messageId = cursor.getLong(0);
|
||||||
|
|
||||||
|
if (!cursor.isNull(1)) {
|
||||||
|
long threadRootMessageId = cursor.getLong(1);
|
||||||
|
db.execSQL("UPDATE threads SET root = (SELECT t.id FROM " +
|
||||||
|
"threads t WHERE t.message_id = ?) " +
|
||||||
|
"WHERE message_id = ?",
|
||||||
|
new String[] {
|
||||||
|
Long.toString(threadRootMessageId),
|
||||||
|
Long.toString(messageId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cursor.isNull(2)) {
|
||||||
|
long threadParentMessageId = cursor.getLong(2);
|
||||||
|
db.execSQL("UPDATE threads SET parent = (SELECT t.id FROM " +
|
||||||
|
"threads t WHERE t.message_id = ?) " +
|
||||||
|
"WHERE message_id = ?",
|
||||||
|
new String[] {
|
||||||
|
Long.toString(threadParentMessageId),
|
||||||
|
Long.toString(messageId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove indices for old thread-related columns in 'messages' table
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
||||||
|
|
||||||
|
// Clear out old thread-related columns in 'messages'
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.putNull("thread_root");
|
||||||
|
cv.putNull("thread_parent");
|
||||||
|
db.update("messages", cv, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() < 48) {
|
||||||
|
db.execSQL("UPDATE threads SET root=id WHERE root IS NULL");
|
||||||
|
|
||||||
|
db.execSQL("CREATE TRIGGER set_thread_root " +
|
||||||
|
"AFTER INSERT ON threads " +
|
||||||
|
"BEGIN " +
|
||||||
|
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
||||||
|
"END");
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 49) {
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 50) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE folders ADD notify_class TEXT default '" +
|
||||||
|
Folder.FolderClass.INHERITED.name() + "'");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("notify_class", Folder.FolderClass.FIRST_CLASS.name());
|
||||||
|
|
||||||
|
db.update("folders", cv, "name = ?",
|
||||||
|
new String[] { this.localStore.getAccount().getInboxFolderName() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.setVersion(LocalStore.DB_VERSION);
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() != LocalStore.DB_VERSION) {
|
||||||
|
throw new RuntimeException("Database upgrade failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update41Metadata(final SQLiteDatabase db, SharedPreferences prefs, int id, String name) {
|
||||||
|
|
||||||
|
|
||||||
|
Folder.FolderClass displayClass = Folder.FolderClass.NO_CLASS;
|
||||||
|
Folder.FolderClass syncClass = Folder.FolderClass.INHERITED;
|
||||||
|
Folder.FolderClass pushClass = Folder.FolderClass.SECOND_CLASS;
|
||||||
|
boolean inTopGroup = false;
|
||||||
|
boolean integrate = false;
|
||||||
|
if (this.localStore.getAccount().getInboxFolderName().equals(name)) {
|
||||||
|
displayClass = Folder.FolderClass.FIRST_CLASS;
|
||||||
|
syncClass = Folder.FolderClass.FIRST_CLASS;
|
||||||
|
pushClass = Folder.FolderClass.FIRST_CLASS;
|
||||||
|
inTopGroup = true;
|
||||||
|
integrate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
displayClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".displayMode", displayClass.name()));
|
||||||
|
syncClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".syncMode", syncClass.name()));
|
||||||
|
pushClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".pushMode", pushClass.name()));
|
||||||
|
inTopGroup = prefs.getBoolean(this.localStore.uUid + "." + name + ".inTopGroup", inTopGroup);
|
||||||
|
integrate = prefs.getBoolean(this.localStore.uUid + "." + name + ".integrate", integrate);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, " Throwing away an error while trying to upgrade folder metadata", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayClass == Folder.FolderClass.NONE) {
|
||||||
|
displayClass = Folder.FolderClass.NO_CLASS;
|
||||||
|
}
|
||||||
|
if (syncClass == Folder.FolderClass.NONE) {
|
||||||
|
syncClass = Folder.FolderClass.INHERITED;
|
||||||
|
}
|
||||||
|
if (pushClass == Folder.FolderClass.NONE) {
|
||||||
|
pushClass = Folder.FolderClass.INHERITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.execSQL("UPDATE folders SET integrate = ?, top_group = ?, poll_class=?, push_class =?, display_class = ? WHERE id = ?",
|
||||||
|
new Object[] { integrate, inTopGroup, syncClass, pushClass, displayClass, id });
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user