Skip to content

Commit 1226747

Browse files
committed
starting point
1 parent 1d88c21 commit 1226747

File tree

4 files changed

+374
-2
lines changed

4 files changed

+374
-2
lines changed

SQLiter/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ kotlin {
6363
dependencies {
6464
implementation "co.touchlab:stately:$STATELY_VERSION"
6565
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
66+
implementation 'com.squareup.sqldelight:runtime:1.0.0-rc4'
6667
}
6768
}
6869
commonTest {

SQLiter/gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
kotlin.code.style=official
1818

1919
GROUP=co.touchlab
20-
VERSION_NAME=0.5.0-SNAPSHOT
20+
VERSION_NAME=0.5.0
2121

22-
STATELY_VERSION=0.4.0-SNAPSHOT
22+
STATELY_VERSION=0.4.0
2323
KOTLIN_VERSION=1.3.10
2424
DOKKA_VERSION=0.9.17
2525

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/*
2+
* Copyright (C) 2018 Touchlab, Inc.
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+
17+
package co.touchlab.sqliter.sqldelight
18+
19+
import co.touchlab.sqliter.DatabaseConnection
20+
import co.touchlab.sqliter.DatabaseManager
21+
import co.touchlab.sqliter.Statement
22+
import co.touchlab.sqliter.bindBlob
23+
import co.touchlab.sqliter.bindString
24+
import co.touchlab.sqliter.bindDouble
25+
import co.touchlab.sqliter.bindLong
26+
import co.touchlab.sqliter.getBytesOrNull
27+
import co.touchlab.sqliter.getDoubleOrNull
28+
import co.touchlab.sqliter.getLongOrNull
29+
import co.touchlab.sqliter.getStringOrNull
30+
import co.touchlab.stately.collections.frozenHashMap
31+
import co.touchlab.stately.collections.frozenLinkedList
32+
import co.touchlab.stately.concurrency.AtomicBoolean
33+
import co.touchlab.stately.concurrency.AtomicReference
34+
import co.touchlab.stately.concurrency.QuickLock
35+
import co.touchlab.stately.concurrency.withLock
36+
import co.touchlab.stately.freeze
37+
import com.squareup.sqldelight.Transacter
38+
import com.squareup.sqldelight.db.SqlDatabase
39+
import com.squareup.sqldelight.db.SqlDatabaseConnection
40+
import com.squareup.sqldelight.db.SqlPreparedStatement
41+
import com.squareup.sqldelight.db.SqlPreparedStatement.Type.DELETE
42+
import com.squareup.sqldelight.db.SqlPreparedStatement.Type.EXECUTE
43+
import com.squareup.sqldelight.db.SqlPreparedStatement.Type.INSERT
44+
import com.squareup.sqldelight.db.SqlPreparedStatement.Type.SELECT
45+
import com.squareup.sqldelight.db.SqlPreparedStatement.Type.UPDATE
46+
import com.squareup.sqldelight.db.SqlCursor
47+
48+
class SqlLighterDriver(
49+
databaseManager: DatabaseManager
50+
) : SqlDatabase {
51+
private val connection = SQLiterConnection(databaseManager.createConnection())
52+
private val enforceClosed = EnforceClosed()
53+
54+
override fun close() {
55+
enforceClosed.checkNotClosed()
56+
enforceClosed.trackClosed()
57+
connection.close()
58+
}
59+
60+
override fun getConnection(): SqlDatabaseConnection {
61+
enforceClosed.checkNotClosed()
62+
return connection
63+
}
64+
}
65+
66+
/**
67+
* Wrap native database connection with SqlDatabaseConnection and
68+
* properly handle closing resources.
69+
*/
70+
fun wrapConnection(
71+
connection: DatabaseConnection,
72+
block: (SqlDatabaseConnection) -> Unit
73+
) {
74+
val conn = SQLiterConnection(connection)
75+
try {
76+
block(conn)
77+
} finally {
78+
conn.close(closeDbConnection = false)
79+
}
80+
}
81+
82+
class SQLiterConnection(
83+
private val databaseConnection: DatabaseConnection
84+
) : SqlDatabaseConnection {
85+
private val enforceClosed = EnforceClosed()
86+
private val transaction: AtomicReference<Transaction?> = AtomicReference(null)
87+
private val transLock = QuickLock()
88+
private val statementList = frozenLinkedList<Statement>(stableIterator = false)
89+
private val queryList = frozenLinkedList<SQLiterQuery>(stableIterator = false)
90+
91+
override fun currentTransaction(): Transacter.Transaction? = transaction.value
92+
93+
override fun newTransaction(): Transacter.Transaction =
94+
transLock.withLock {
95+
val enclosing = transaction.value
96+
val trans = Transaction(enclosing).freeze()
97+
transaction.value = trans
98+
99+
if (enclosing == null) {
100+
databaseConnection.beginTransaction()
101+
}
102+
103+
return trans
104+
}
105+
106+
override fun prepareStatement(
107+
sql: String,
108+
type: SqlPreparedStatement.Type,
109+
parameters: Int
110+
): SqlPreparedStatement {
111+
enforceClosed.checkNotClosed()
112+
113+
return when (type) {
114+
SELECT -> {
115+
SQLiterQuery(sql, databaseConnection).apply { queryList.add(this) }
116+
}
117+
INSERT, UPDATE, DELETE, EXECUTE -> {
118+
val statement = databaseConnection.createStatement(sql)
119+
statementList.add(statement)
120+
SQLiterStatement(statement)
121+
}
122+
}
123+
}
124+
125+
internal fun close(closeDbConnection: Boolean = true) {
126+
enforceClosed.checkNotClosed()
127+
enforceClosed.trackClosed()
128+
statementList.forEach { it.finalizeStatement() }
129+
queryList.forEach { it.close() }
130+
if (closeDbConnection) {
131+
databaseConnection.close()
132+
}
133+
}
134+
135+
inner class Transaction(
136+
override val enclosingTransaction: Transaction?
137+
) : Transacter.Transaction() {
138+
139+
override fun endTransaction(successful: Boolean) = transLock.withLock {
140+
if (enclosingTransaction == null) {
141+
if (successful) {
142+
databaseConnection.setTransactionSuccessful()
143+
databaseConnection.endTransaction()
144+
} else {
145+
databaseConnection.endTransaction()
146+
}
147+
}
148+
transaction.value = enclosingTransaction
149+
}
150+
}
151+
}
152+
153+
private class SQLiterQuery(
154+
private val sql: String,
155+
private val database: DatabaseConnection
156+
) : SqlPreparedStatement {
157+
private val availableStatements = frozenLinkedList<Statement>(stableIterator = false)
158+
private val allStatements = frozenLinkedList<Statement>(stableIterator = false)
159+
private val enforceClosed = EnforceClosed()
160+
private val queryLock = QuickLock()
161+
private val binds = frozenHashMap<Int, (Statement) -> Unit>()
162+
163+
internal fun close() {
164+
queryLock.withLock {
165+
enforceClosed.checkNotClosed()
166+
enforceClosed.trackClosed()
167+
allStatements.forEach {
168+
it.finalizeStatement()
169+
}
170+
}
171+
}
172+
173+
override fun bindBytes(
174+
index: Int,
175+
bytes: ByteArray?
176+
) {
177+
bytes.freeze()
178+
binds.put(index) { it.bindBlob(index, bytes) }
179+
}
180+
181+
override fun bindDouble(
182+
index: Int,
183+
double: Double?
184+
) {
185+
binds.put(index) { it.bindDouble(index, double) }
186+
}
187+
188+
override fun bindLong(
189+
index: Int,
190+
long: Long?
191+
) {
192+
binds.put(index) { it.bindLong(index, long) }
193+
}
194+
195+
override fun bindString(
196+
index: Int,
197+
string: String?
198+
) {
199+
binds.put(index) { it.bindString(index, string) }
200+
}
201+
202+
private fun bindTo(statement: Statement) {
203+
for (bind in binds.values.iterator()) {
204+
bind(statement)
205+
}
206+
}
207+
208+
override fun execute() = throw UnsupportedOperationException()
209+
210+
private fun findStatement(): Statement = queryLock.withLock {
211+
enforceClosed.checkNotClosed()
212+
return if (availableStatements.size == 0) {
213+
val statement = database.createStatement(sql)
214+
allStatements.add(statement)
215+
statement
216+
} else {
217+
availableStatements.removeAt(0)
218+
}
219+
}
220+
221+
internal fun cacheStatement(statement: Statement) {
222+
queryLock.withLock {
223+
enforceClosed.checkNotClosed()
224+
statement.resetStatement()
225+
availableStatements.add(statement)
226+
}
227+
}
228+
229+
override fun executeQuery(): SqlCursor {
230+
val stmt = findStatement()
231+
bindTo(stmt)
232+
return SQLiterCursor(stmt, this)
233+
}
234+
}
235+
236+
private class SQLiterStatement(
237+
private val statement: Statement
238+
) : SqlPreparedStatement {
239+
240+
override fun bindBytes(
241+
index: Int,
242+
bytes: ByteArray?
243+
) {
244+
bytes.freeze()
245+
statement.bindBlob(index, bytes)
246+
}
247+
248+
override fun bindDouble(
249+
index: Int,
250+
double: Double?
251+
) {
252+
statement.bindDouble(index, double)
253+
}
254+
255+
override fun bindLong(
256+
index: Int,
257+
long: Long?
258+
) {
259+
statement.bindLong(index, long)
260+
}
261+
262+
override fun bindString(
263+
index: Int,
264+
string: String?
265+
) {
266+
statement.bindString(index, string)
267+
}
268+
269+
override fun execute() {
270+
statement.execute()
271+
}
272+
273+
override fun executeQuery(): SqlCursor = throw UnsupportedOperationException()
274+
}
275+
276+
private class SQLiterCursor(
277+
private val statement: Statement,
278+
private val query: SQLiterQuery
279+
) : SqlCursor {
280+
private val cursor = statement.query()
281+
282+
override fun close() {
283+
query.cacheStatement(statement)
284+
}
285+
286+
override fun getBytes(index: Int): ByteArray? = cursor.getBytesOrNull(index)
287+
288+
override fun getDouble(index: Int): Double? = cursor.getDoubleOrNull(index)
289+
290+
override fun getLong(index: Int): Long? = cursor.getLongOrNull(index)
291+
292+
override fun getString(index: Int): String? = cursor.getStringOrNull(index)
293+
294+
override fun next(): Boolean = cursor.next()
295+
}
296+
297+
private class EnforceClosed {
298+
private val closed = AtomicBoolean(false)
299+
300+
fun trackClosed() {
301+
closed.value = true
302+
}
303+
304+
fun checkNotClosed() {
305+
if (closed.value)
306+
throw IllegalStateException("Closed")
307+
}
308+
}

0 commit comments

Comments
 (0)