Get a big backupV2 import fully working.

This commit is contained in:
Greyson Parrelli
2024-10-10 16:01:48 -04:00
parent 0d878ca70a
commit a90df1e262
27 changed files with 261 additions and 193 deletions

View File

@@ -6,6 +6,12 @@ import android.database.sqlite.SQLiteDatabase
import androidx.core.content.contentValuesOf
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.db.SupportSQLiteQueryBuilder
import org.signal.core.util.SqlUtil.ForeignKeyViolation
import org.signal.core.util.logging.Log
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
private val TAG = "SQLiteDatabaseExtensions"
/**
* Begins a transaction on the `this` database, runs the provided [block] providing the `this` value as it's argument
@@ -46,7 +52,11 @@ fun SupportSQLiteDatabase.getAllTables(): List<String> {
* 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'")
return this
.select("name", "sql")
.from("sqlite_schema")
.where("type = ? AND sql NOT NULL AND name != ?", "table", "sqlite_sequence")
.run()
.readToList { cursor ->
CreateStatement(
name = cursor.requireNonNullString("name"),
@@ -61,7 +71,11 @@ fun SupportSQLiteDatabase.getAllTableDefinitions(): List<CreateStatement> {
* 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")
return this
.select("name", "sql")
.from("sqlite_schema")
.where("type = ? AND sql NOT NULL", "index")
.run()
.readToList { cursor ->
CreateStatement(
name = cursor.requireNonNullString("name"),
@@ -71,6 +85,24 @@ fun SupportSQLiteDatabase.getAllIndexDefinitions(): List<CreateStatement> {
.sortedBy { it.name }
}
/**
* Retrieves the names of all triggers, sorted alphabetically.
*/
fun SupportSQLiteDatabase.getAllTriggerDefinitions(): List<CreateStatement> {
return this
.select("name", "sql")
.from("sqlite_schema")
.where("type = ? AND sql NOT NULL", "trigger")
.run()
.readToList {
CreateStatement(
name = it.requireNonNullString("name"),
statement = it.requireNonNullString("sql")
)
}
.sortedBy { it.name }
}
fun SupportSQLiteDatabase.getForeignKeys(): List<ForeignKeyConstraint> {
return SqlUtil.getAllTables(this)
.map { table ->
@@ -93,6 +125,24 @@ fun SupportSQLiteDatabase.areForeignKeyConstraintsEnabled(): Boolean {
}
}
/**
* Provides a list of all foreign key violations present.
* If a [targetTable] is specified, results will be limited to that table specifically.
* Otherwise, the check will be performed across all tables.
*/
@JvmOverloads
fun SupportSQLiteDatabase.getForeignKeyViolations(targetTable: String? = null): List<ForeignKeyViolation> {
return SqlUtil.getForeignKeyViolations(this, targetTable)
}
/**
* For tables that have an autoincrementing primary key, this will reset the key to start back at 1.
* IMPORTANT: This is quite dangerous! Only do this if you're effectively resetting the entire database.
*/
fun SupportSQLiteDatabase.resetAutoIncrementValue(targetTable: String) {
SqlUtil.resetAutoIncrementValue(this, targetTable)
}
/**
* Does a full WAL checkpoint (TRUNCATE mode, where the log is for sure flushed and the log is zero'd out).
* Will try up to [maxAttempts] times. Can technically fail if the database is too active and the checkpoint
@@ -132,6 +182,22 @@ fun SupportSQLiteDatabase.getIndexes(): List<Index> {
}
}
fun SupportSQLiteDatabase.forceForeignKeyConstraintsEnabled(enabled: Boolean, timeout: Duration = 10.seconds) {
val startTime = System.currentTimeMillis()
while (true) {
try {
this.setForeignKeyConstraintsEnabled(enabled)
break
} catch (e: IllegalStateException) {
if (System.currentTimeMillis() - startTime > timeout.inWholeMilliseconds) {
throw IllegalStateException("Failed to force foreign keys to '$enabled' within the timeout of $timeout", e)
}
Log.w(TAG, "Failed to set foreign keys because we're in a transaction. Waiting 100ms then trying again.")
ThreadUtil.sleep(100)
}
}
}
/**
* Checks if a row exists that matches the query.
*/