1 /*
<lambda>null2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.sqlite.db.framework
17 
18 import android.annotation.SuppressLint
19 import android.content.ContentValues
20 import android.database.Cursor
21 import android.database.SQLException
22 import android.database.sqlite.SQLiteCursor
23 import android.database.sqlite.SQLiteCursorDriver
24 import android.database.sqlite.SQLiteDatabase
25 import android.database.sqlite.SQLiteQuery
26 import android.database.sqlite.SQLiteTransactionListener
27 import android.os.Build
28 import android.os.CancellationSignal
29 import android.text.TextUtils
30 import android.util.Pair
31 import androidx.annotation.RequiresApi
32 import androidx.sqlite.db.SimpleSQLiteQuery
33 import androidx.sqlite.db.SupportSQLiteDatabase
34 import androidx.sqlite.db.SupportSQLiteQuery
35 import androidx.sqlite.db.SupportSQLiteStatement
36 import java.io.IOException
37 import java.util.Locale
38 
39 /**
40  * Delegates all calls to an implementation of [SQLiteDatabase].
41  *
42  * @param delegate The delegate to receive all calls.
43  * @constructor Creates a wrapper around [SQLiteDatabase].
44  */
45 internal class FrameworkSQLiteDatabase(private val delegate: SQLiteDatabase) :
46     SupportSQLiteDatabase {
47     override fun compileStatement(sql: String): SupportSQLiteStatement {
48         return FrameworkSQLiteStatement(delegate.compileStatement(sql))
49     }
50 
51     override fun beginTransaction() {
52         delegate.beginTransaction()
53     }
54 
55     override fun beginTransactionNonExclusive() {
56         delegate.beginTransactionNonExclusive()
57     }
58 
59     override fun beginTransactionReadOnly() {
60         internalBeginTransactionWithListenerReadOnly(null)
61     }
62 
63     override fun beginTransactionWithListener(transactionListener: SQLiteTransactionListener) {
64         delegate.beginTransactionWithListener(transactionListener)
65     }
66 
67     override fun beginTransactionWithListenerNonExclusive(
68         transactionListener: SQLiteTransactionListener
69     ) {
70         delegate.beginTransactionWithListenerNonExclusive(transactionListener)
71     }
72 
73     override fun beginTransactionWithListenerReadOnly(
74         transactionListener: SQLiteTransactionListener
75     ) {
76         internalBeginTransactionWithListenerReadOnly(transactionListener)
77     }
78 
79     // TODO(b/288918056): Use Android V API once it is available and SDK check the reflection call.
80     @SuppressLint("BanUncheckedReflection")
81     private fun internalBeginTransactionWithListenerReadOnly(
82         transactionListener: SQLiteTransactionListener?
83     ) {
84         if (beginTransactionMethod != null && getThreadSessionMethod != null) {
85             beginTransactionMethod!!.invoke(
86                 checkNotNull(getThreadSessionMethod!!.invoke(delegate)),
87                 0 /* SQLiteSession.TRANSACTION_MODE_DEFERRED */,
88                 transactionListener,
89                 0 /* connectionFlags */,
90                 null /* cancellationSignal */
91             )
92         } else if (transactionListener != null) {
93             beginTransactionWithListener(transactionListener)
94         } else {
95             beginTransaction()
96         }
97     }
98 
99     override fun endTransaction() {
100         delegate.endTransaction()
101     }
102 
103     override fun setTransactionSuccessful() {
104         delegate.setTransactionSuccessful()
105     }
106 
107     override fun inTransaction(): Boolean {
108         return delegate.inTransaction()
109     }
110 
111     override val isDbLockedByCurrentThread: Boolean
112         get() = delegate.isDbLockedByCurrentThread
113 
114     override fun yieldIfContendedSafely(): Boolean {
115         return delegate.yieldIfContendedSafely()
116     }
117 
118     override fun yieldIfContendedSafely(sleepAfterYieldDelayMillis: Long): Boolean {
119         return delegate.yieldIfContendedSafely(sleepAfterYieldDelayMillis)
120     }
121 
122     override var version: Int
123         get() = delegate.version
124         set(value) {
125             delegate.version = value
126         }
127 
128     override var maximumSize: Long
129         get() = delegate.maximumSize
130         set(numBytes) {
131             delegate.maximumSize = numBytes
132         }
133 
134     override fun setMaximumSize(numBytes: Long): Long {
135         delegate.maximumSize = numBytes
136         return delegate.maximumSize
137     }
138 
139     override val isExecPerConnectionSQLSupported: Boolean
140         get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
141 
142     override fun execPerConnectionSQL(sql: String, bindArgs: Array<out Any?>?) {
143         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
144             Api30Impl.execPerConnectionSQL(delegate, sql, bindArgs)
145         } else {
146             throw UnsupportedOperationException(
147                 "execPerConnectionSQL is not supported on a " +
148                     "SDK version lower than 30, current version is: " +
149                     Build.VERSION.SDK_INT
150             )
151         }
152     }
153 
154     override var pageSize: Long
155         get() = delegate.pageSize
156         set(numBytes) {
157             delegate.pageSize = numBytes
158         }
159 
160     override fun query(query: String): Cursor {
161         return query(SimpleSQLiteQuery(query))
162     }
163 
164     override fun query(query: String, bindArgs: Array<out Any?>): Cursor {
165         return query(SimpleSQLiteQuery(query, bindArgs))
166     }
167 
168     override fun query(query: SupportSQLiteQuery): Cursor {
169         val cursorFactory =
170             {
171                 _: SQLiteDatabase?,
172                 masterQuery: SQLiteCursorDriver?,
173                 editTable: String?,
174                 sqLiteQuery: SQLiteQuery? ->
175                 query.bindTo(FrameworkSQLiteProgram(sqLiteQuery!!))
176                 SQLiteCursor(masterQuery, editTable, sqLiteQuery)
177             }
178 
179         return delegate.rawQueryWithFactory(cursorFactory, query.sql, EMPTY_STRING_ARRAY, null)
180     }
181 
182     override fun query(query: SupportSQLiteQuery, cancellationSignal: CancellationSignal?): Cursor {
183         return delegate.rawQueryWithFactory(
184             {
185                 _: SQLiteDatabase?,
186                 masterQuery: SQLiteCursorDriver?,
187                 editTable: String?,
188                 sqLiteQuery: SQLiteQuery? ->
189                 query.bindTo(FrameworkSQLiteProgram(sqLiteQuery!!))
190                 SQLiteCursor(masterQuery, editTable, sqLiteQuery)
191             },
192             query.sql,
193             EMPTY_STRING_ARRAY,
194             null,
195             cancellationSignal!!
196         )
197     }
198 
199     @Throws(SQLException::class)
200     override fun insert(table: String, conflictAlgorithm: Int, values: ContentValues): Long {
201         return delegate.insertWithOnConflict(table, null, values, conflictAlgorithm)
202     }
203 
204     override fun delete(table: String, whereClause: String?, whereArgs: Array<out Any?>?): Int {
205         val query = buildString {
206             append("DELETE FROM ")
207             append(table)
208             if (!whereClause.isNullOrEmpty()) {
209                 append(" WHERE ")
210                 append(whereClause)
211             }
212         }
213         val statement = compileStatement(query)
214         SimpleSQLiteQuery.bind(statement, whereArgs)
215         return statement.executeUpdateDelete()
216     }
217 
218     override fun update(
219         table: String,
220         conflictAlgorithm: Int,
221         values: ContentValues,
222         whereClause: String?,
223         whereArgs: Array<out Any?>?
224     ): Int {
225         // taken from SQLiteDatabase class.
226         require(values.size() != 0) { "Empty values" }
227 
228         // move all bind args to one array
229         val setValuesSize = values.size()
230         val bindArgsSize = if (whereArgs == null) setValuesSize else setValuesSize + whereArgs.size
231         val bindArgs = arrayOfNulls<Any>(bindArgsSize)
232         val sql = buildString {
233             append("UPDATE ")
234             append(CONFLICT_VALUES[conflictAlgorithm])
235             append(table)
236             append(" SET ")
237 
238             var i = 0
239             for (colName in values.keySet()) {
240                 append(if (i > 0) "," else "")
241                 append(colName)
242                 bindArgs[i++] = values[colName]
243                 append("=?")
244             }
245             if (whereArgs != null) {
246                 i = setValuesSize
247                 while (i < bindArgsSize) {
248                     bindArgs[i] = whereArgs[i - setValuesSize]
249                     i++
250                 }
251             }
252             if (!TextUtils.isEmpty(whereClause)) {
253                 append(" WHERE ")
254                 append(whereClause)
255             }
256         }
257         val stmt = compileStatement(sql)
258         SimpleSQLiteQuery.bind(stmt, bindArgs)
259         return stmt.executeUpdateDelete()
260     }
261 
262     @Throws(SQLException::class)
263     override fun execSQL(sql: String) {
264         delegate.execSQL(sql)
265     }
266 
267     @Throws(SQLException::class)
268     override fun execSQL(sql: String, bindArgs: Array<out Any?>) {
269         delegate.execSQL(sql, bindArgs)
270     }
271 
272     override val isReadOnly: Boolean
273         get() = delegate.isReadOnly
274 
275     override val isOpen: Boolean
276         get() = delegate.isOpen
277 
278     override fun needUpgrade(newVersion: Int): Boolean {
279         return delegate.needUpgrade(newVersion)
280     }
281 
282     override val path: String?
283         get() = delegate.path
284 
285     override fun setLocale(locale: Locale) {
286         delegate.setLocale(locale)
287     }
288 
289     override fun setMaxSqlCacheSize(cacheSize: Int) {
290         delegate.setMaxSqlCacheSize(cacheSize)
291     }
292 
293     override fun setForeignKeyConstraintsEnabled(enabled: Boolean) {
294         delegate.setForeignKeyConstraintsEnabled(enabled)
295     }
296 
297     override fun enableWriteAheadLogging(): Boolean {
298         return delegate.enableWriteAheadLogging()
299     }
300 
301     override fun disableWriteAheadLogging() {
302         delegate.disableWriteAheadLogging()
303     }
304 
305     override val isWriteAheadLoggingEnabled: Boolean
306         get() = delegate.isWriteAheadLoggingEnabled
307 
308     override val attachedDbs: List<Pair<String, String>>?
309         get() = delegate.attachedDbs
310 
311     override val isDatabaseIntegrityOk: Boolean
312         get() = delegate.isDatabaseIntegrityOk
313 
314     @Throws(IOException::class)
315     override fun close() {
316         delegate.close()
317     }
318 
319     /** Checks if this object delegates to the same given database reference. */
320     fun isDelegate(sqLiteDatabase: SQLiteDatabase): Boolean {
321         return delegate == sqLiteDatabase
322     }
323 
324     @RequiresApi(30)
325     internal object Api30Impl {
326         fun execPerConnectionSQL(
327             sQLiteDatabase: SQLiteDatabase,
328             sql: String,
329             bindArgs: Array<out Any?>?
330         ) {
331             sQLiteDatabase.execPerConnectionSQL(sql, bindArgs)
332         }
333     }
334 
335     companion object {
336         private val CONFLICT_VALUES =
337             arrayOf("", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE ")
338         private val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)
339 
340         private val getThreadSessionMethod by
341             lazy(LazyThreadSafetyMode.NONE) {
342                 try {
343                     SQLiteDatabase::class.java.getDeclaredMethod("getThreadSession").apply {
344                         isAccessible = true
345                     }
346                 } catch (t: Throwable) {
347                     null
348                 }
349             }
350 
351         private val beginTransactionMethod by
352             lazy(LazyThreadSafetyMode.NONE) {
353                 try {
354                     getThreadSessionMethod
355                         ?.returnType
356                         ?.getDeclaredMethod(
357                             "beginTransaction",
358                             Int::class.java,
359                             SQLiteTransactionListener::class.java,
360                             Int::class.java,
361                             CancellationSignal::class.java
362                         )
363                 } catch (t: Throwable) {
364                     null
365                 }
366             }
367     }
368 }
369