Add a log section for the database schema.

This commit is contained in:
Greyson Parrelli 2024-04-05 16:43:09 -04:00
parent 9bd4e9524c
commit 8793981804
3 changed files with 83 additions and 0 deletions

View file

@ -0,0 +1,44 @@
package org.thoughtcrime.securesms.logsubmit
import android.content.Context
import org.signal.core.util.getAllIndexDefinitions
import org.signal.core.util.getAllTableDefinitions
import org.signal.core.util.getForeignKeys
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations
/**
* Renders data pertaining to sender key. While all private info is obfuscated, this is still only intended to be printed for internal users.
*/
class LogSectionDatabaseSchema : LogSection {
override fun getTitle(): String {
return "DATABASE SCHEMA"
}
override fun getContent(context: Context): CharSequence {
val builder = StringBuilder()
builder.append("--- Metadata").append("\n")
builder.append("Version: ${SignalDatabaseMigrations.DATABASE_VERSION}\n")
builder.append("\n\n")
builder.append("--- Tables").append("\n")
SignalDatabase.rawDatabase.getAllTableDefinitions().forEach {
builder.append(it.statement).append("\n")
}
builder.append("\n\n")
builder.append("--- Indexes").append("\n")
SignalDatabase.rawDatabase.getAllIndexDefinitions().forEach {
builder.append(it.statement).append("\n")
}
builder.append("\n\n")
builder.append("--- Foreign Keys").append("\n")
SignalDatabase.rawDatabase.getForeignKeys().forEach {
builder.append("${it.table}.${it.column} DEPENDS ON ${it.dependsOnTable}.${it.dependsOnColumn}, ON DELETE ${it.onDelete}").append("\n")
}
builder.append("\n\n")
return builder
}
}

View file

@ -95,6 +95,7 @@ public class SubmitDebugLogRepository {
if (FeatureFlags.internalUser()) {
add(new LogSectionSenderKey());
}
add(new LogSectionDatabaseSchema());
add(new LogSectionRemappedRecords());
add(new LogSectionAnr());
add(new LogSectionLogcat());

View file

@ -35,6 +35,39 @@ fun SupportSQLiteDatabase.getTableRowCount(table: String): Int {
}
}
fun SupportSQLiteDatabase.getAllTables(): List<String> {
return SqlUtil.getAllTables(this)
}
/**
* Returns a list of objects that represent the table definitions in the database. Basically the table name and then the SQL that was used to create it.
*/
fun SupportSQLiteDatabase.getAllTableDefinitions(): List<CreateStatement> {
return this.query("SELECT name, sql FROM sqlite_schema WHERE type = 'table' AND sql NOT NULL AND name != 'sqlite_sequence'")
.readToList { cursor ->
CreateStatement(
name = cursor.requireNonNullString("name"),
statement = cursor.requireNonNullString("sql").replace(" ", "")
)
}
.filterNot { it.name.startsWith("sqlite_stat") }
.sortedBy { it.name }
}
/**
* Returns a list of objects that represent the index definitions in the database. Basically the index name and then the SQL that was used to create it.
*/
fun SupportSQLiteDatabase.getAllIndexDefinitions(): List<CreateStatement> {
return this.query("SELECT name, sql FROM sqlite_schema WHERE type = 'index' AND sql NOT NULL")
.readToList { cursor ->
CreateStatement(
name = cursor.requireNonNullString("name"),
statement = cursor.requireNonNullString("sql")
)
}
.sortedBy { it.name }
}
fun SupportSQLiteDatabase.getForeignKeys(): List<ForeignKeyConstraint> {
return SqlUtil.getAllTables(this)
.map { table ->
@ -426,3 +459,8 @@ data class Index(
val table: String,
val columns: List<String>
)
data class CreateStatement(
val name: String,
val statement: String
)