Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a windowSizeBytes parameter to AndroidSqliteDriver #4804

Merged
merged 2 commits into from
Nov 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package app.cash.sqldelight.driver.android

import android.content.Context
import android.database.AbstractWindowedCursor
import android.database.Cursor
import android.database.CursorWindow
import android.os.Build
import android.util.LruCache
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.db.SupportSQLiteOpenHelper
import androidx.sqlite.db.SupportSQLiteProgram
Expand All @@ -17,13 +22,15 @@ import app.cash.sqldelight.db.SqlCursor
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlPreparedStatement
import app.cash.sqldelight.db.SqlSchema
import app.cash.sqldelight.driver.android.Api28Impl.setWindowSize

private const val DEFAULT_CACHE_SIZE = 20

class AndroidSqliteDriver private constructor(
private val openHelper: SupportSQLiteOpenHelper? = null,
database: SupportSQLiteDatabase? = null,
private val cacheSize: Int,
private val windowSizeBytes: Long? = null,
) : SqlDriver {
init {
require((openHelper != null) xor (database != null))
Expand All @@ -36,21 +43,24 @@ class AndroidSqliteDriver private constructor(

constructor(
openHelper: SupportSQLiteOpenHelper,
) : this(openHelper = openHelper, database = null, cacheSize = DEFAULT_CACHE_SIZE)
) : this(openHelper = openHelper, database = null, cacheSize = DEFAULT_CACHE_SIZE, windowSizeBytes = null)

/**
* @param [cacheSize] The number of compiled sqlite statements to keep in memory per connection.
* Defaults to 20.
* @param [useNoBackupDirectory] Sets whether to use a no backup directory or not.
* @param [windowSizeBytes] Size of cursor window in bytes, per [CursorWindow] (Android 28+ only), or null to use the default.
*/
@JvmOverloads constructor(
@JvmOverloads
constructor(
schema: SqlSchema<QueryResult.Value<Unit>>,
context: Context,
name: String? = null,
factory: SupportSQLiteOpenHelper.Factory = FrameworkSQLiteOpenHelperFactory(),
callback: SupportSQLiteOpenHelper.Callback = AndroidSqliteDriver.Callback(schema),
cacheSize: Int = DEFAULT_CACHE_SIZE,
useNoBackupDirectory: Boolean = false,
windowSizeBytes: Long? = null,
) : this(
database = null,
openHelper = factory.create(
Expand All @@ -61,12 +71,15 @@ class AndroidSqliteDriver private constructor(
.build(),
),
cacheSize = cacheSize,
windowSizeBytes = windowSizeBytes,
)

@JvmOverloads constructor(
@JvmOverloads
constructor(
database: SupportSQLiteDatabase,
cacheSize: Int = DEFAULT_CACHE_SIZE,
) : this(openHelper = null, database = database, cacheSize = cacheSize)
windowSizeBytes: Long? = null,
) : this(openHelper = null, database = database, cacheSize = cacheSize, windowSizeBytes = windowSizeBytes)

private val statements = object : LruCache<Int, AndroidStatement>(cacheSize) {
override fun entryRemoved(
Expand Down Expand Up @@ -176,7 +189,7 @@ class AndroidSqliteDriver private constructor(
mapper: (SqlCursor) -> QueryResult<R>,
parameters: Int,
binders: (SqlPreparedStatement.() -> Unit)?,
) = execute(identifier, { AndroidQuery(sql, database, parameters) }, binders) { executeQuery(mapper) }
) = execute(identifier, { AndroidQuery(sql, database, parameters, windowSizeBytes) }, binders) { executeQuery(mapper) }

override fun close() {
statements.evictAll()
Expand Down Expand Up @@ -257,6 +270,7 @@ private class AndroidQuery(
override val sql: String,
private val database: SupportSQLiteDatabase,
override val argCount: Int,
private val windowSizeBytes: Long?,
) : SupportSQLiteQuery, AndroidStatement {
private val binds = MutableList<((SupportSQLiteProgram) -> Unit)?>(argCount) { null }

Expand Down Expand Up @@ -290,7 +304,7 @@ private class AndroidQuery(

override fun <R> executeQuery(mapper: (SqlCursor) -> QueryResult<R>): R {
return database.query(this)
.use { cursor -> mapper(AndroidCursor(cursor)).value }
.use { cursor -> mapper(AndroidCursor(cursor, windowSizeBytes)).value }
}

override fun bindTo(statement: SupportSQLiteProgram) {
Expand All @@ -301,16 +315,36 @@ private class AndroidQuery(

override fun toString() = sql

override fun close() { }
override fun close() {}
}

private class AndroidCursor(
private val cursor: Cursor,
windowSizeBytes: Long?,
) : SqlCursor {
init {
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
windowSizeBytes != null &&
cursor is AbstractWindowedCursor
) {
cursor.setWindowSize(windowSizeBytes)
}
}

override fun next(): QueryResult.Value<Boolean> = QueryResult.Value(cursor.moveToNext())
override fun getString(index: Int) = if (cursor.isNull(index)) null else cursor.getString(index)
override fun getLong(index: Int) = if (cursor.isNull(index)) null else cursor.getLong(index)
override fun getBytes(index: Int) = if (cursor.isNull(index)) null else cursor.getBlob(index)
override fun getDouble(index: Int) = if (cursor.isNull(index)) null else cursor.getDouble(index)
override fun getBoolean(index: Int) = if (cursor.isNull(index)) null else cursor.getLong(index) == 1L
}

@RequiresApi(Build.VERSION_CODES.P)
private object Api28Impl {
@JvmStatic
@DoNotInline
fun AbstractWindowedCursor.setWindowSize(windowSizeBytes: Long) {
window = CursorWindow(null, windowSizeBytes)
}
}