2020-01-13 15:01:46 +01:00
|
|
|
package com.topjohnwu.magisk.core.magiskdb
|
2019-05-06 19:03:28 +02:00
|
|
|
|
|
|
|
import androidx.annotation.StringDef
|
2020-07-09 13:49:14 +02:00
|
|
|
import com.topjohnwu.magisk.extensions.await
|
|
|
|
import com.topjohnwu.superuser.Shell
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.async
|
|
|
|
import kotlinx.coroutines.awaitAll
|
|
|
|
import kotlinx.coroutines.withContext
|
2019-05-06 19:03:28 +02:00
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
class Query(private val _query: String) {
|
|
|
|
val query get() = "magisk --sqlite '$_query'"
|
2019-05-06 19:03:28 +02:00
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
interface Builder {
|
|
|
|
val requestType: String
|
|
|
|
var table: String
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
2020-07-09 13:49:14 +02:00
|
|
|
|
|
|
|
suspend inline fun <R : Any> query(crossinline mapper: (Map<String, String>) -> R?): List<R> =
|
|
|
|
withContext(Dispatchers.Default) {
|
|
|
|
Shell.su(query).await().out.map { line ->
|
|
|
|
async {
|
|
|
|
line.split("\\|".toRegex())
|
|
|
|
.map { it.split("=", limit = 2) }
|
|
|
|
.filter { it.size == 2 }
|
|
|
|
.map { it[0] to it[1] }
|
|
|
|
.toMap()
|
|
|
|
.let(mapper)
|
|
|
|
}
|
|
|
|
}.awaitAll().filterNotNull()
|
|
|
|
}
|
|
|
|
|
|
|
|
suspend inline fun query() = query { it }
|
|
|
|
|
|
|
|
suspend inline fun commit() = Shell.su(query).to(null).await()
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
class Delete : Query.Builder {
|
2019-05-06 19:03:28 +02:00
|
|
|
override val requestType: String = "DELETE FROM"
|
|
|
|
override var table = ""
|
|
|
|
|
|
|
|
private var condition = ""
|
|
|
|
|
|
|
|
fun condition(builder: Condition.() -> Unit) {
|
|
|
|
condition = Condition().apply(builder).toString()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun toString(): String {
|
2019-06-06 09:39:24 +02:00
|
|
|
return listOf(requestType, table, condition).joinToString(" ")
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
class Select : Query.Builder {
|
2019-05-06 19:03:28 +02:00
|
|
|
override val requestType: String get() = "SELECT $fields FROM"
|
|
|
|
override lateinit var table: String
|
|
|
|
|
|
|
|
private var fields = "*"
|
|
|
|
private var condition = ""
|
|
|
|
private var orderField = ""
|
|
|
|
|
|
|
|
fun fields(vararg newFields: String) {
|
|
|
|
if (newFields.isEmpty()) {
|
|
|
|
fields = "*"
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fields = newFields.joinToString(", ")
|
|
|
|
}
|
|
|
|
|
|
|
|
fun condition(builder: Condition.() -> Unit) {
|
|
|
|
condition = Condition().apply(builder).toString()
|
|
|
|
}
|
|
|
|
|
|
|
|
fun orderBy(field: String, @OrderStrict order: String) {
|
|
|
|
orderField = "ORDER BY $field $order"
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun toString(): String {
|
2019-06-06 09:39:24 +02:00
|
|
|
return listOf(requestType, table, condition, orderField).joinToString(" ")
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Replace : Insert() {
|
|
|
|
override val requestType: String = "REPLACE INTO"
|
|
|
|
}
|
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
open class Insert : Query.Builder {
|
2019-05-06 19:03:28 +02:00
|
|
|
override val requestType: String = "INSERT INTO"
|
|
|
|
override lateinit var table: String
|
|
|
|
|
|
|
|
private val keys get() = _values.keys.joinToString(",")
|
2019-06-06 09:39:24 +02:00
|
|
|
private val values get() = _values.values.joinToString(",") {
|
2019-06-07 10:05:54 +02:00
|
|
|
when (it) {
|
|
|
|
is Boolean -> if (it) "1" else "0"
|
|
|
|
is Number -> it.toString()
|
|
|
|
else -> "\"$it\""
|
|
|
|
}
|
2019-06-06 09:39:24 +02:00
|
|
|
}
|
|
|
|
private var _values: Map<String, Any> = mapOf()
|
2019-05-06 19:03:28 +02:00
|
|
|
|
2019-06-06 09:39:24 +02:00
|
|
|
fun values(vararg pairs: Pair<String, Any>) {
|
2019-05-06 19:03:28 +02:00
|
|
|
_values = pairs.toMap()
|
|
|
|
}
|
|
|
|
|
2019-06-06 09:39:24 +02:00
|
|
|
fun values(values: Map<String, Any>) {
|
2019-05-06 19:03:28 +02:00
|
|
|
_values = values
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun toString(): String {
|
2019-06-06 09:39:24 +02:00
|
|
|
return listOf(requestType, table, "($keys) VALUES($values)").joinToString(" ")
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Condition {
|
|
|
|
|
|
|
|
private val conditionWord = "WHERE %s"
|
|
|
|
private var condition: String = ""
|
|
|
|
|
2019-05-12 18:34:28 +02:00
|
|
|
fun equals(field: String, value: Any) {
|
|
|
|
condition = when (value) {
|
|
|
|
is String -> "$field=\"$value\""
|
|
|
|
else -> "$field=$value"
|
|
|
|
}
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun greaterThan(field: String, value: String) {
|
|
|
|
condition = "$field > $value"
|
|
|
|
}
|
|
|
|
|
|
|
|
fun lessThan(field: String, value: String) {
|
|
|
|
condition = "$field < $value"
|
|
|
|
}
|
|
|
|
|
|
|
|
fun greaterOrEqualTo(field: String, value: String) {
|
|
|
|
condition = "$field >= $value"
|
|
|
|
}
|
|
|
|
|
|
|
|
fun lessOrEqualTo(field: String, value: String) {
|
|
|
|
condition = "$field <= $value"
|
|
|
|
}
|
|
|
|
|
|
|
|
fun and(builder: Condition.() -> Unit) {
|
2019-05-23 19:17:41 +02:00
|
|
|
condition = "($condition AND ${Condition().apply(builder).condition})"
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun or(builder: Condition.() -> Unit) {
|
2019-05-23 19:17:41 +02:00
|
|
|
condition = "($condition OR ${Condition().apply(builder).condition})"
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun toString(): String {
|
|
|
|
return conditionWord.format(condition)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
object Order {
|
|
|
|
const val ASC = "ASC"
|
|
|
|
const val DESC = "DESC"
|
2019-05-06 19:03:28 +02:00
|
|
|
}
|
|
|
|
|
2019-11-11 21:46:02 +01:00
|
|
|
@StringDef(Order.ASC, Order.DESC)
|
2019-05-06 19:03:28 +02:00
|
|
|
@Retention(AnnotationRetention.SOURCE)
|
2019-11-11 21:46:02 +01:00
|
|
|
annotation class OrderStrict
|