Better database error handling.
This commit is contained in:
parent
f8d2044356
commit
d5f63da9e4
6 changed files with 74 additions and 4 deletions
|
@ -101,7 +101,7 @@ public class JobDatabase extends SQLiteOpenHelper implements SignalDatabase {
|
|||
}
|
||||
|
||||
public JobDatabase(@NonNull Application application, @NonNull DatabaseSecret databaseSecret) {
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook());
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook(), new SqlCipherErrorHandler(DATABASE_NAME));
|
||||
|
||||
this.application = application;
|
||||
this.databaseSecret = databaseSecret;
|
||||
|
|
|
@ -61,7 +61,7 @@ public class KeyValueDatabase extends SQLiteOpenHelper implements SignalDatabase
|
|||
}
|
||||
|
||||
public KeyValueDatabase(@NonNull Application application, @NonNull DatabaseSecret databaseSecret) {
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook());
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook(), new SqlCipherErrorHandler(DATABASE_NAME));
|
||||
|
||||
this.application = application;
|
||||
this.databaseSecret = databaseSecret;
|
||||
|
|
|
@ -64,7 +64,7 @@ public class MegaphoneDatabase extends SQLiteOpenHelper implements SignalDatabas
|
|||
}
|
||||
|
||||
public MegaphoneDatabase(@NonNull Application application, @NonNull DatabaseSecret databaseSecret) {
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook());
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook(), new SqlCipherErrorHandler(DATABASE_NAME));
|
||||
|
||||
this.application = application;
|
||||
this.databaseSecret = databaseSecret;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.DatabaseErrorHandler;
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||
|
||||
/**
|
||||
* The default error handler wipes the file. This one instead prints some diagnostics and then crashes so the original corrupt file isn't lost.
|
||||
*/
|
||||
public final class SqlCipherErrorHandler implements DatabaseErrorHandler {
|
||||
|
||||
private static final String TAG = Log.tag(SqlCipherErrorHandler.class);
|
||||
|
||||
private final String tableName;
|
||||
|
||||
public SqlCipherErrorHandler(@NonNull String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCorruption(SQLiteDatabase db) {
|
||||
Log.e(TAG, "Database '" + tableName + "' corrupted! Going to try to run some diagnostics.");
|
||||
|
||||
Log.w(TAG, " ===== PRAGMA integrity_check =====");
|
||||
try (Cursor cursor = db.rawQuery("PRAGMA integrity_check", null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
Log.w(TAG, CursorUtil.readRowAsString(cursor));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e(TAG, "Failed to do integrity_check!", t);
|
||||
}
|
||||
|
||||
Log.w(TAG, "===== PRAGMA cipher_integrity_check =====");
|
||||
try (Cursor cursor = db.rawQuery("PRAGMA cipher_integrity_check", null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
Log.w(TAG, CursorUtil.readRowAsString(cursor));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e(TAG, "Failed to do cipher_integrity_check!", t);
|
||||
}
|
||||
|
||||
throw new DatabaseCorruptedError();
|
||||
}
|
||||
|
||||
public static final class DatabaseCorruptedError extends Error {
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
|
|||
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
|
||||
import org.thoughtcrime.securesms.database.SqlCipherErrorHandler;
|
||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
||||
|
@ -205,7 +206,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
|||
private final DatabaseSecret databaseSecret;
|
||||
|
||||
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook());
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION, new SqlCipherDatabaseHook(), new SqlCipherErrorHandler(DATABASE_NAME));
|
||||
|
||||
this.context = context.getApplicationContext();
|
||||
this.databaseSecret = databaseSecret;
|
||||
|
|
|
@ -77,4 +77,20 @@ public final class CursorUtil {
|
|||
return Optional.fromNullable(requireBlob(cursor, column));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads each column as a string, and concatenates them together into a single string separated by |
|
||||
*/
|
||||
public static String readRowAsString(@NonNull Cursor cursor) {
|
||||
StringBuilder row = new StringBuilder();
|
||||
|
||||
for (int i = 0, len = cursor.getColumnCount(); i < len; i++) {
|
||||
row.append(cursor.getString(i));
|
||||
if (i < len - 1) {
|
||||
row.append(" | ");
|
||||
}
|
||||
}
|
||||
|
||||
return row.toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue