Created
January 18, 2019 06:26
-
-
Save LanderlYoung/2f460dc4da624cd4ede92633043d89b1 to your computer and use it in GitHub Desktop.
This is a WCDB driver for sqldeight
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2018 Square, Inc. | |
* Copyright (C) 2019 [email protected] | |
* Copyright (C) 2019 [email protected] | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.tencent.hms.extension.wcdb | |
import android.content.Context | |
import android.util.LruCache | |
import com.squareup.sqldelight.Transacter | |
import com.squareup.sqldelight.db.SqlCursor | |
import com.squareup.sqldelight.db.SqlDriver | |
import com.squareup.sqldelight.db.SqlPreparedStatement | |
import com.tencent.wcdb.Cursor | |
import com.tencent.wcdb.DatabaseErrorHandler | |
import com.tencent.wcdb.database.* | |
/** | |
* ``` | |
* Author: [email protected] | |
* Date: 2019-01-18 | |
* Time: 10:53 | |
* Life with Passion, Code with Creativity. | |
* ``` | |
* | |
* This is a [WCDB](https://github.com/Tencent/wcdb) driver for [sqldeight](https://github.com/square/sqldelight), | |
* based on the [Standard Android implementation](https://github.com/square/sqldelight/blob/master/drivers/android-driver/src/main/java/com/squareup/sqldelight/android/AndroidSqliteDriver.kt) | |
* | |
* Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) | |
*/ | |
class WcdbSqlDriver private constructor( | |
private val openHelper: SQLiteOpenHelper? = null, | |
database: SQLiteDatabase? = null, | |
private val cacheSize: Int | |
) : SqlDriver { | |
companion object { | |
private const val DEFAULT_CACHE_SIZE = 20 | |
} | |
private val transactions = ThreadLocal<Transacter.Transaction>() | |
private val database = openHelper?.writableDatabase ?: database!! | |
constructor( | |
openHelper: SQLiteOpenHelper | |
) : this(openHelper = openHelper, database = null, cacheSize = DEFAULT_CACHE_SIZE) | |
/** | |
* @param [cacheSize] The number of compiled sqlite statements to keep in memory per connection. | |
* Defaults to 20. | |
*/ | |
@JvmOverloads | |
constructor( | |
schema: SqlDriver.Schema, | |
context: Context, | |
name: String? = null, | |
password: ByteArray, | |
cipher: SQLiteCipherSpec?, | |
errorHandler: DatabaseErrorHandler, | |
cacheSize: Int = DEFAULT_CACHE_SIZE | |
) : this( | |
database = null, | |
openHelper = object : SQLiteOpenHelper( | |
context, name, | |
password, cipher, | |
null, schema.version, errorHandler | |
) { | |
override fun onCreate(db: SQLiteDatabase?) { | |
schema.create(WcdbSqlDriver(openHelper = null, database = db, cacheSize = 1)) | |
} | |
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { | |
schema.migrate( | |
WcdbSqlDriver(openHelper = null, database = db, cacheSize = 1), | |
oldVersion, | |
newVersion | |
) | |
} | |
}, | |
cacheSize = cacheSize | |
) | |
@JvmOverloads | |
constructor( | |
database: SQLiteDatabase, | |
cacheSize: Int = DEFAULT_CACHE_SIZE | |
) : this(openHelper = null, database = database, cacheSize = cacheSize) | |
private val statements = object : LruCache<Int, WcdbStatement>(cacheSize) { | |
override fun entryRemoved( | |
evicted: Boolean, | |
key: Int, | |
oldValue: WcdbStatement, | |
newValue: WcdbStatement? | |
) { | |
if (evicted) oldValue.close() | |
} | |
} | |
override fun newTransaction(): Transacter.Transaction { | |
val enclosing = transactions.get() | |
val transaction = Transaction(enclosing) | |
transactions.set(transaction) | |
if (enclosing == null) { | |
database.beginTransactionNonExclusive() | |
} | |
return transaction | |
} | |
override fun currentTransaction(): Transacter.Transaction? = transactions.get() | |
inner class Transaction( | |
override val enclosingTransaction: Transacter.Transaction? | |
) : Transacter.Transaction() { | |
override fun endTransaction(successful: Boolean) { | |
if (enclosingTransaction == null) { | |
if (successful) { | |
database.setTransactionSuccessful() | |
database.endTransaction() | |
} else { | |
database.endTransaction() | |
} | |
} | |
transactions.set(enclosingTransaction) | |
} | |
} | |
private fun <T> execute( | |
identifier: Int?, | |
createStatement: () -> WcdbStatement, | |
binders: (SqlPreparedStatement.() -> Unit)?, | |
result: WcdbStatement.() -> T | |
): T { | |
var statement: WcdbStatement? = null | |
if (identifier != null) { | |
statement = statements.remove(identifier) | |
} | |
if (statement == null) { | |
statement = createStatement() | |
} | |
try { | |
if (binders != null) { | |
statement.binders() | |
} | |
return statement.result() | |
} finally { | |
if (identifier != null) statements.put(identifier, statement)?.close() | |
} | |
} | |
override fun execute( | |
identifier: Int?, | |
sql: String, | |
parameters: Int, | |
binders: (SqlPreparedStatement.() -> Unit)? | |
) = execute( | |
identifier, | |
{ WcdbPreparedStatement(database.compileStatement(sql)) }, | |
binders, | |
WcdbStatement::execute | |
) | |
override fun executeQuery( | |
identifier: Int?, | |
sql: String, | |
parameters: Int, | |
binders: (SqlPreparedStatement.() -> Unit)? | |
) = execute(identifier, { WcdbQuery(sql, database, parameters) }, binders, WcdbStatement::executeQuery) | |
override fun close() { | |
if (openHelper == null) { | |
throw IllegalStateException("Tried to call close during initialization") | |
} | |
statements.evictAll() | |
return openHelper.close() | |
} | |
} | |
private interface WcdbStatement : SqlPreparedStatement { | |
fun execute() | |
fun executeQuery(): SqlCursor | |
fun close() | |
} | |
private class WcdbPreparedStatement( | |
private val statement: SQLiteStatement | |
) : WcdbStatement { | |
override fun bindBytes(index: Int, value: ByteArray?) { | |
if (value == null) statement.bindNull(index) else statement.bindBlob(index, value) | |
} | |
override fun bindLong(index: Int, value: Long?) { | |
if (value == null) statement.bindNull(index) else statement.bindLong(index, value) | |
} | |
override fun bindDouble(index: Int, value: Double?) { | |
if (value == null) statement.bindNull(index) else statement.bindDouble(index, value) | |
} | |
override fun bindString(index: Int, value: String?) { | |
if (value == null) statement.bindNull(index) else statement.bindString(index, value) | |
} | |
override fun executeQuery() = throw UnsupportedOperationException() | |
override fun execute() { | |
statement.execute() | |
} | |
override fun close() { | |
statement.close() | |
} | |
} | |
private class WcdbQuery( | |
private val sql: String, | |
private val database: SQLiteDatabase, | |
private val argCount: Int | |
) : WcdbStatement { | |
private val binds: MutableMap<Int, (SQLiteProgram) -> Unit> = LinkedHashMap() | |
override fun bindBytes(index: Int, value: ByteArray?) { | |
binds[index] = { if (value == null) it.bindNull(index) else it.bindBlob(index, value) } | |
} | |
override fun bindLong(index: Int, value: Long?) { | |
binds[index] = { if (value == null) it.bindNull(index) else it.bindLong(index, value) } | |
} | |
override fun bindDouble(index: Int, value: Double?) { | |
binds[index] = { if (value == null) it.bindNull(index) else it.bindDouble(index, value) } | |
} | |
override fun bindString(index: Int, value: String?) { | |
binds[index] = { if (value == null) it.bindNull(index) else it.bindString(index, value) } | |
} | |
override fun execute() = throw UnsupportedOperationException() | |
override fun executeQuery() = WcdbCursor( | |
database.rawQueryWithFactory( | |
object : SQLiteDatabase.CursorFactory by SQLiteCursor.FACTORY { | |
override fun newCursor( | |
db: SQLiteDatabase, | |
masterQuery: SQLiteCursorDriver, | |
editTable: String?, | |
query: SQLiteProgram | |
): Cursor { | |
bindTo(query) | |
return SQLiteCursor.FACTORY.newCursor(db, masterQuery, editTable, query) | |
} | |
}, sql, emptyArray(), null | |
) | |
) | |
private fun bindTo(statement: SQLiteProgram) { | |
for (action in binds.values) { | |
action(statement) | |
} | |
} | |
override fun toString() = sql | |
override fun close() {} | |
} | |
private class WcdbCursor( | |
private val cursor: Cursor | |
) : SqlCursor { | |
override fun next() = 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 close() = cursor.close() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment