From 5fbf0147f4239d7f7801014389f70d0883160cdf Mon Sep 17 00:00:00 2001 From: qiaoyuang <qiaoyuang2012@gmail.com> Date: Tue, 23 Apr 2024 14:39:47 +0800 Subject: [PATCH 1/5] Fix bugs about null values and String elements --- CHANGELOG.md | 7 +++++ .../com/ctrip/sqllin/driver/AndroidCursor.kt | 31 ++++++++++++++++--- .../com/ctrip/sqllin/driver/CommonCursor.kt | 8 ++--- .../com/ctrip/sqllin/driver/Extension.kt | 2 +- .../com/ctrip/sqllin/driver/JdbcCursor.kt | 12 ++++--- .../sqllin/driver/ConcurrentStatement.kt | 8 ++--- .../com/ctrip/sqllin/driver/NativeCursor.kt | 12 +++---- .../ctrip/sqllin/driver/SQLiteStatement.kt | 8 ++--- .../sqllin/driver/cinterop/NativeStatement.kt | 29 +++++++++-------- .../dsl/sql/compiler/AbstractValuesEncoder.kt | 18 +++++++++-- .../sqllin/dsl/sql/compiler/QueryDecoder.kt | 27 +++++++++------- .../ctrip/sqllin/dsl/sql/operation/Insert.kt | 2 +- .../dsl/sql/statement/OtherStatement.kt | 2 +- .../com/ctrip/sqllin/dsl/CommonBasicTest.kt | 1 - 14 files changed, 111 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bbf68f..0831543 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ - Date format: YYYY-MM-dd +## v1.3.1 / 2024-04-xx + +### sqllin-dsl + +* Fix a crash when a data class doesn't contain any `String` element. +* Fix the [issue#81](https://github.com/ctripcorp/SQLlin/issues/81) about insert and query null values + ## v1.3.0 / 2024-04-21 ### All diff --git a/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt b/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt index 0a2c2a5..e60af32 100644 --- a/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt +++ b/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt @@ -25,10 +25,33 @@ import android.database.Cursor internal class AndroidCursor(private val cursor: Cursor) : CommonCursor { - override fun getInt(columnIndex: Int): Int = cursor.getInt(columnIndex) - override fun getLong(columnIndex: Int): Long = cursor.getLong(columnIndex) - override fun getFloat(columnIndex: Int): Float = cursor.getFloat(columnIndex) - override fun getDouble(columnIndex: Int): Double = cursor.getDouble(columnIndex) + override fun getInt(columnIndex: Int): Int? = try { + cursor.getInt(columnIndex) + } catch (e: Exception) { + e.printStackTrace() + null + } + + override fun getLong(columnIndex: Int): Long? = try { + cursor.getLong(columnIndex) + } catch (e: Exception) { + e.printStackTrace() + null + } + + override fun getFloat(columnIndex: Int): Float? = try { + cursor.getFloat(columnIndex) + } catch (e: Exception) { + e.printStackTrace() + null + } + + override fun getDouble(columnIndex: Int): Double? = try { + cursor.getDouble(columnIndex) + } catch (e: Exception) { + e.printStackTrace() + null + } override fun getString(columnIndex: Int): String? = try { cursor.getString(columnIndex) diff --git a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt index d76e370..09ea108 100644 --- a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt +++ b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt @@ -24,10 +24,10 @@ package com.ctrip.sqllin.driver @OptIn(ExperimentalStdlibApi::class) public interface CommonCursor : AutoCloseable { - public fun getInt(columnIndex: Int): Int - public fun getLong(columnIndex: Int): Long - public fun getFloat(columnIndex: Int): Float - public fun getDouble(columnIndex: Int): Double + public fun getInt(columnIndex: Int): Int? + public fun getLong(columnIndex: Int): Long? + public fun getFloat(columnIndex: Int): Float? + public fun getDouble(columnIndex: Int): Double? public fun getString(columnIndex: Int): String? public fun getByteArray(columnIndex: Int): ByteArray? diff --git a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/Extension.kt b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/Extension.kt index 101aa28..e6856e1 100644 --- a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/Extension.kt +++ b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/Extension.kt @@ -93,7 +93,7 @@ internal fun DatabaseConnection.migrateIfNeeded( ) = withTransaction { val initialVersion = withQuery("PRAGMA user_version;") { it.next() - it.getInt(0) + it.getInt(0) ?: 0 } if (initialVersion == 0) { create(this) diff --git a/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt b/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt index 7a66592..065aa57 100644 --- a/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt +++ b/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt @@ -24,13 +24,17 @@ import java.sql.ResultSet */ internal class JdbcCursor(private val resultSet: ResultSet) : CommonCursor { - override fun getInt(columnIndex: Int): Int = resultSet.getInt(columnIndex + 1) + override fun getInt(columnIndex: Int): Int? = + resultSet.getInt(columnIndex + 1).takeUnless { resultSet.wasNull() } - override fun getLong(columnIndex: Int): Long = resultSet.getLong(columnIndex + 1) + override fun getLong(columnIndex: Int): Long? = + resultSet.getLong(columnIndex + 1).takeUnless { resultSet.wasNull() } - override fun getFloat(columnIndex: Int): Float = resultSet.getFloat(columnIndex + 1) + override fun getFloat(columnIndex: Int): Float? = + resultSet.getFloat(columnIndex + 1).takeUnless { resultSet.wasNull() } - override fun getDouble(columnIndex: Int): Double = resultSet.getDouble(columnIndex + 1) + override fun getDouble(columnIndex: Int): Double? = + resultSet.getDouble(columnIndex + 1).takeUnless { resultSet.wasNull() } override fun getString(columnIndex: Int): String? = resultSet.getString(columnIndex + 1) diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt index 270c454..87fb885 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt @@ -30,19 +30,19 @@ internal class ConcurrentStatement( private val accessLock: Lock, ) : SQLiteStatement { - override fun columnGetLong(columnIndex: Int): Long = accessLock.withLock { + override fun columnGetLong(columnIndex: Int): Long? = accessLock.withLock { delegateStatement.columnGetLong(columnIndex) } - override fun columnGetDouble(columnIndex: Int): Double = accessLock.withLock { + override fun columnGetDouble(columnIndex: Int): Double? = accessLock.withLock { delegateStatement.columnGetDouble(columnIndex) } - override fun columnGetString(columnIndex: Int): String = accessLock.withLock { + override fun columnGetString(columnIndex: Int): String? = accessLock.withLock { delegateStatement.columnGetString(columnIndex) } - override fun columnGetBlob(columnIndex: Int): ByteArray = accessLock.withLock { + override fun columnGetBlob(columnIndex: Int): ByteArray? = accessLock.withLock { delegateStatement.columnGetBlob(columnIndex) } diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt index fc7c469..f149f5e 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt @@ -25,17 +25,17 @@ internal class NativeCursor( private val statement: SQLiteStatement ) : CommonCursor { - override fun getInt(columnIndex: Int): Int = getLong(columnIndex).toInt() + override fun getInt(columnIndex: Int): Int? = getLong(columnIndex)?.toInt() - override fun getLong(columnIndex: Int): Long = statement.columnGetLong(columnIndex) + override fun getLong(columnIndex: Int): Long? = statement.columnGetLong(columnIndex) - override fun getFloat(columnIndex: Int): Float = getDouble(columnIndex).toFloat() + override fun getFloat(columnIndex: Int): Float? = getDouble(columnIndex)?.toFloat() - override fun getDouble(columnIndex: Int): Double = statement.columnGetDouble(columnIndex) + override fun getDouble(columnIndex: Int): Double? = statement.columnGetDouble(columnIndex) - override fun getString(columnIndex: Int): String = statement.columnGetString(columnIndex) + override fun getString(columnIndex: Int): String? = statement.columnGetString(columnIndex) - override fun getByteArray(columnIndex: Int): ByteArray = statement.columnGetBlob(columnIndex) + override fun getByteArray(columnIndex: Int): ByteArray? = statement.columnGetBlob(columnIndex) override fun getColumnIndex(columnName: String): Int = columnNames[columnName] ?: throw IllegalArgumentException("Col for $columnName not found") diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt index 6d6c9ca..afb7e6c 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt @@ -23,13 +23,13 @@ package com.ctrip.sqllin.driver internal interface SQLiteStatement { - fun columnGetLong(columnIndex: Int): Long + fun columnGetLong(columnIndex: Int): Long? - fun columnGetDouble(columnIndex: Int): Double + fun columnGetDouble(columnIndex: Int): Double? - fun columnGetString(columnIndex: Int): String + fun columnGetString(columnIndex: Int): String? - fun columnGetBlob(columnIndex: Int): ByteArray + fun columnGetBlob(columnIndex: Int): ByteArray? fun columnCount(): Int diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt index 0780dd7..f73307e 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt @@ -66,28 +66,31 @@ internal class NativeStatement( ) : SQLiteStatement { // Cursor methods - fun isNull(index: Int): Boolean = + private fun isNull(index: Int): Boolean = sqlite3_column_type(cStatementPointer, index) == SQLITE_NULL - override fun columnGetLong(columnIndex: Int): Long = - sqlite3_column_int64(cStatementPointer, columnIndex) + override fun columnGetLong(columnIndex: Int): Long? = + if (isNull(columnIndex)) null else sqlite3_column_int64(cStatementPointer, columnIndex) - override fun columnGetDouble(columnIndex: Int): Double = - sqlite3_column_double(cStatementPointer, columnIndex) + override fun columnGetDouble(columnIndex: Int): Double? = + if (isNull(columnIndex)) null else sqlite3_column_double(cStatementPointer, columnIndex) - override fun columnGetString(columnIndex: Int): String = - sqlite3_column_text(cStatementPointer, columnIndex) - ?.reinterpret<ByteVar>() - ?.let { bytesToString(it) } - ?: "" + override fun columnGetString(columnIndex: Int): String? = + if (isNull(columnIndex)) + null + else + sqlite3_column_text(cStatementPointer, columnIndex) + ?.reinterpret<ByteVar>() + ?.let { bytesToString(it) } - override fun columnGetBlob(columnIndex: Int): ByteArray { + override fun columnGetBlob(columnIndex: Int): ByteArray? { + if (isNull(columnIndex)) + return null val blobSize = sqlite3_column_bytes(cStatementPointer, columnIndex) return if (blobSize == 0) - byteArrayOf() + null else sqlite3_column_blob(cStatementPointer, columnIndex)?.readBytes(blobSize) - ?: throw sqliteException("Byte array size/type issue col $columnIndex") } override fun columnCount(): Int = sqlite3_column_count(cStatementPointer) diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt index de1067d..58345fc 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt @@ -17,6 +17,7 @@ package com.ctrip.sqllin.dsl.sql.compiler import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.AbstractEncoder import kotlinx.serialization.modules.EmptySerializersModule @@ -44,8 +45,7 @@ internal abstract class AbstractValuesEncoder : AbstractEncoder() { get() = sqlStrBuilder.toString() override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean { - if (index == 0) - elementsCount = descriptor.elementsCount + elementsCount = descriptor.elementsCount elementsIndex = index return true } @@ -83,5 +83,19 @@ internal abstract class AbstractValuesEncoder : AbstractEncoder() { sqlStrBuilder.append(value).appendTail() } + override fun <T : Any> encodeNullableSerializableElement( + descriptor: SerialDescriptor, + index: Int, + serializer: SerializationStrategy<T>, + value: T? + ) { + if (value == null) { + elementsCount = descriptor.elementsCount + elementsIndex = index + sqlStrBuilder.append("NULL").appendTail() + } else + super.encodeNullableSerializableElement(descriptor, index, serializer, value) + } + override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = encodeInt(index) } \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt index d099141..15ff878 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt @@ -37,6 +37,7 @@ internal class QueryDecoder( private var elementIndex = 0 private var elementName = "" + private var elementNotNullMark = true override val serializersModule: SerializersModule = EmptySerializersModule() @@ -45,6 +46,7 @@ internal class QueryDecoder( CompositeDecoder.DECODE_DONE else { elementName = descriptor.getElementName(elementIndex) + elementNotNullMark = !descriptor.getElementDescriptor(elementIndex).isNullable val resultIndex = elementIndex++ if (cursorColumnIndex >= 0) resultIndex @@ -57,22 +59,25 @@ internal class QueryDecoder( private inline val cursorColumnIndex get() = cursor.getColumnIndex(elementName) + private inline val sqlNullException + get() = IllegalStateException("The value in database is null, please declare your property be nullable") + private inline fun <T> deserialize(block: (Int) -> T): T = cursorColumnIndex.let { if (it >= 0) block(it) else throw SerializationException("The Cursor doesn't have this column") } - override fun decodeBoolean(): Boolean = deserialize { cursor.getInt(it) > 0 } - override fun decodeByte(): Byte = deserialize { cursor.getInt(it).toByte() } - override fun decodeShort(): Short = deserialize { cursor.getInt(it).toShort() } - override fun decodeInt(): Int = deserialize { cursor.getInt(it) } - override fun decodeLong(): Long = deserialize { cursor.getLong(it) } - override fun decodeChar(): Char = deserialize { cursor.getString(it)?.first() ?: '\u0000' } - override fun decodeString(): String = deserialize { cursor.getString(it) ?: "" } - override fun decodeFloat(): Float = deserialize { cursor.getFloat(it) } - override fun decodeDouble(): Double = deserialize { cursor.getDouble(it) } - override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = deserialize { cursor.getInt(it) } + override fun decodeBoolean(): Boolean = deserialize { (cursor.getInt(it) ?: throw sqlNullException) > 0 } + override fun decodeByte(): Byte = deserialize { cursor.getInt(it)?.toByte() ?: throw sqlNullException } + override fun decodeShort(): Short = deserialize { cursor.getInt(it)?.toShort() ?: throw sqlNullException } + override fun decodeInt(): Int = deserialize { cursor.getInt(it) ?: throw sqlNullException } + override fun decodeLong(): Long = deserialize { cursor.getLong(it) ?: throw sqlNullException } + override fun decodeChar(): Char = deserialize { cursor.getString(it)?.first() ?: throw sqlNullException } + override fun decodeString(): String = deserialize { cursor.getString(it) ?: throw sqlNullException } + override fun decodeFloat(): Float = deserialize { cursor.getFloat(it) ?: throw sqlNullException } + override fun decodeDouble(): Double = deserialize { cursor.getDouble(it) ?: throw sqlNullException } + override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = deserialize { cursor.getInt(it) ?: throw sqlNullException } - override fun decodeNotNullMark(): Boolean = cursorColumnIndex >= 0 + override fun decodeNotNullMark(): Boolean = elementNotNullMark // override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {} } \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/operation/Insert.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/operation/Insert.kt index 796f2e8..e2bc531 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/operation/Insert.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/operation/Insert.kt @@ -40,6 +40,6 @@ internal object Insert : Operation { append(' ') append(encodeEntities2InsertValues(table.kSerializer(), entities, parameters)) } - return InsertStatement(sql, connection, parameters) + return InsertStatement(sql, connection, parameters.takeIf { it.isNotEmpty() }) } } \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/OtherStatement.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/OtherStatement.kt index 0a3cae2..6e94365 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/OtherStatement.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/OtherStatement.kt @@ -43,7 +43,7 @@ public class UpdateDeleteStatement internal constructor( public class InsertStatement internal constructor( sqlStr: String, private val connection: DatabaseConnection, - override val parameters: MutableList<String>, + override val parameters: MutableList<String>?, ) : SingleStatement(sqlStr) { public override fun execute(): Unit = connection.executeInsert(sqlStr, params) } diff --git a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt index b2a63bf..3cf09eb 100644 --- a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt +++ b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt @@ -24,7 +24,6 @@ import com.ctrip.sqllin.dsl.sql.clause.OrderByWay.ASC import com.ctrip.sqllin.dsl.sql.clause.OrderByWay.DESC import com.ctrip.sqllin.dsl.sql.statement.SelectStatement import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlin.test.assertEquals From 5f046417f1ab42d08483acfa830e630a9213113a Mon Sep 17 00:00:00 2001 From: qiaoyuang <qiaoyuang2012@gmail.com> Date: Tue, 23 Apr 2024 22:12:33 +0800 Subject: [PATCH 2/5] Add the null-values unit tests --- .../com/ctrip/sqllin/dsl/AndroidTest.kt | 3 +++ .../dsl/sql/compiler/AbstractValuesEncoder.kt | 15 ++--------- .../com/ctrip/sqllin/dsl/CommonBasicTest.kt | 25 ++++++++++++++++++ .../kotlin/com/ctrip/sqllin/dsl/Entities.kt | 26 ++++++++++++------- .../kotlin/com/ctrip/sqllin/dsl/JvmTest.kt | 3 +++ .../kotlin/com/ctrip/sqllin/dsl/NativeTest.kt | 3 +++ 6 files changed, 53 insertions(+), 22 deletions(-) diff --git a/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt b/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt index a5ec336..3a75f15 100644 --- a/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt +++ b/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt @@ -74,6 +74,9 @@ class AndroidTest { @Test fun testPrimitiveTypeForKSP() = commonTest.testPrimitiveTypeForKSP() + @Test + fun testNullInsertAndSelect() = commonTest.testNullInsertAndSelect() + @Before fun setUp() { val context = InstrumentationRegistry.getInstrumentation().targetContext diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt index 58345fc..41c6b95 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/AbstractValuesEncoder.kt @@ -17,7 +17,6 @@ package com.ctrip.sqllin.dsl.sql.compiler import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.AbstractEncoder import kotlinx.serialization.modules.EmptySerializersModule @@ -83,18 +82,8 @@ internal abstract class AbstractValuesEncoder : AbstractEncoder() { sqlStrBuilder.append(value).appendTail() } - override fun <T : Any> encodeNullableSerializableElement( - descriptor: SerialDescriptor, - index: Int, - serializer: SerializationStrategy<T>, - value: T? - ) { - if (value == null) { - elementsCount = descriptor.elementsCount - elementsIndex = index - sqlStrBuilder.append("NULL").appendTail() - } else - super.encodeNullableSerializableElement(descriptor, index, serializer, value) + override fun encodeNull() { + sqlStrBuilder.append("NULL").appendTail() } override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = encodeInt(index) diff --git a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt index 3cf09eb..a985f64 100644 --- a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt +++ b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt @@ -40,6 +40,7 @@ class CommonBasicTest(private val path: DatabasePath) { const val DATABASE_NAME = "BookStore.db" const val SQL_CREATE_BOOK = "create table book (id integer primary key autoincrement, name text, author text, pages integer, price real)" const val SQL_CREATE_CATEGORY = "create table category (id integer primary key autoincrement, name text, code integer)" + const val SQL_CREATE_NULL_TESTER = "create table NullTester (id integer primary key autoincrement, paramInt integer, paramString text, paramDouble real)" } private inline fun Database.databaseAutoClose(block: (Database) -> Unit) = try { @@ -427,6 +428,30 @@ class CommonBasicTest(private val path: DatabasePath) { } } + fun testNullInsertAndSelect() { + val config = DatabaseConfiguration( + name = DATABASE_NAME, + path = path, + version = 1, + create = { + it.execSQL(SQL_CREATE_NULL_TESTER) + } + ) + Database(config, true).databaseAutoClose { database -> + lateinit var selectStatement: SelectStatement<NullTester> + database { + NullTesterTable { table -> + table INSERT listOf( + NullTester(null, null, null), + NullTester(8, "888", 8.8), + ) + selectStatement = table SELECT X + } + } + assertEquals(2, selectStatement.getResults().size) + } + } + private fun getDefaultDBConfig(): DatabaseConfiguration = DatabaseConfiguration( name = DATABASE_NAME, diff --git a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/Entities.kt b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/Entities.kt index 73451d1..445e731 100644 --- a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/Entities.kt +++ b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/Entities.kt @@ -42,17 +42,25 @@ data class Category( @Serializable data class Joiner( - val name: String, - val author: String, - val price: Double, - val pages: Int, - val code: Int, + val name: String?, + val author: String?, + val price: Double?, + val pages: Int?, + val code: Int?, ) @Serializable data class CrossJoiner( - val author: String, - val price: Double, - val pages: Int, - val code: Int, + val author: String?, + val price: Double?, + val pages: Int?, + val code: Int?, +) + +@DBRow("NullTester") +@Serializable +data class NullTester( + val paramInt: Int?, + val paramString: String?, + val paramDouble: Double?, ) \ No newline at end of file diff --git a/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt b/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt index 803bc0a..9f75932 100644 --- a/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt +++ b/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt @@ -65,6 +65,9 @@ class JvmTest { @Test fun testPrimitiveTypeForKSP() = commonTest.testPrimitiveTypeForKSP() + @Test + fun testNullInsertAndSelect() = commonTest.testNullInsertAndSelect() + @BeforeTest fun setUp() { deleteDatabase(path, CommonBasicTest.DATABASE_NAME) diff --git a/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt b/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt index bf06a11..4b4ab0c 100644 --- a/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt +++ b/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt @@ -68,6 +68,9 @@ class NativeTest { @Test fun testPrimitiveTypeForKSP() = commonTest.testPrimitiveTypeForKSP() + @Test + fun testNullInsertAndSelect() = commonTest.testNullInsertAndSelect() + @BeforeTest fun setUp() { deleteDatabase(path, CommonBasicTest.DATABASE_NAME) From 8b286c5e3b0c30be62025732b40d8f9ff4a1d5d5 Mon Sep 17 00:00:00 2001 From: qiaoyuang <qiaoyuang2012@gmail.com> Date: Wed, 24 Apr 2024 13:23:37 +0800 Subject: [PATCH 3/5] Fix some bugs --- CHANGELOG.md | 9 +++- .../com/ctrip/sqllin/driver/AndroidCursor.kt | 24 +++++------ .../com/ctrip/sqllin/driver/CommonCursor.kt | 15 +++---- .../ctrip/sqllin/driver/SQLiteException.kt | 24 +++++++++++ .../ctrip/sqllin/driver/CommonBasicTest.kt | 12 ++++-- .../com/ctrip/sqllin/driver/JdbcCursor.kt | 43 +++++++++++++------ .../sqllin/driver/ConcurrentStatement.kt | 8 +++- .../com/ctrip/sqllin/driver/NativeCursor.kt | 24 ++++++----- ...SQLiteException.kt => SQLiteResultCode.kt} | 8 +--- .../ctrip/sqllin/driver/SQLiteStatement.kt | 6 ++- .../sqllin/driver/cinterop/NativeStatement.kt | 23 +++++----- .../sqllin/dsl/sql/clause/ClauseNumber.kt | 4 +- .../sqllin/dsl/sql/clause/ClauseString.kt | 17 ++++---- .../ctrip/sqllin/dsl/sql/clause/SetClause.kt | 4 +- .../sqllin/dsl/sql/compiler/QueryDecoder.kt | 31 ++++++------- .../com/ctrip/sqllin/dsl/CommonBasicTest.kt | 43 ++++++++++++++++++- 16 files changed, 188 insertions(+), 107 deletions(-) create mode 100644 sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt rename sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/{SQLiteException.kt => SQLiteResultCode.kt} (87%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0831543..da4de98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,19 @@ - Date format: YYYY-MM-dd -## v1.3.1 / 2024-04-xx +## v1.3.1 / 2024-04-24 ### sqllin-dsl * Fix a crash when a data class doesn't contain any `String` element. * Fix the [issue#81](https://github.com/ctripcorp/SQLlin/issues/81) about insert and query null values +* Fix some wrongs about generation of SQL syntax + +### sqllin-driver + +* **Breaking change**: Remove the deprecated API `CommonCursor#forEachRows` +* **Breaking change**: the `getInt`, `getLong`, `getFloat` and `getDouble` will throw an exception when the value is NULl in SQLite +* Add a new public API: `CommonCursor#isNull`, for check if the value is NULL in SQLite ## v1.3.0 / 2024-04-21 diff --git a/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt b/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt index e60af32..72279e7 100644 --- a/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt +++ b/sqllin-driver/src/androidMain/kotlin/com/ctrip/sqllin/driver/AndroidCursor.kt @@ -25,32 +25,32 @@ import android.database.Cursor internal class AndroidCursor(private val cursor: Cursor) : CommonCursor { - override fun getInt(columnIndex: Int): Int? = try { + override fun getInt(columnIndex: Int): Int = try { cursor.getInt(columnIndex) } catch (e: Exception) { e.printStackTrace() - null + throw SQLiteException("The value of column $columnIndex is NULL") } - override fun getLong(columnIndex: Int): Long? = try { + override fun getLong(columnIndex: Int): Long = try { cursor.getLong(columnIndex) } catch (e: Exception) { e.printStackTrace() - null + throw SQLiteException("The value of column $columnIndex is NULL") } - override fun getFloat(columnIndex: Int): Float? = try { + override fun getFloat(columnIndex: Int): Float = try { cursor.getFloat(columnIndex) } catch (e: Exception) { e.printStackTrace() - null + throw SQLiteException("The value of column $columnIndex is NULL") } - override fun getDouble(columnIndex: Int): Double? = try { + override fun getDouble(columnIndex: Int): Double = try { cursor.getDouble(columnIndex) } catch (e: Exception) { e.printStackTrace() - null + throw SQLiteException("The value of column $columnIndex is NULL") } override fun getString(columnIndex: Int): String? = try { @@ -69,12 +69,6 @@ internal class AndroidCursor(private val cursor: Cursor) : CommonCursor { override fun getColumnIndex(columnName: String): Int = cursor.getColumnIndexOrThrow(columnName) - @Deprecated( - message = "Please use the new API: forEachRow", - replaceWith = ReplaceWith(expression = "forEachRow"), - ) - override fun forEachRows(block: (Int) -> Unit) = forEachRow(block) - override fun forEachRow(block: (Int) -> Unit) { if (!cursor.moveToFirst()) return var index = 0 @@ -84,5 +78,7 @@ internal class AndroidCursor(private val cursor: Cursor) : CommonCursor { override fun next(): Boolean = cursor.moveToNext() + override fun isNull(columnIndex: Int): Boolean = cursor.isNull(columnIndex) + override fun close() = cursor.close() } \ No newline at end of file diff --git a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt index 09ea108..7e01a65 100644 --- a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt +++ b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/CommonCursor.kt @@ -24,23 +24,20 @@ package com.ctrip.sqllin.driver @OptIn(ExperimentalStdlibApi::class) public interface CommonCursor : AutoCloseable { - public fun getInt(columnIndex: Int): Int? - public fun getLong(columnIndex: Int): Long? - public fun getFloat(columnIndex: Int): Float? - public fun getDouble(columnIndex: Int): Double? + public fun getInt(columnIndex: Int): Int + public fun getLong(columnIndex: Int): Long + public fun getFloat(columnIndex: Int): Float + public fun getDouble(columnIndex: Int): Double public fun getString(columnIndex: Int): String? public fun getByteArray(columnIndex: Int): ByteArray? public fun getColumnIndex(columnName: String): Int - @Deprecated( - message = "Please use the new API: forEachRow", - replaceWith = ReplaceWith(expression = "forEachRow"), - ) - public fun forEachRows(block: (Int) -> Unit) public fun forEachRow(block: (Int) -> Unit) public fun next(): Boolean + public fun isNull(columnIndex: Int): Boolean + public override fun close() } \ No newline at end of file diff --git a/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt new file mode 100644 index 0000000..f3b49e5 --- /dev/null +++ b/sqllin-driver/src/commonMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 Trip.com. + * + * 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.ctrip.sqllin.driver + +/** + * The exceptions about SQLite, they include the native SQLite result codes and error message + * @author Yuang Qiao + */ + +public open class SQLiteException(message: String) : Exception(message) \ No newline at end of file diff --git a/sqllin-driver/src/commonTest/kotlin/com/ctrip/sqllin/driver/CommonBasicTest.kt b/sqllin-driver/src/commonTest/kotlin/com/ctrip/sqllin/driver/CommonBasicTest.kt index 30787d8..6b44705 100644 --- a/sqllin-driver/src/commonTest/kotlin/com/ctrip/sqllin/driver/CommonBasicTest.kt +++ b/sqllin-driver/src/commonTest/kotlin/com/ctrip/sqllin/driver/CommonBasicTest.kt @@ -28,15 +28,17 @@ class CommonBasicTest(private val path: DatabasePath) { private class Book( val name: String, - val author: String, + val author: String?, val pages: Int, val price: Double, - val array: ByteArray, + val array: ByteArray?, ) private val bookList = listOf( Book(name = "The Da Vinci Code", author = "Dan Brown", pages = 454, price = 16.96, byteArrayOf()), Book(name = "The Lost Symbol", author = "Dan Brown", pages = 510, price = 19.95, byteArrayOf(1, 2, 3)), + Book(name = "", author = "Dan Brown", pages = 454, price = 16.96, byteArrayOf()), + Book(name = "The Lost Symbol", author = null, pages = 510, price = 19.95, null), ) fun testCreateAndUpgrade() { @@ -87,6 +89,8 @@ class CommonBasicTest(private val path: DatabasePath) { it.withTransaction { connection -> connection.executeInsert(SQL.INSERT_BOOK, arrayOf("The Da Vinci Code", "Dan Brown", 454, 16.96, byteArrayOf())) connection.executeInsert(SQL.INSERT_BOOK, arrayOf("The Lost Symbol", "Dan Brown", 510, 19.95, byteArrayOf(1, 2, 3))) + connection.executeInsert(SQL.INSERT_BOOK, arrayOf("", "Dan Brown", 454, 16.96, byteArrayOf())) + connection.executeInsert(SQL.INSERT_BOOK, arrayOf("The Lost Symbol", null, 510, 19.95, null)) } } val readOnlyConfig = getDefaultDBConfig(true) @@ -99,7 +103,7 @@ class CommonBasicTest(private val path: DatabasePath) { assertEquals(book.author, cursor.getString(++columnIndex)) assertEquals(book.pages, cursor.getInt(++columnIndex)) assertEquals(book.price, cursor.getDouble(++columnIndex)) - assertEquals(book.array.size, cursor.getByteArray(++columnIndex)?.size) + assertEquals(book.array?.size, cursor.getByteArray(++columnIndex)?.size) } } } @@ -204,7 +208,7 @@ class CommonBasicTest(private val path: DatabasePath) { assertEquals(book.author, cursor.getString(++columnIndex)) assertEquals(book.pages, cursor.getInt(++columnIndex)) assertEquals(book.price, cursor.getDouble(++columnIndex)) - assertEquals(book.array.size, cursor.getByteArray(++columnIndex)?.size) + assertEquals(book.array?.size, cursor.getByteArray(++columnIndex)?.size) } } } diff --git a/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt b/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt index 065aa57..9fdd4b2 100644 --- a/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt +++ b/sqllin-driver/src/jvmMain/kotlin/com/ctrip/sqllin/driver/JdbcCursor.kt @@ -24,17 +24,33 @@ import java.sql.ResultSet */ internal class JdbcCursor(private val resultSet: ResultSet) : CommonCursor { - override fun getInt(columnIndex: Int): Int? = - resultSet.getInt(columnIndex + 1).takeUnless { resultSet.wasNull() } + override fun getInt(columnIndex: Int): Int { + val result = resultSet.getInt(columnIndex + 1) + if (resultSet.wasNull()) + throw SQLiteException("The value of column $columnIndex is NULL") + return result + } - override fun getLong(columnIndex: Int): Long? = - resultSet.getLong(columnIndex + 1).takeUnless { resultSet.wasNull() } + override fun getLong(columnIndex: Int): Long { + val result = resultSet.getLong(columnIndex + 1) + if (resultSet.wasNull()) + throw SQLiteException("The value of column $columnIndex is NULL") + return result + } - override fun getFloat(columnIndex: Int): Float? = - resultSet.getFloat(columnIndex + 1).takeUnless { resultSet.wasNull() } + override fun getFloat(columnIndex: Int): Float { + val result = resultSet.getFloat(columnIndex + 1) + if (resultSet.wasNull()) + throw SQLiteException("The value of column $columnIndex is NULL") + return result + } - override fun getDouble(columnIndex: Int): Double? = - resultSet.getDouble(columnIndex + 1).takeUnless { resultSet.wasNull() } + override fun getDouble(columnIndex: Int): Double { + val result = resultSet.getDouble(columnIndex + 1) + if (resultSet.wasNull()) + throw SQLiteException("The value of column $columnIndex is NULL") + return result + } override fun getString(columnIndex: Int): String? = resultSet.getString(columnIndex + 1) @@ -42,12 +58,6 @@ internal class JdbcCursor(private val resultSet: ResultSet) : CommonCursor { override fun getColumnIndex(columnName: String): Int = resultSet.findColumn(columnName) - 1 - @Deprecated( - message = "Please use the new API: forEachRow", - replaceWith = ReplaceWith(expression = "forEachRow"), - ) - override fun forEachRows(block: (Int) -> Unit) = forEachRow(block) - override fun forEachRow(block: (Int) -> Unit) { var index = 0 while (next()) @@ -56,6 +66,11 @@ internal class JdbcCursor(private val resultSet: ResultSet) : CommonCursor { override fun next(): Boolean = resultSet.next() + override fun isNull(columnIndex: Int): Boolean { + resultSet.getObject(columnIndex + 1) + return resultSet.wasNull() + } + override fun close() { resultSet.close() resultSet.statement.close() diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt index 87fb885..6221fbe 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/ConcurrentStatement.kt @@ -30,11 +30,15 @@ internal class ConcurrentStatement( private val accessLock: Lock, ) : SQLiteStatement { - override fun columnGetLong(columnIndex: Int): Long? = accessLock.withLock { + override fun isNull(columnIndex: Int): Boolean = accessLock.withLock { + delegateStatement.isNull(columnIndex) + } + + override fun columnGetLong(columnIndex: Int): Long = accessLock.withLock { delegateStatement.columnGetLong(columnIndex) } - override fun columnGetDouble(columnIndex: Int): Double? = accessLock.withLock { + override fun columnGetDouble(columnIndex: Int): Double = accessLock.withLock { delegateStatement.columnGetDouble(columnIndex) } diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt index f149f5e..efa7ca6 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/NativeCursor.kt @@ -25,13 +25,21 @@ internal class NativeCursor( private val statement: SQLiteStatement ) : CommonCursor { - override fun getInt(columnIndex: Int): Int? = getLong(columnIndex)?.toInt() + override fun getInt(columnIndex: Int): Int = getLong(columnIndex).toInt() - override fun getLong(columnIndex: Int): Long? = statement.columnGetLong(columnIndex) + override fun getLong(columnIndex: Int): Long { + if (isNull(columnIndex)) + throw SQLiteException("The value of column $columnIndex is NULL") + return statement.columnGetLong(columnIndex) + } - override fun getFloat(columnIndex: Int): Float? = getDouble(columnIndex)?.toFloat() + override fun getFloat(columnIndex: Int): Float = getDouble(columnIndex).toFloat() - override fun getDouble(columnIndex: Int): Double? = statement.columnGetDouble(columnIndex) + override fun getDouble(columnIndex: Int): Double { + if (isNull(columnIndex)) + throw SQLiteException("The value of column $columnIndex is NULL") + return statement.columnGetDouble(columnIndex) + } override fun getString(columnIndex: Int): String? = statement.columnGetString(columnIndex) @@ -41,12 +49,6 @@ internal class NativeCursor( override fun next(): Boolean = statement.step() - @Deprecated( - message = "Please use the new API: forEachRow", - replaceWith = ReplaceWith(expression = "forEachRow"), - ) - override fun forEachRows(block: (Int) -> Unit) = forEachRow(block) - override fun forEachRow(block: (Int) -> Unit) { var index = 0 while (next()) @@ -55,6 +57,8 @@ internal class NativeCursor( override fun close() = statement.finalizeStatement() + override fun isNull(columnIndex: Int): Boolean = statement.isNull(columnIndex) + private val columnNames: Map<String, Int> by lazy { val count = statement.columnCount() val map = HashMap<String, Int>(count) diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteResultCode.kt similarity index 87% rename from sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt rename to sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteResultCode.kt index 3a4b6d3..4f8dbb7 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteException.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteResultCode.kt @@ -19,15 +19,9 @@ package com.ctrip.sqllin.driver import com.ctrip.sqllin.driver.SQLiteResultCode.Companion.INVALID_CODE import com.ctrip.sqllin.driver.cinterop.SQLiteErrorType -/** - * The exceptions about SQLite, they include the native SQLite result codes and error message - * @author yaqiao - */ - -public open class SQLiteException(message: String) : Exception(message) - /** * The result codes in SQLite + * @author Yuang Qiao */ public class SQLiteResultCode(message: String, resultCode: Int) : SQLiteException( "$message | error code ${ diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt index afb7e6c..c968129 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/SQLiteStatement.kt @@ -23,9 +23,11 @@ package com.ctrip.sqllin.driver internal interface SQLiteStatement { - fun columnGetLong(columnIndex: Int): Long? + fun isNull(columnIndex: Int): Boolean - fun columnGetDouble(columnIndex: Int): Double? + fun columnGetLong(columnIndex: Int): Long + + fun columnGetDouble(columnIndex: Int): Double fun columnGetString(columnIndex: Int): String? diff --git a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt index f73307e..d8090c8 100644 --- a/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt +++ b/sqllin-driver/src/nativeMain/kotlin/com/ctrip/sqllin/driver/cinterop/NativeStatement.kt @@ -66,29 +66,26 @@ internal class NativeStatement( ) : SQLiteStatement { // Cursor methods - private fun isNull(index: Int): Boolean = - sqlite3_column_type(cStatementPointer, index) == SQLITE_NULL + override fun isNull(columnIndex: Int): Boolean = + sqlite3_column_type(cStatementPointer, columnIndex) == SQLITE_NULL - override fun columnGetLong(columnIndex: Int): Long? = - if (isNull(columnIndex)) null else sqlite3_column_int64(cStatementPointer, columnIndex) + override fun columnGetLong(columnIndex: Int): Long = + sqlite3_column_int64(cStatementPointer, columnIndex) - override fun columnGetDouble(columnIndex: Int): Double? = - if (isNull(columnIndex)) null else sqlite3_column_double(cStatementPointer, columnIndex) + override fun columnGetDouble(columnIndex: Int): Double = + sqlite3_column_double(cStatementPointer, columnIndex) override fun columnGetString(columnIndex: Int): String? = - if (isNull(columnIndex)) - null - else - sqlite3_column_text(cStatementPointer, columnIndex) - ?.reinterpret<ByteVar>() - ?.let { bytesToString(it) } + sqlite3_column_text(cStatementPointer, columnIndex) + ?.reinterpret<ByteVar>() + ?.let { bytesToString(it) } override fun columnGetBlob(columnIndex: Int): ByteArray? { if (isNull(columnIndex)) return null val blobSize = sqlite3_column_bytes(cStatementPointer, columnIndex) return if (blobSize == 0) - null + byteArrayOf() else sqlite3_column_blob(cStatementPointer, columnIndex)?.readBytes(blobSize) } diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseNumber.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseNumber.kt index e569583..03b8b31 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseNumber.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseNumber.kt @@ -42,13 +42,13 @@ public class ClauseNumber( internal infix fun lte(clauseNumber: ClauseNumber): SelectCondition = appendClauseNumber("<=", clauseNumber) // Equals, == - internal infix fun eq(number: Number?): SelectCondition = appendNullableNumber("=", "IS", number) + internal infix fun eq(number: Number?): SelectCondition = appendNullableNumber("=", " IS", number) // Equals, append to ClauseNumber internal infix fun eq(clauseNumber: ClauseNumber): SelectCondition = appendClauseNumber("=", clauseNumber) // Not equals to, != - internal infix fun neq(number: Number?): SelectCondition = appendNullableNumber("!=", "IS NOT", number) + internal infix fun neq(number: Number?): SelectCondition = appendNullableNumber("!=", " IS NOT", number) // Not equals to, append to ClauseNumber internal infix fun neq(clauseNumber: ClauseNumber): SelectCondition = appendClauseNumber("!=", clauseNumber) diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseString.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseString.kt index b897a20..980ccfd 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseString.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseString.kt @@ -30,20 +30,20 @@ public class ClauseString( ) : ClauseElement(valueName, table, isFunction) { // Equals, == - internal infix fun eq(str: String?): SelectCondition = appendString("=", "IS", str) + internal infix fun eq(str: String?): SelectCondition = appendString("=", " IS", str) // Equals, append another ClauseString internal infix fun eq(clauseString: ClauseString): SelectCondition = appendClauseString("=", clauseString) // Not equals to, != - internal infix fun neq(str: String?): SelectCondition = appendString("!=", "IS NOT", str) + internal infix fun neq(str: String?): SelectCondition = appendString("!=", " IS NOT", str) // Not equal to, append another ClauseString internal infix fun neq(clauseString: ClauseString): SelectCondition = appendClauseString("!=", clauseString) - internal infix fun like(regex: String): SelectCondition = appendRegex("LIKE", regex) + internal infix fun like(regex: String): SelectCondition = appendRegex(" LIKE ", regex) - internal infix fun glob(regex: String): SelectCondition = appendRegex("GLOB", regex) + internal infix fun glob(regex: String): SelectCondition = appendRegex(" GLOB ", regex) private fun appendRegex(symbol: String, regex: String): SelectCondition { val sql = buildString { @@ -66,12 +66,13 @@ public class ClauseString( } append(valueName) val isNull = str == null - val symbol = if (isNull) nullSymbol else notNullSymbol - append(symbol) - if (str == null) + if (isNull) { + append(nullSymbol) append(" NULL") - else + } else { + append(notNullSymbol) append('?') + } } return SelectCondition(sql, if (str == null) null else mutableListOf(str)) } diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/SetClause.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/SetClause.kt index c9508b5..b26e2da 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/SetClause.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/SetClause.kt @@ -31,9 +31,9 @@ public class SetClause<T> : Clause<T> { public fun appendString(propertyName: String, propertyValue: String?) { clauseBuilder.append(propertyName) if (propertyValue == null) - clauseBuilder.append("NULL,") + clauseBuilder.append("=NULL,") else { - clauseBuilder.append("?,") + clauseBuilder.append("=?,") val params = parameters ?: ArrayList<String>().also { parameters = it } diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt index 15ff878..0b8af6f 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/compiler/QueryDecoder.kt @@ -37,7 +37,7 @@ internal class QueryDecoder( private var elementIndex = 0 private var elementName = "" - private var elementNotNullMark = true + private var elementNullable = false override val serializersModule: SerializersModule = EmptySerializersModule() @@ -46,7 +46,7 @@ internal class QueryDecoder( CompositeDecoder.DECODE_DONE else { elementName = descriptor.getElementName(elementIndex) - elementNotNullMark = !descriptor.getElementDescriptor(elementIndex).isNullable + elementNullable = descriptor.getElementDescriptor(elementIndex).isNullable val resultIndex = elementIndex++ if (cursorColumnIndex >= 0) resultIndex @@ -59,25 +59,20 @@ internal class QueryDecoder( private inline val cursorColumnIndex get() = cursor.getColumnIndex(elementName) - private inline val sqlNullException - get() = IllegalStateException("The value in database is null, please declare your property be nullable") - private inline fun <T> deserialize(block: (Int) -> T): T = cursorColumnIndex.let { if (it >= 0) block(it) else throw SerializationException("The Cursor doesn't have this column") } - override fun decodeBoolean(): Boolean = deserialize { (cursor.getInt(it) ?: throw sqlNullException) > 0 } - override fun decodeByte(): Byte = deserialize { cursor.getInt(it)?.toByte() ?: throw sqlNullException } - override fun decodeShort(): Short = deserialize { cursor.getInt(it)?.toShort() ?: throw sqlNullException } - override fun decodeInt(): Int = deserialize { cursor.getInt(it) ?: throw sqlNullException } - override fun decodeLong(): Long = deserialize { cursor.getLong(it) ?: throw sqlNullException } - override fun decodeChar(): Char = deserialize { cursor.getString(it)?.first() ?: throw sqlNullException } - override fun decodeString(): String = deserialize { cursor.getString(it) ?: throw sqlNullException } - override fun decodeFloat(): Float = deserialize { cursor.getFloat(it) ?: throw sqlNullException } - override fun decodeDouble(): Double = deserialize { cursor.getDouble(it) ?: throw sqlNullException } - override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = deserialize { cursor.getInt(it) ?: throw sqlNullException } - - override fun decodeNotNullMark(): Boolean = elementNotNullMark + override fun decodeBoolean(): Boolean = deserialize { cursor.getInt(it) > 0 } + override fun decodeByte(): Byte = deserialize { cursor.getInt(it).toByte() } + override fun decodeShort(): Short = deserialize { cursor.getInt(it).toShort() } + override fun decodeInt(): Int = deserialize { cursor.getInt(it) } + override fun decodeLong(): Long = deserialize { cursor.getLong(it) } + override fun decodeChar(): Char = deserialize { cursor.getString(it)?.first() ?: '\u0000' } + override fun decodeString(): String = deserialize { cursor.getString(it) ?: "" } + override fun decodeFloat(): Float = deserialize { cursor.getFloat(it) } + override fun decodeDouble(): Double = deserialize { cursor.getDouble(it) } + override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = deserialize { cursor.getInt(it) } - // override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {} + override fun decodeNotNullMark(): Boolean = !cursor.isNull(cursorColumnIndex) || !elementNullable } \ No newline at end of file diff --git a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt index a985f64..936e3ea 100644 --- a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt +++ b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt @@ -439,6 +439,7 @@ class CommonBasicTest(private val path: DatabasePath) { ) Database(config, true).databaseAutoClose { database -> lateinit var selectStatement: SelectStatement<NullTester> + // INSERT & SELECT database { NullTesterTable { table -> table INSERT listOf( @@ -448,7 +449,47 @@ class CommonBasicTest(private val path: DatabasePath) { selectStatement = table SELECT X } } - assertEquals(2, selectStatement.getResults().size) + + selectStatement.getResults().forEachIndexed { i, tester -> + when (i) { + 0 -> { + assertEquals(null, tester.paramInt) + assertEquals(null, tester.paramString) + assertEquals(null, tester.paramDouble) + } + 1 -> { + assertEquals(8, tester.paramInt) + assertEquals("888", tester.paramString) + assertEquals(8.8, tester.paramDouble) + } + } + } + + // UPDATE & SELECT + database { + NullTesterTable { table -> + table UPDATE SET { paramString = null } WHERE (paramDouble EQ 8.8) + selectStatement = table SELECT WHERE (paramInt NEQ null) + } + } + val result1 = selectStatement.getResults().first() + assertEquals(1, selectStatement.getResults().size) + assertEquals(8, result1.paramInt) + assertEquals(null, result1.paramString) + assertEquals(8.8, result1.paramDouble) + + // DELETE & SELECT + database { + NullTesterTable { table -> + table DELETE WHERE (paramInt EQ null OR (paramDouble EQ null)) + selectStatement = table SELECT X + } + } + val result2 = selectStatement.getResults().first() + assertEquals(1, selectStatement.getResults().size) + assertEquals(8, result1.paramInt) + assertEquals(null, result1.paramString) + assertEquals(8.8, result1.paramDouble) } } From 2b55a006e134a5095bc456140f882b394e5b080d Mon Sep 17 00:00:00 2001 From: qiaoyuang <qiaoyuang2012@gmail.com> Date: Wed, 24 Apr 2024 13:36:56 +0800 Subject: [PATCH 4/5] Fix the unit tests of sqllin-dsl --- .../com/ctrip/sqllin/dsl/AndroidTest.kt | 2 +- .../com/ctrip/sqllin/dsl/CommonBasicTest.kt | 66 +++++++++---------- .../kotlin/com/ctrip/sqllin/dsl/JvmTest.kt | 2 +- .../kotlin/com/ctrip/sqllin/dsl/NativeTest.kt | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt b/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt index 3a75f15..f1c99cf 100644 --- a/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt +++ b/sqllin-dsl/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/AndroidTest.kt @@ -75,7 +75,7 @@ class AndroidTest { fun testPrimitiveTypeForKSP() = commonTest.testPrimitiveTypeForKSP() @Test - fun testNullInsertAndSelect() = commonTest.testNullInsertAndSelect() + fun testNullValue() = commonTest.testNullValue() @Before fun setUp() { diff --git a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt index 936e3ea..9b42576 100644 --- a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt +++ b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/CommonBasicTest.kt @@ -369,43 +369,43 @@ class CommonBasicTest(private val path: DatabasePath) { assertEquals(outerJoinStatementWithOn?.getResults()?.size, books.size) } - fun testConcurrency() = runBlocking(Dispatchers.Default) { - val book1 = Book(name = "The Da Vinci Code", author = "Dan Brown", pages = 454, price = 16.96) - val book2 = Book(name = "The Lost Symbol", author = "Dan Brown", pages = 510, price = 19.95) - val database = Database(getDefaultDBConfig(), true) - launch { - var statement: SelectStatement<Book>? = null - database suspendedScope { - statement = BookTable { table -> - table INSERT listOf(book1, book2) - table SELECT X - } - } - + fun testConcurrency() = Database(getDefaultDBConfig(), true).databaseAutoClose { database -> + runBlocking(Dispatchers.Default) { + val book1 = Book(name = "The Da Vinci Code", author = "Dan Brown", pages = 454, price = 16.96) + val book2 = Book(name = "The Lost Symbol", author = "Dan Brown", pages = 510, price = 19.95) launch { - val book1NewPrice = 18.96 - val book2NewPrice = 21.95 - val newBook1 = Book(name = "The Da Vinci Code", author = "Dan Brown", pages = 454, price = book1NewPrice) - val newBook2 = Book(name = "The Lost Symbol", author = "Dan Brown", pages = 510, price = book2NewPrice) - var newResult: SelectStatement<Book>? = null + var statement: SelectStatement<Book>? = null database suspendedScope { - newResult = transaction { - BookTable { table -> - table UPDATE SET { price = book1NewPrice } WHERE (name EQ book1.name AND (price EQ book1.price)) - table UPDATE SET { price = book2NewPrice } WHERE (name EQ book2.name AND (price EQ book2.price)) - table SELECT X + statement = BookTable { table -> + table INSERT listOf(book1, book2) + table SELECT X + } + } + + launch { + val book1NewPrice = 18.96 + val book2NewPrice = 21.95 + val newBook1 = Book(name = "The Da Vinci Code", author = "Dan Brown", pages = 454, price = book1NewPrice) + val newBook2 = Book(name = "The Lost Symbol", author = "Dan Brown", pages = 510, price = book2NewPrice) + var newResult: SelectStatement<Book>? = null + database suspendedScope { + newResult = transaction { + BookTable { table -> + table UPDATE SET { price = book1NewPrice } WHERE (name EQ book1.name AND (price EQ book1.price)) + table UPDATE SET { price = book2NewPrice } WHERE (name EQ book2.name AND (price EQ book2.price)) + table SELECT X + } } } + + assertEquals(true, newResult!!.getResults().any { it == newBook1 }) + assertEquals(true, newResult!!.getResults().any { it == newBook2 }) } - assertEquals(true, newResult!!.getResults().any { it == newBook1 }) - assertEquals(true, newResult!!.getResults().any { it == newBook2 }) + assertEquals(true, statement!!.getResults().any { it == book1 }) + assertEquals(true, statement!!.getResults().any { it == book2 }) } - - assertEquals(true, statement!!.getResults().any { it == book1 }) - assertEquals(true, statement!!.getResults().any { it == book2 }) } - Unit } fun testPrimitiveTypeForKSP() { @@ -428,7 +428,7 @@ class CommonBasicTest(private val path: DatabasePath) { } } - fun testNullInsertAndSelect() { + fun testNullValue() { val config = DatabaseConfiguration( name = DATABASE_NAME, path = path, @@ -487,9 +487,9 @@ class CommonBasicTest(private val path: DatabasePath) { } val result2 = selectStatement.getResults().first() assertEquals(1, selectStatement.getResults().size) - assertEquals(8, result1.paramInt) - assertEquals(null, result1.paramString) - assertEquals(8.8, result1.paramDouble) + assertEquals(8, result2.paramInt) + assertEquals(null, result2.paramString) + assertEquals(8.8, result2.paramDouble) } } diff --git a/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt b/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt index 9f75932..b2096d0 100644 --- a/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt +++ b/sqllin-dsl/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/JvmTest.kt @@ -66,7 +66,7 @@ class JvmTest { fun testPrimitiveTypeForKSP() = commonTest.testPrimitiveTypeForKSP() @Test - fun testNullInsertAndSelect() = commonTest.testNullInsertAndSelect() + fun testNullValue() = commonTest.testNullValue() @BeforeTest fun setUp() { diff --git a/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt b/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt index 4b4ab0c..a2943dd 100644 --- a/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt +++ b/sqllin-dsl/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/NativeTest.kt @@ -69,7 +69,7 @@ class NativeTest { fun testPrimitiveTypeForKSP() = commonTest.testPrimitiveTypeForKSP() @Test - fun testNullInsertAndSelect() = commonTest.testNullInsertAndSelect() + fun testNullValue() = commonTest.testNullValue() @BeforeTest fun setUp() { From c988a557a8bf3f860c2bf054b8aacc5bf0f2aac9 Mon Sep 17 00:00:00 2001 From: qiaoyuang <qiaoyuang2012@gmail.com> Date: Wed, 24 Apr 2024 13:52:06 +0800 Subject: [PATCH 5/5] Update version to 1.3.1 --- gradle.properties | 2 +- sqllin-driver/README.md | 2 +- sqllin-driver/README_CN.md | 2 +- sqllin-dsl/doc/getting-start-cn.md | 2 +- sqllin-dsl/doc/getting-start.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2b48f4d..c0c7bce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION=1.3.0 +VERSION=1.3.1 GROUP=com.ctrip.kotlin kotlinVersion=1.9.23 diff --git a/sqllin-driver/README.md b/sqllin-driver/README.md index 93920b2..0e49c57 100644 --- a/sqllin-driver/README.md +++ b/sqllin-driver/README.md @@ -102,7 +102,7 @@ databaseConnection.executeUpdateDelete(SQL.UPDATE, arrayOf(20, "Tom")) // SELECT val cursor: CommonCursor = databaseConnection.query(SQL.QUERY, arrayOf(20, "Tom")) -cursor.forEachRows { index -> // Index of rows +cursor.forEachRow { index -> // Index of rows val age: Int = cursor.getInt("age") val name: String = cursor.getString("name") } diff --git a/sqllin-driver/README_CN.md b/sqllin-driver/README_CN.md index 2845fb7..73776df 100644 --- a/sqllin-driver/README_CN.md +++ b/sqllin-driver/README_CN.md @@ -91,7 +91,7 @@ databaseConnection.executeUpdateDelete(SQL.UPDATE, arrayOf(20, "Tom")) // SELECT val cursor: CommonCursor = databaseConnection.query(SQL.QUERY, arrayOf(20, "Tom")) -cursor.forEachRows { index -> // Index of rows +cursor.forEachRow { index -> // Index of rows val age: Int = cursor.getInt("age") val name: String = cursor.getString("name") } diff --git a/sqllin-dsl/doc/getting-start-cn.md b/sqllin-dsl/doc/getting-start-cn.md index 5523dfe..7331274 100644 --- a/sqllin-dsl/doc/getting-start-cn.md +++ b/sqllin-dsl/doc/getting-start-cn.md @@ -14,7 +14,7 @@ plugins { id("com.google.devtools.ksp") } -val sqllinVersion = "1.3.0" +val sqllinVersion = "1.3.1" kotlin { // ...... diff --git a/sqllin-dsl/doc/getting-start.md b/sqllin-dsl/doc/getting-start.md index 69c0c55..21c14a8 100644 --- a/sqllin-dsl/doc/getting-start.md +++ b/sqllin-dsl/doc/getting-start.md @@ -16,7 +16,7 @@ plugins { id("com.google.devtools.ksp") } -val sqllinVersion = "1.3.0" +val sqllinVersion = "1.3.1" kotlin { // ......