-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQueryPlanExplainer.kt
155 lines (130 loc) · 5.14 KB
/
QueryPlanExplainer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package com.tazkiyatech.utils.sqlite
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import java.util.*
/**
* Helper class for building up a [List] of [QueryPlanRow] objects
* that explain the strategy or plan that SQLite will use to implement a specific SQL query.
*
* @property database the [SQLiteDatabase] object against which to run the "EXPLAIN QUERY PLAN" command.
*/
class QueryPlanExplainer(private val database: SQLiteDatabase) {
/**
* Composes and executes an "EXECUTE QUERY PLAN" command for the SQLite query provided.
*
* @param sql the SQLite SELECT/UPDATE/etc statement for which to run the "EXPLAIN QUERY PLAN" query.
* @return the result of the "EXPLAIN QUERY PLAN" command.
*/
fun explainQueryPlanForSqlStatement(sql: String): List<QueryPlanRow> {
val explainQueryPlanStatement = "EXPLAIN QUERY PLAN $sql"
return executeExplainQueryPlanStatement(explainQueryPlanStatement, null)
}
/**
* Composes and executes an "EXECUTE QUERY PLAN" command
* for the SELECT query that would be composed from the parameters provided.
*
* @return the result of the "EXPLAIN QUERY PLAN" command.
* @see SQLiteDatabase.query
*/
fun explainQueryPlanForSelectStatement(table: String,
columns: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
groupBy: String?,
having: String?,
orderBy: String?,
limit: String?): List<QueryPlanRow> {
val sb = StringBuilder()
sb.append("EXPLAIN QUERY PLAN SELECT ")
if (columns == null || columns.isEmpty()) {
sb.append(" * ")
} else {
sb.append(columns.joinToString(", "))
}
sb.append(" FROM ")
sb.append(table)
if (!selection.isNullOrEmpty()) {
sb.append(" WHERE ")
sb.append(selection)
}
if (!groupBy.isNullOrEmpty()) {
sb.append(" GROUP BY ")
sb.append(groupBy)
}
if (!having.isNullOrEmpty()) {
sb.append(" HAVING ")
sb.append(having)
}
if (!orderBy.isNullOrEmpty()) {
sb.append(" ORDER BY ")
sb.append(orderBy)
}
if (!limit.isNullOrEmpty()) {
sb.append(" LIMIT ")
sb.append(limit)
}
return executeExplainQueryPlanStatement(sb.toString(), selectionArgs)
}
/**
* Composes and executes an "EXECUTE QUERY PLAN" command
* for the UPDATE query that would be composed from the parameters provided.
*
* @return the result of the "EXPLAIN QUERY PLAN" command.
* @see SQLiteDatabase.update
*/
fun explainQueryPlanForUpdateStatement(table: String,
contentValues: ContentValues,
selection: String?,
selectionArgs: Array<String>?): List<QueryPlanRow> {
val sb = StringBuilder()
sb.append("EXPLAIN QUERY PLAN UPDATE ")
sb.append(table)
sb.append(" SET ")
val keys = contentValues.keySet()
var firstKey = true
for (key in keys) {
if (!firstKey) {
sb.append(", ")
}
sb.append(key)
sb.append(" = ")
when (val value = contentValues[key]) {
null -> sb.append("NULL")
is Number -> sb.append(value)
is Boolean -> {
if (value) {
sb.append("1")
} else {
sb.append("0")
}
}
else -> sb.append("'$value'")
}
firstKey = false
}
if (!selection.isNullOrEmpty()) {
sb.append(" WHERE ")
sb.append(selection)
}
return executeExplainQueryPlanStatement(sb.toString(), selectionArgs)
}
/**
* Executes the sql command provided on the database contained within this class.
*
* @param sql the "EXPLAIN QUERY PLAN" command to call.
* @param selectionArgs the values to use in place of the ?s in the where clause of `sql`.
* @return the result of the "EXPLAIN QUERY PLAN" command.
*/
private fun executeExplainQueryPlanStatement(sql: String,
selectionArgs: Array<String>?): List<QueryPlanRow> {
database.rawQuery(sql, selectionArgs).use { cursor ->
val queryPlanRowList: MutableList<QueryPlanRow> = ArrayList()
while (cursor.moveToNext()) {
val detailColumnIndex = cursor.getColumnIndex("detail")
val detail = cursor.getString(detailColumnIndex)
queryPlanRowList.add(QueryPlanRow(detail))
}
return queryPlanRowList
}
}
}