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