From 5bd04a575b0fcacc8fa0dfd5042657850a8e85d6 Mon Sep 17 00:00:00 2001 From: qiaoyuang Date: Tue, 28 Nov 2023 17:04:51 +0800 Subject: [PATCH] Update version to 1.2.3 --- .github/workflows/build.yml | 12 +- .github/workflows/publish.yml | 12 +- CHANGELOG.md | 30 +- build.gradle.kts | 2 +- gradle.properties | 6 +- sample/build.gradle.kts | 2 +- sqllin-driver/build.gradle.kts | 2 +- .../com/ctrip/sqllin/driver/AndroidCursor.kt | 8 +- .../com/ctrip/sqllin/driver/CommonCursor.kt | 10 +- .../ctrip/sqllin/driver/CommonBasicTest.kt | 10 +- .../com/ctrip/sqllin/driver/JdbcCursor.kt | 8 +- .../sqllin/driver/platform/UtilsLinux.kt | 16 + .../sqllin/driver/platform/UtilsMinGW.kt | 16 + .../com/ctrip/sqllin/driver/NativeCursor.kt | 8 +- sqllin-dsl/build.gradle.kts | 2 +- sqllin-dsl/doc/getting-start-cn.md | 2 +- sqllin-dsl/doc/getting-start.md | 2 +- sqllin-dsl/doc/query-cn.md | 27 +- sqllin-dsl/doc/query.md | 27 +- .../kotlin/com/ctrip/sqllin/dsl/Database.kt | 300 +----------------- .../com/ctrip/sqllin/dsl/DatabaseScope.kt | 299 +++++++++++++++++ .../sqllin/dsl/sql/clause/GroupByClause.kt | 18 +- .../sqllin/dsl/sql/clause/OrderByClause.kt | 76 ++++- .../sql/statement/DatabaseExecuteEngine.kt | 32 +- .../dsl/sql/statement/SelectStatement.kt | 26 +- .../statement/TransactionStatementsGroup.kt | 4 +- .../com/ctrip/sqllin/dsl/CommonBasicTest.kt | 62 ++-- 27 files changed, 602 insertions(+), 417 deletions(-) create mode 100644 sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/DatabaseScope.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bb1c4a2..e8ae886 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -105,14 +105,14 @@ jobs: with: name: Test-Reports path: sqllin-driver/build/reports - if: always() + if: failure() - name: Upload sqllin-dsl Reports uses: actions/upload-artifact@v2 with: name: Test-Reports path: sqllin-dsl/build/reports - if: always() + if: failure() build-on-windows: runs-on: windows-latest @@ -165,14 +165,14 @@ jobs: with: name: Test-Reports path: sqllin-driver/build/reports - if: always() + if: failure() - name: Upload sqllin-dsl Reports uses: actions/upload-artifact@v2 with: name: Test-Reports path: sqllin-dsl/build/reports - if: always() + if: failure() build-on-linux: runs-on: ubuntu-latest @@ -265,11 +265,11 @@ jobs: with: name: Test-Reports path: sqllin-driver/build/reports - if: always() + if: failure() - name: Upload sqllin-dsl Reports uses: actions/upload-artifact@v2 with: name: Test-Reports path: sqllin-dsl/build/reports - if: always() + if: failure() diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7780c7c..72a7caa 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -100,14 +100,14 @@ jobs: with: name: Test-Reports path: sqllin-driver/build/reports - if: always() + if: failure() - name: Upload sqllin-dsl Reports uses: actions/upload-artifact@v2 with: name: Test-Reports path: sqllin-dsl/build/reports - if: always() + if: failure() - name: Publish to MavenCentral run: ./publish_apple_android_jvm.sh @@ -163,14 +163,14 @@ jobs: with: name: Test-Reports path: sqllin-driver/build/reports - if: always() + if: failure() - name: Upload sqllin-dsl Reports uses: actions/upload-artifact@v2 with: name: Test-Reports path: sqllin-dsl/build/reports - if: always() + if: failure() - name: Publish to MavenCentral run: ./gradlew :sqllin-driver:publishMingwX64PublicationToMavenRepository && ./gradlew :sqllin-dsl:publishMingwX64PublicationToMavenRepository @@ -266,14 +266,14 @@ jobs: with: name: Test-Reports path: sqllin-driver/build/reports - if: always() + if: failure() - name: Upload sqllin-dsl Reports uses: actions/upload-artifact@v2 with: name: Test-Reports path: sqllin-dsl/build/reports - if: always() + if: failure() - name: Publish to MavenCentral run: ./publish_linux_processor.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9596b..7d897ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ - Date format: YYYY-MM-dd +## v1.2.3 / 2023-11-28 + +### All + +* Update `Kotlin`'s version to `1.9.21` + +### sqllin-dsl + +* Now, the `ORDER_BY` clause could ignore the `OrderByWay` parameter like SQL. +* Optimize the performance in concurrent scenarios. Some types have changed, but users don't need to change the code. +* Now, `SelectStatement` has been changed to lazy deserialization mode, that's means the first time you invoke the +function `SelectStatement#getResults` will consume more time. But, correspondingly, executing `SELECT` statements will be faster. +* Add the `enableSimpleSQLLog` parameter to `Database`'s constructor, default by `true`, if you set it to +`false`, you can disable the simple SQL logout. + +### sqllin-driver + +* Deprecated the public API `CommonCursor#forEachRows`, you can replace with `CommonCursor#forEachRow`. This +change just for fixing a typo :). And, the `CommonCursor#forEachRows` will be removed in next version. + +### sqllin-processor + +* Update `KSP`'s version to `1.9.21-1.0.15` + ## v1.2.2 / 2023-11-08 ### All @@ -35,13 +59,13 @@ * Fix the problem: [Native driver does not respect isReadOnly](https://github.com/ctripcorp/SQLlin/issues/50). ***On native platforms***. Now, if a user set `isReadOnly = true` in `DatabaseConfigurtaion`, the database file must exist. And, if opening in read-write mode fails due to OS-level permissions, the user will get a read-only database, and if the user try to modify the database, will receive -a runtime exception. Thanks for [@nbransby](https://github.com/nbransby) +a runtime exception. Thanks for [@nbransby](https://github.com/nbransby). ### sqllin-processor * Update `KSP`'s version to `1.9.10-1.0.13` * Now, if your data class with `@DBRow` can't be solved or imported successfully(Using `KSNode#validate` to judge), the -`ClauseProcessor` would try to resolve it in second round +`ClauseProcessor` would try to resolve it in second round. ## v1.2.0 / 2023-09-19 @@ -63,7 +87,7 @@ a runtime exception. Thanks for [@nbransby](https://github.com/nbransby) ### sqllin-dsl -* Deprecated the public API `DBEntity`([#36](https://github.com/ctripcorp/SQLlin/pull/36), [#37](https://github.com/ctripcorp/SQLlin/pull/37)), any data classes used in _sqllin-dsl_ don't need to extend `DBEntity` anymore +* Deprecated the public API `DBEntity`([#36](https://github.com/ctripcorp/SQLlin/pull/36), [#37](https://github.com/ctripcorp/SQLlin/pull/37)), any data classes used in _sqllin-dsl_ don't need to extend `DBEntity` anymore. ### sqllin-driver diff --git a/build.gradle.kts b/build.gradle.kts index a1cde5f..a943932 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { dependencies { val kotlinVersion: String by project classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - classpath("com.android.tools.build:gradle:8.1.2") + classpath("com.android.tools.build:gradle:8.1.4") } } diff --git a/gradle.properties b/gradle.properties index f83be9e..18761e2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ -VERSION=1.2.2 +VERSION=1.2.3 GROUP=com.ctrip.kotlin -kotlinVersion=1.9.20 -kspVersion=1.9.20-1.0.13 +kotlinVersion=1.9.21 +kspVersion=1.9.21-1.0.15 coroutinesVersion=1.7.3 #Maven Publish Information diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 4f52bd6..ed3053b 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -42,7 +42,7 @@ kotlin { android { namespace = "com.ctrip.sqllin.sample" - compileSdk = 33 + compileSdk = 34 defaultConfig { minSdk = 23 } diff --git a/sqllin-driver/build.gradle.kts b/sqllin-driver/build.gradle.kts index 622ab2d..4c6f3bf 100644 --- a/sqllin-driver/build.gradle.kts +++ b/sqllin-driver/build.gradle.kts @@ -117,7 +117,7 @@ gradle.taskGraph.whenReady { android { namespace = "com.ctrip.sqllin.driver" - compileSdk = 33 + compileSdk = 34 defaultConfig { minSdk = 23 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 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 0726d4d..0a2c2a5 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 @@ -46,7 +46,13 @@ internal class AndroidCursor(private val cursor: Cursor) : CommonCursor { override fun getColumnIndex(columnName: String): Int = cursor.getColumnIndexOrThrow(columnName) - override fun forEachRows(block: (Int) -> Unit) { + @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 do block(index++) 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 8680448..d76e370 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 @@ -21,7 +21,8 @@ package com.ctrip.sqllin.driver * @author yaqiao */ -public interface CommonCursor { +@OptIn(ExperimentalStdlibApi::class) +public interface CommonCursor : AutoCloseable { public fun getInt(columnIndex: Int): Int public fun getLong(columnIndex: Int): Long @@ -32,9 +33,14 @@ public interface CommonCursor { 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 close() + public override fun close() } \ 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 8cd962d..30787d8 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 @@ -92,7 +92,7 @@ class CommonBasicTest(private val path: DatabasePath) { val readOnlyConfig = getDefaultDBConfig(true) openDatabase(readOnlyConfig) { it.withQuery(SQL.QUERY_BOOK, null) { cursor -> - cursor.forEachRows { rowIndex -> + cursor.forEachRow { rowIndex -> val book = bookList[rowIndex] var columnIndex = 0 assertEquals(book.name, cursor.getString(++columnIndex)) @@ -121,7 +121,7 @@ class CommonBasicTest(private val path: DatabasePath) { val readOnlyConfig = getDefaultDBConfig(true) openDatabase(readOnlyConfig) { it.withQuery(SQL.QUERY_BOOK, null) { cursor -> - cursor.forEachRows { rowIndex -> + cursor.forEachRow { rowIndex -> val (name, price) = when (rowIndex) { 0 -> "The Da Vinci Code" to 18.99 1 -> "The Lost Symbol" to 25.88 @@ -147,7 +147,7 @@ class CommonBasicTest(private val path: DatabasePath) { val readOnlyConfig = getDefaultDBConfig(true) openDatabase(readOnlyConfig) { it.withQuery(SQL.QUERY_BOOK, null) { cursor -> - cursor.forEachRows { + cursor.forEachRow { val book = bookList.first() var columnIndex = 0 assertEquals(book.name, cursor.getString(++columnIndex)) @@ -180,7 +180,7 @@ class CommonBasicTest(private val path: DatabasePath) { val readOnlyConfig = getDefaultDBConfig(true) openDatabase(readOnlyConfig) { it.withQuery(SQL.QUERY_BOOK, null) { cursor -> - cursor.forEachRows { rowIndex -> + cursor.forEachRow { rowIndex -> val (name, price) = bookList[rowIndex].run { name to price } assertEquals(name, cursor.getString(1)) assertEquals(price, cursor.getDouble(4)) @@ -197,7 +197,7 @@ class CommonBasicTest(private val path: DatabasePath) { val readOnlyConfig = getDefaultDBConfig(true) openDatabase(readOnlyConfig) { connection -> connection.withQuery(SQL.QUERY_BOOK, null) { cursor -> - cursor.forEachRows { rowIndex -> + cursor.forEachRow { rowIndex -> val book = bookList[rowIndex] var columnIndex = 0 assertEquals(book.name, cursor.getString(++columnIndex)) 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 2298ec5..7a66592 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 @@ -38,7 +38,13 @@ internal class JdbcCursor(private val resultSet: ResultSet) : CommonCursor { override fun getColumnIndex(columnName: String): Int = resultSet.findColumn(columnName) - 1 - override fun forEachRows(block: (Int) -> Unit) { + @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()) block(index++) diff --git a/sqllin-driver/src/linuxMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsLinux.kt b/sqllin-driver/src/linuxMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsLinux.kt index 77f1967..c3a6be3 100644 --- a/sqllin-driver/src/linuxMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsLinux.kt +++ b/sqllin-driver/src/linuxMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsLinux.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 Ctrip.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.platform import kotlinx.cinterop.ByteVar diff --git a/sqllin-driver/src/mingwMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsMinGW.kt b/sqllin-driver/src/mingwMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsMinGW.kt index a10d37e..9124a4a 100644 --- a/sqllin-driver/src/mingwMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsMinGW.kt +++ b/sqllin-driver/src/mingwMain/kotlin/com/ctrip/sqllin/driver/platform/UtilsMinGW.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 Ctrip.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.platform import kotlinx.cinterop.ByteVar 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 5a76aad..fc7c469 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 @@ -41,7 +41,13 @@ internal class NativeCursor( override fun next(): Boolean = statement.step() - override fun forEachRows(block: (Int) -> Unit) { + @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()) block(index++) diff --git a/sqllin-dsl/build.gradle.kts b/sqllin-dsl/build.gradle.kts index 9984504..e976b08 100644 --- a/sqllin-dsl/build.gradle.kts +++ b/sqllin-dsl/build.gradle.kts @@ -119,7 +119,7 @@ gradle.taskGraph.whenReady { android { namespace = "com.ctrip.sqllin.dsl" - compileSdk = 33 + compileSdk = 34 defaultConfig { minSdk = 23 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/sqllin-dsl/doc/getting-start-cn.md b/sqllin-dsl/doc/getting-start-cn.md index 377de5f..2fa1081 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.2.2" +val sqllinVersion = "1.2.3" kotlin { // ...... diff --git a/sqllin-dsl/doc/getting-start.md b/sqllin-dsl/doc/getting-start.md index cd30fd8..d63fd81 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.2.2" +val sqllinVersion = "1.2.3" kotlin { // ...... diff --git a/sqllin-dsl/doc/query-cn.md b/sqllin-dsl/doc/query-cn.md index c07d19f..88252d0 100644 --- a/sqllin-dsl/doc/query-cn.md +++ b/sqllin-dsl/doc/query-cn.md @@ -37,31 +37,30 @@ _GROUP BY_ 。示例代码如下所示: fun sample() { database { PersonTable { table -> - table SELECT WHERE(age LTE 5) - table SELECT ORDER_BY(age to DESC) - table SELECT LIMIT(3) - table SELECT GROUP_BY(name) + table SELECT WHERE(age LTE 5) + table SELECT ORDER_BY(age to DESC) + table SELECT ORDER_BY(age) + table SELECT LIMIT(3) + table SELECT GROUP_BY(name) } } } ``` -在 _ORDER BY_ 子句中,排序方式(`ASC` 或 `DESC`)必须被显式写出。 - ## 子句连接 有时我们会一次使用多个子句。在 SQL 中,有一些子句必须跟在另一些子句之后,比如 _HAVING_ 跟在 _GROUP BY_ 后面。SQLlin 确保你不会在子句的顺序上出错,子句的连接规则如下表所示: -|Clause/Statement| Can Connect | -|---|----------------------------------| +|Clause/Statement| Connectable | +|---|------------------------------| |SELECT| WHERE, ORDER BY, LIMIT, GROUP BY | -|WHERE| LIMIT, ORDER BY, GROUP BY | -|GROUP BY| HAVING, ORDER BY | -|HAVING| ORDER BY, LIMIT | -|ORDER BY| LIMIT | -|LIMIT| OFFSET | -|OFFSET| / | +|WHERE| LIMIT, ORDER BY, GROUP BY | +|GROUP BY| HAVING, ORDER BY | +|HAVING| ORDER BY, LIMIT | +|ORDER BY| LIMIT | +|LIMIT| OFFSET | +|OFFSET| / | 一个带有多子句的 _SELECT_ 如下所示: diff --git a/sqllin-dsl/doc/query.md b/sqllin-dsl/doc/query.md index ebf7ef4..2a462ce 100644 --- a/sqllin-dsl/doc/query.md +++ b/sqllin-dsl/doc/query.md @@ -38,31 +38,30 @@ _GROUP BY_. The sample code like this: fun sample() { database { PersonTable { table -> - table SELECT WHERE(age LTE 5) - table SELECT ORDER_BY(age to DESC) - table SELECT LIMIT(3) - table SELECT GROUP_BY(name) + table SELECT WHERE(age LTE 5) + table SELECT ORDER_BY(age to DESC) + table SELECT ORDER_BY(age) + table SELECT LIMIT(3) + table SELECT GROUP_BY(name) } } } ``` -In _ORDER BY_ clause, The sorting method (`ASC` or `DESC`) must be explicitly written. - ## Clause Connection Sometimes we need to use multiple clauses once. In SQL, some clauses must be added after other clauses. For example, the _HAVING_ behind with the _GROUP BY_. SQLlin makes sure you don't make mistakes in the order of clauses, the clauses connection regular like this chart: -|Clause/Statement| Can Connect | -|---|----------------------------------| +|Clause/Statement| Connectable | +|---|------------------------------| |SELECT| WHERE, ORDER BY, LIMIT, GROUP BY | -|WHERE| LIMIT, ORDER BY, GROUP BY | -|GROUP BY| HAVING, ORDER BY | -|HAVING| ORDER BY, LIMIT | -|ORDER BY| LIMIT | -|LIMIT| OFFSET | -|OFFSET| / | +|WHERE| LIMIT, ORDER BY, GROUP BY | +|GROUP BY| HAVING, ORDER BY | +|HAVING| ORDER BY, LIMIT | +|ORDER BY| LIMIT | +|LIMIT| OFFSET | +|OFFSET| / | A _SELECT_ statement with multiple clauses like this: diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/Database.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/Database.kt index 4e88466..17de530 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/Database.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/Database.kt @@ -19,42 +19,31 @@ package com.ctrip.sqllin.dsl import com.ctrip.sqllin.driver.DatabaseConfiguration import com.ctrip.sqllin.driver.DatabasePath import com.ctrip.sqllin.driver.openDatabase -import com.ctrip.sqllin.dsl.sql.* -import com.ctrip.sqllin.dsl.sql.clause.* -import com.ctrip.sqllin.dsl.sql.operation.Delete -import com.ctrip.sqllin.dsl.sql.operation.Insert -import com.ctrip.sqllin.dsl.sql.operation.Update -import com.ctrip.sqllin.dsl.sql.operation.Select -import com.ctrip.sqllin.dsl.sql.statement.* -import com.ctrip.sqllin.dsl.sql.statement.DatabaseExecuteEngine -import com.ctrip.sqllin.dsl.sql.statement.TransactionStatementsGroup import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import kotlinx.serialization.KSerializer -import kotlinx.serialization.modules.EmptySerializersModule -import kotlinx.serialization.serializer -import kotlin.concurrent.Volatile /** * Database object * @author yaqiao */ -@Suppress("UNCHECKED_CAST") public class Database( configuration: DatabaseConfiguration, + private val enableSimpleSQLLog: Boolean = true, ) { public constructor( name: String, path: DatabasePath, version: Int, + enableSimpleSQLLog: Boolean = true, ) : this( DatabaseConfiguration( name = name, path = path, version = version, - ) + ), + enableSimpleSQLLog, ) private val databaseConnection = openDatabase(configuration) @@ -67,284 +56,21 @@ public class Database( /** * Start a scope with this database object that used for execute SQL. */ - public operator fun invoke(block: Database.() -> T): T { - val result = block() - executeAllStatements(prepareForExecution()) + public operator fun invoke(block: DatabaseScope.() -> T): T { + val databaseScope = DatabaseScope(databaseConnection, enableSimpleSQLLog) + val result = databaseScope.block() + databaseScope.executeAllStatements() return result } - private val assembledMutex by lazy { Mutex() } private val executiveMutex by lazy { Mutex() } - public suspend infix fun suspendedScope(block: suspend Database.() -> T): T { - val (result, executiveLinkedList) = assembledMutex.withLock { - val result = block() - val executiveLinkedList = executiveMutex.withLock { - prepareForExecution() - } - result to executiveLinkedList - } + public suspend infix fun suspendedScope(block: suspend DatabaseScope.() -> T): T { + val databaseScope = DatabaseScope(databaseConnection, enableSimpleSQLLog) + val result = databaseScope.block() executiveMutex.withLock { - executeAllStatements(executiveLinkedList) + databaseScope.executeAllStatements() } return result } - - /** - * Transaction. - */ - - @Volatile - private var transactionStatementsGroup: TransactionStatementsGroup? = null - - private inline val isInTransaction - get() = transactionStatementsGroup != null - - public fun beginTransaction(): Boolean { - if (isInTransaction) - return false - transactionStatementsGroup = TransactionStatementsGroup(databaseConnection) - executiveEngine.addStatement(transactionStatementsGroup!!) - return true - } - - public fun endTransaction() { - transactionStatementsGroup = null - } - - public inline fun transaction(block: Database.() -> T): T { - beginTransaction() - try { - return block() - } finally { - endTransaction() - } - } - - /** - * SQL execute. - */ - - private val executiveEngine = DatabaseExecuteEngine() - - private fun addStatement(statement: SingleStatement) { - if (isInTransaction) - transactionStatementsGroup!!.addStatement(statement) - else - executiveEngine.addStatement(statement) - } - - private fun addSelectStatement(statement: SelectStatement) { - if (unionSelectStatementGroupStack.isNotEmpty) - (unionSelectStatementGroupStack.top as UnionSelectStatementGroup).addSelectStatement(statement) - else - addStatement(statement) - } - - private fun prepareForExecution() = executiveEngine.prepareForExecution() - private fun executeAllStatements(executiveLinkedList: StatementLinkedList) = - executiveEngine executeAllStatement executiveLinkedList - - /** - * Insert. - */ - - public infix fun Table.INSERT(entities: Iterable) { - val statement = Insert.insert(this, databaseConnection, entities) - addStatement(statement) - } - - public infix fun Table.INSERT(entity: T): Unit = - INSERT(listOf(entity)) - - /** - * Update. - */ - - public infix fun Table.UPDATE(clause: SetClause): UpdateStatementWithoutWhereClause = - transactionStatementsGroup?.let { - val statement = Update.update(this, databaseConnection, it, clause) - it addStatement statement - statement - } ?: Update.update(this, databaseConnection, executiveEngine, clause).also { - executiveEngine addStatement it - } - - /** - * Delete. - */ - - public infix fun Table<*>.DELETE(x: X) { - val statement = Delete.deleteAllEntity(this, databaseConnection) - addStatement(statement) - } - - public infix fun Table.DELETE(clause: WhereClause) { - val statement = Delete.delete(this, databaseConnection, clause) - addStatement(statement) - } - - /** - * Select. - */ - - /** - * Select with no any clause. - */ - public inline infix fun Table.SELECT(x: X): FinalSelectStatement = - select(kSerializer(), false) - - public inline infix fun Table.SELECT_DISTINCT(x: X): FinalSelectStatement = - select(kSerializer(), true) - - public fun Table.select(serializer: KSerializer, isDistinct: Boolean): FinalSelectStatement { - val container = getSelectStatementGroup() - val statement = Select.select(this, isDistinct, serializer, databaseConnection, container) - addSelectStatement(statement) - return statement - } - - /** - * Receive the 'WHERE' clause. - */ - public inline infix fun Table.SELECT(clause: WhereClause): WhereSelectStatement = - select(kSerializer(), clause, false) - - public inline infix fun Table.SELECT_DISTINCT(clause: WhereClause): WhereSelectStatement = - select(kSerializer(), clause, true) - - public fun Table.select(serializer: KSerializer, clause: WhereClause, isDistinct: Boolean): WhereSelectStatement { - val container = getSelectStatementGroup() - val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) - addSelectStatement(statement) - return statement - } - - /** - * Receive the 'ORDER BY' clause. - */ - public inline infix fun Table.SELECT(clause: OrderByClause): OrderBySelectStatement = - select(kSerializer(), clause, false) - - public inline infix fun Table.SELECT_DISTINCT(clause: OrderByClause): OrderBySelectStatement = - select(kSerializer(), clause, true) - - public fun Table.select(serializer: KSerializer, clause: OrderByClause, isDistinct: Boolean): OrderBySelectStatement { - val container = getSelectStatementGroup() - val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) - addSelectStatement(statement) - return statement - } - - /** - * Receive the 'LIMIT' clause. - */ - public inline infix fun Table.SELECT(clause: LimitClause): LimitSelectStatement = - select(kSerializer(), clause, false) - - public inline infix fun Table.SELECT_DISTINCT(clause: LimitClause): LimitSelectStatement = - select(kSerializer(), clause, true) - - public fun Table.select(serializer: KSerializer, clause: LimitClause, isDistinct: Boolean): LimitSelectStatement { - val container = getSelectStatementGroup() - val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) - addSelectStatement(statement) - return statement - } - - /** - * Receive the 'GROUP BY' clause. - */ - public inline infix fun Table.SELECT(clause: GroupByClause): GroupBySelectStatement = - select(kSerializer(), clause, false) - - public inline infix fun Table.SELECT_DISTINCT(clause: GroupByClause): GroupBySelectStatement = - select(kSerializer(), clause, true) - - public fun Table.select(serializer: KSerializer, clause: GroupByClause, isDistinct: Boolean): GroupBySelectStatement { - val container = getSelectStatementGroup() - val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) - addSelectStatement(statement) - return statement - } - - public inline fun getKSerializer(): KSerializer = EmptySerializersModule().serializer() - - /** - * The 'UNION' clause of Select. - */ - - private val unionSelectStatementGroupStack by lazy { Stack>() } - - private fun getSelectStatementGroup(): StatementContainer = unionSelectStatementGroupStack.top ?: transactionStatementsGroup ?: executiveEngine - - public inline fun Table.UNION(block: Table.(Table) -> Unit): FinalSelectStatement { - beginUnion() - var selectStatement: SelectStatement? = null - try { - block(this) - selectStatement = createUnionSelectStatement(false) - return selectStatement - } finally { - endUnion(selectStatement) - } - } - - public inline fun Table.UNION_ALL(block: Table.(Table) -> Unit): FinalSelectStatement { - beginUnion() - var selectStatement: SelectStatement? = null - try { - block(this) - selectStatement = createUnionSelectStatement(true) - return selectStatement - } finally { - endUnion(selectStatement) - } - } - - public fun beginUnion() { - unionSelectStatementGroupStack.push(UnionSelectStatementGroup()) - } - - public fun createUnionSelectStatement(isUnionAll: Boolean): FinalSelectStatement { - check(unionSelectStatementGroupStack.isNotEmpty) { "Please invoke the 'beginUnion' before you invoke this function!!!" } - return (unionSelectStatementGroupStack.top as UnionSelectStatementGroup).unionStatements(isUnionAll) - } - - public fun endUnion(selectStatement: SelectStatement?) { - unionSelectStatementGroupStack.pop() - selectStatement?.let { addSelectStatement(it) } - } - - /** - * Receive the 'JOIN' clause. - */ - - public inline infix fun Table.SELECT(clause: JoinClause): JoinStatementWithoutCondition = - select(getKSerializer(), clause, false) - - public inline infix fun Table.SELECT_DISTINCT(clause: JoinClause): JoinStatementWithoutCondition = - select(getKSerializer(), clause, true) - - public fun Table.select(serializer: KSerializer, clause: JoinClause, isDistinct: Boolean): JoinStatementWithoutCondition { - val container = getSelectStatementGroup() - return Select.select(this, clause, isDistinct, serializer, databaseConnection, container, ::addSelectStatement) - } - - /** - * Receive the natural join clause(includes 'NATURAL LEFT OUTER JOIN' and 'NATURAL INNER JOIN'). - */ - - public inline infix fun Table.SELECT(clause: NaturalJoinClause): JoinSelectStatement = - select(getKSerializer(), clause, false) - - public inline infix fun Table.SELECT_DISTINCT(clause: NaturalJoinClause): JoinSelectStatement = - select(getKSerializer(), clause, true) - - public fun Table.select(serializer: KSerializer, clause: NaturalJoinClause, isDistinct: Boolean): JoinSelectStatement { - val container = getSelectStatementGroup() - val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) - addSelectStatement(statement) - return statement - } -} +} \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/DatabaseScope.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/DatabaseScope.kt new file mode 100644 index 0000000..2e3a3fc --- /dev/null +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/DatabaseScope.kt @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2022 Ctrip.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.dsl + +import com.ctrip.sqllin.driver.DatabaseConnection +import com.ctrip.sqllin.dsl.sql.Table +import com.ctrip.sqllin.dsl.sql.X +import com.ctrip.sqllin.dsl.sql.clause.* +import com.ctrip.sqllin.dsl.sql.operation.Delete +import com.ctrip.sqllin.dsl.sql.operation.Insert +import com.ctrip.sqllin.dsl.sql.operation.Select +import com.ctrip.sqllin.dsl.sql.operation.Update +import com.ctrip.sqllin.dsl.sql.statement.* +import kotlinx.serialization.KSerializer +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.serializer +import kotlin.concurrent.Volatile + +/** + * The database scope, it's used to restrict the scope that write DSL SQL statements + * @author yaqiao + */ + +@Suppress("UNCHECKED_CAST") +public class DatabaseScope internal constructor( + private val databaseConnection: DatabaseConnection, + private val enableSimpleSQLLog: Boolean, +) { + + /** + * Transaction. + */ + + @Volatile + private var transactionStatementsGroup: TransactionStatementsGroup? = null + + private inline val isInTransaction + get() = transactionStatementsGroup != null + + public fun beginTransaction(): Boolean { + if (isInTransaction) + return false + transactionStatementsGroup = TransactionStatementsGroup(databaseConnection, enableSimpleSQLLog) + executiveEngine.addStatement(transactionStatementsGroup!!) + return true + } + + public fun endTransaction() { + transactionStatementsGroup = null + } + + public inline fun transaction(block: DatabaseScope.() -> T): T { + beginTransaction() + try { + return block() + } finally { + endTransaction() + } + } + + /** + * SQL execute. + */ + + private val executiveEngine = DatabaseExecuteEngine(enableSimpleSQLLog) + + private fun addStatement(statement: SingleStatement) { + if (isInTransaction) + transactionStatementsGroup!!.addStatement(statement) + else + executiveEngine.addStatement(statement) + } + + private fun addSelectStatement(statement: SelectStatement) { + if (unionSelectStatementGroupStack.isNotEmpty) + (unionSelectStatementGroupStack.top as UnionSelectStatementGroup).addSelectStatement(statement) + else + addStatement(statement) + } + + internal fun executeAllStatements() = executiveEngine.executeAllStatement() + + /** + * Insert. + */ + + public infix fun Table.INSERT(entities: Iterable) { + val statement = Insert.insert(this, databaseConnection, entities) + addStatement(statement) + } + + public infix fun Table.INSERT(entity: T): Unit = + INSERT(listOf(entity)) + + /** + * Update. + */ + + public infix fun Table.UPDATE(clause: SetClause): UpdateStatementWithoutWhereClause = + transactionStatementsGroup?.let { + val statement = Update.update(this, databaseConnection, it, clause) + it addStatement statement + statement + } ?: Update.update(this, databaseConnection, executiveEngine, clause).also { + executiveEngine addStatement it + } + + /** + * Delete. + */ + + public infix fun Table<*>.DELETE(x: X) { + val statement = Delete.deleteAllEntity(this, databaseConnection) + addStatement(statement) + } + + public infix fun Table.DELETE(clause: WhereClause) { + val statement = Delete.delete(this, databaseConnection, clause) + addStatement(statement) + } + + /** + * Select. + */ + + /** + * Select with no any clause. + */ + public inline infix fun Table.SELECT(x: X): FinalSelectStatement = + select(kSerializer(), false) + + public inline infix fun Table.SELECT_DISTINCT(x: X): FinalSelectStatement = + select(kSerializer(), true) + + public fun Table.select(serializer: KSerializer, isDistinct: Boolean): FinalSelectStatement { + val container = getSelectStatementGroup() + val statement = Select.select(this, isDistinct, serializer, databaseConnection, container) + addSelectStatement(statement) + return statement + } + + /** + * Receive the 'WHERE' clause. + */ + public inline infix fun Table.SELECT(clause: WhereClause): WhereSelectStatement = + select(kSerializer(), clause, false) + + public inline infix fun Table.SELECT_DISTINCT(clause: WhereClause): WhereSelectStatement = + select(kSerializer(), clause, true) + + public fun Table.select(serializer: KSerializer, clause: WhereClause, isDistinct: Boolean): WhereSelectStatement { + val container = getSelectStatementGroup() + val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) + addSelectStatement(statement) + return statement + } + + /** + * Receive the 'ORDER BY' clause. + */ + public inline infix fun Table.SELECT(clause: OrderByClause): OrderBySelectStatement = + select(kSerializer(), clause, false) + + public inline infix fun Table.SELECT_DISTINCT(clause: OrderByClause): OrderBySelectStatement = + select(kSerializer(), clause, true) + + public fun Table.select(serializer: KSerializer, clause: OrderByClause, isDistinct: Boolean): OrderBySelectStatement { + val container = getSelectStatementGroup() + val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) + addSelectStatement(statement) + return statement + } + + /** + * Receive the 'LIMIT' clause. + */ + public inline infix fun Table.SELECT(clause: LimitClause): LimitSelectStatement = + select(kSerializer(), clause, false) + + public inline infix fun Table.SELECT_DISTINCT(clause: LimitClause): LimitSelectStatement = + select(kSerializer(), clause, true) + + public fun Table.select(serializer: KSerializer, clause: LimitClause, isDistinct: Boolean): LimitSelectStatement { + val container = getSelectStatementGroup() + val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) + addSelectStatement(statement) + return statement + } + + /** + * Receive the 'GROUP BY' clause. + */ + public inline infix fun Table.SELECT(clause: GroupByClause): GroupBySelectStatement = + select(kSerializer(), clause, false) + + public inline infix fun Table.SELECT_DISTINCT(clause: GroupByClause): GroupBySelectStatement = + select(kSerializer(), clause, true) + + public fun Table.select(serializer: KSerializer, clause: GroupByClause, isDistinct: Boolean): GroupBySelectStatement { + val container = getSelectStatementGroup() + val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) + addSelectStatement(statement) + return statement + } + + public inline fun getKSerializer(): KSerializer = EmptySerializersModule().serializer() + + /** + * The 'UNION' clause of Select. + */ + + private val unionSelectStatementGroupStack by lazy { Stack>() } + + private fun getSelectStatementGroup(): StatementContainer = unionSelectStatementGroupStack.top ?: transactionStatementsGroup ?: executiveEngine + + public inline fun Table.UNION(block: Table.(Table) -> Unit): FinalSelectStatement { + beginUnion() + var selectStatement: SelectStatement? = null + try { + block(this) + selectStatement = createUnionSelectStatement(false) + return selectStatement + } finally { + endUnion(selectStatement) + } + } + + public inline fun Table.UNION_ALL(block: Table.(Table) -> Unit): FinalSelectStatement { + beginUnion() + var selectStatement: SelectStatement? = null + try { + block(this) + selectStatement = createUnionSelectStatement(true) + return selectStatement + } finally { + endUnion(selectStatement) + } + } + + public fun beginUnion() { + unionSelectStatementGroupStack.push(UnionSelectStatementGroup()) + } + + public fun createUnionSelectStatement(isUnionAll: Boolean): FinalSelectStatement { + check(unionSelectStatementGroupStack.isNotEmpty) { "Please invoke the 'beginUnion' before you invoke this function!!!" } + return (unionSelectStatementGroupStack.top as UnionSelectStatementGroup).unionStatements(isUnionAll) + } + + public fun endUnion(selectStatement: SelectStatement?) { + unionSelectStatementGroupStack.pop() + selectStatement?.let { addSelectStatement(it) } + } + + /** + * Receive the 'JOIN' clause. + */ + + public inline infix fun Table.SELECT(clause: JoinClause): JoinStatementWithoutCondition = + select(getKSerializer(), clause, false) + + public inline infix fun Table.SELECT_DISTINCT(clause: JoinClause): JoinStatementWithoutCondition = + select(getKSerializer(), clause, true) + + public fun Table.select(serializer: KSerializer, clause: JoinClause, isDistinct: Boolean): JoinStatementWithoutCondition { + val container = getSelectStatementGroup() + return Select.select(this, clause, isDistinct, serializer, databaseConnection, container, ::addSelectStatement) + } + + /** + * Receive the natural join clause(includes 'NATURAL LEFT OUTER JOIN' and 'NATURAL INNER JOIN'). + */ + + public inline infix fun Table.SELECT(clause: NaturalJoinClause): JoinSelectStatement = + select(getKSerializer(), clause, false) + + public inline infix fun Table.SELECT_DISTINCT(clause: NaturalJoinClause): JoinSelectStatement = + select(getKSerializer(), clause, true) + + public fun Table.select(serializer: KSerializer, clause: NaturalJoinClause, isDistinct: Boolean): JoinSelectStatement { + val container = getSelectStatementGroup() + val statement = Select.select(this, clause, isDistinct, serializer, databaseConnection, container) + addSelectStatement(statement) + return statement + } +} \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/GroupByClause.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/GroupByClause.kt index e648091..5f4385b 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/GroupByClause.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/GroupByClause.kt @@ -25,15 +25,17 @@ import com.ctrip.sqllin.dsl.sql.statement.WhereSelectStatement * @author yaqiao */ -public class GroupByClause internal constructor(private val columnNames: List) : SelectClause { +public class GroupByClause internal constructor(private val columnNames: Iterable) : SelectClause { override val clauseStr: String get() = buildString { append(" GROUP BY ") - columnNames.forEachIndexed { index, clauseElement -> - append(clauseElement.valueName) - if (index < columnNames.lastIndex) - append(',') + val iterator = columnNames.iterator() + require(iterator.hasNext()) { "Please provider at least one 'BaseClauseElement' for 'GROUP BY' clause!!!" } + append(iterator.next().valueName) + while (iterator.hasNext()) { + append(',') + append(iterator.next().valueName) } } } @@ -46,8 +48,7 @@ public infix fun WhereSelectStatement.GROUP_BY(element: ClauseElement): G } public infix fun WhereSelectStatement.GROUP_BY(elements: Iterable): GroupBySelectStatement { - val elementList = if (elements is List) elements else elements.toList() - val statement = appendToGroupBy(GroupByClause(elementList)) + val statement = appendToGroupBy(GroupByClause(elements)) container changeLastStatement statement return statement } @@ -58,8 +59,7 @@ public infix fun JoinSelectStatement.GROUP_BY(element: ClauseElement): Gr } public infix fun JoinSelectStatement.GROUP_BY(elements: Iterable): GroupBySelectStatement { - val elementList = if (elements is List) elements else elements.toList() - val statement = appendToGroupBy(GroupByClause(elementList)) + val statement = appendToGroupBy(GroupByClause(elements)) container changeLastStatement statement return statement } \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/OrderByClause.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/OrderByClause.kt index 95f476c..11db8f9 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/OrderByClause.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/OrderByClause.kt @@ -23,17 +23,19 @@ import com.ctrip.sqllin.dsl.sql.statement.* * @author yaqiao */ -public class OrderByClause internal constructor(private val column2WayMap: Map) : SelectClause { +public sealed interface OrderByClause : SelectClause + +internal class CompleteOrderByClause(private val column2WayMap: Map) : OrderByClause { override val clauseStr: String get() { - require(column2WayMap.isNotEmpty()) { "Please provider at least one 'BaseClauseElement' -> 'OrderByWay' entry when you use the 'ORDER BY' clause!!!" } + require(column2WayMap.isNotEmpty()) { "Please provider at least one 'BaseClauseElement' -> 'OrderByWay' entry for 'ORDER BY' clause!!!" } return buildString { append(" ORDER BY ") val iterator = column2WayMap.entries.iterator() do { - val (number, way) = iterator.next() - append(number.valueName) + val (element, way) = iterator.next() + append(element.valueName) append(' ') append(way.str) val hasNext = iterator.hasNext() @@ -50,14 +52,14 @@ public enum class OrderByWay(internal val str: String) { } public fun ORDER_BY(vararg column2Ways: Pair): OrderByClause = - OrderByClause(mapOf(*column2Ways)) + CompleteOrderByClause(mapOf(*column2Ways)) @Suppress("NOTHING_TO_INLINE") public inline infix fun WhereSelectStatement.ORDER_BY(column2Way: Pair): OrderBySelectStatement = ORDER_BY(mapOf(column2Way)) public infix fun WhereSelectStatement.ORDER_BY(column2WayMap: Map): OrderBySelectStatement = - appendToOrderBy(OrderByClause(column2WayMap)).also { + appendToOrderBy(CompleteOrderByClause(column2WayMap)).also { container changeLastStatement it } @@ -66,7 +68,7 @@ public inline infix fun HavingSelectStatement.ORDER_BY(column2Way: Pair HavingSelectStatement.ORDER_BY(column2WayMap: Map): OrderBySelectStatement = - appendToOrderBy(OrderByClause(column2WayMap)).also { + appendToOrderBy(CompleteOrderByClause(column2WayMap)).also { container changeLastStatement it } @@ -75,7 +77,7 @@ public inline infix fun GroupBySelectStatement.ORDER_BY(column2Way: Pair< ORDER_BY(mapOf(column2Way)) public infix fun GroupBySelectStatement.ORDER_BY(column2WayMap: Map): OrderBySelectStatement = - appendToOrderBy(OrderByClause(column2WayMap)).also { + appendToOrderBy(CompleteOrderByClause(column2WayMap)).also { container changeLastStatement it } @@ -84,6 +86,62 @@ public inline infix fun JoinSelectStatement.ORDER_BY(column2Way: Pair JoinSelectStatement.ORDER_BY(column2WayMap: Map): OrderBySelectStatement = - appendToOrderBy(OrderByClause(column2WayMap)).also { + appendToOrderBy(CompleteOrderByClause(column2WayMap)).also { + container changeLastStatement it + } + +internal class SimpleOrderByClause(private val columns: Iterable) : OrderByClause { + + override val clauseStr: String + get() { + val iterator = columns.iterator() + require(iterator.hasNext()) { "Please provider at least one 'BaseClauseElement' for 'ORDER BY' clause!!!" } + return buildString { + append(" ORDER BY ") + append(iterator.next().valueName) + while (iterator.hasNext()) { + append(',') + append(iterator.next().valueName) + } + append(' ') + } + } +} +public fun ORDER_BY(vararg elements: ClauseElement): OrderByClause = + SimpleOrderByClause(elements.toList()) + +@Suppress("NOTHING_TO_INLINE") +public inline infix fun WhereSelectStatement.ORDER_BY(column: ClauseElement): OrderBySelectStatement = + ORDER_BY(listOf(column)) + +public infix fun WhereSelectStatement.ORDER_BY(columns: Iterable): OrderBySelectStatement = + appendToOrderBy(SimpleOrderByClause(columns)).also { + container changeLastStatement it + } + +@Suppress("NOTHING_TO_INLINE") +public inline infix fun HavingSelectStatement.ORDER_BY(column: ClauseElement): OrderBySelectStatement = + ORDER_BY(listOf(column)) + +public infix fun HavingSelectStatement.ORDER_BY(columns: Iterable): OrderBySelectStatement = + appendToOrderBy(SimpleOrderByClause(columns)).also { + container changeLastStatement it + } + +@Suppress("NOTHING_TO_INLINE") +public inline infix fun GroupBySelectStatement.ORDER_BY(column: ClauseElement): OrderBySelectStatement = + ORDER_BY(listOf(column)) + +public infix fun GroupBySelectStatement.ORDER_BY(columns: Iterable): OrderBySelectStatement = + appendToOrderBy(SimpleOrderByClause(columns)).also { + container changeLastStatement it + } + +@Suppress("NOTHING_TO_INLINE") +public inline infix fun JoinSelectStatement.ORDER_BY(column: ClauseElement): OrderBySelectStatement = + ORDER_BY(listOf(column)) + +public infix fun JoinSelectStatement.ORDER_BY(columns: Iterable): OrderBySelectStatement = + appendToOrderBy(SimpleOrderByClause(columns)).also { container changeLastStatement it } \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/DatabaseExecuteEngine.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/DatabaseExecuteEngine.kt index 0f0a6a7..f21bd94 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/DatabaseExecuteEngine.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/DatabaseExecuteEngine.kt @@ -16,44 +16,38 @@ package com.ctrip.sqllin.dsl.sql.statement -import kotlin.concurrent.Volatile - /** * Collect and execute all SQL statement in 'database {}' block * @author yaqiao */ -internal class DatabaseExecuteEngine : StatementContainer { +internal class DatabaseExecuteEngine( + private val enableSimpleSQLLog: Boolean, +) : StatementContainer { - @Volatile - private var statementsLinkedList: StatementLinkedList? = null + private lateinit var statementsLinkedList: StatementLinkedList override infix fun changeLastStatement(statement: SingleStatement) { - if (statementsLinkedList?.lastStatement is UpdateStatementWithoutWhereClause<*> - || statementsLinkedList?.lastStatement is SelectStatement<*>) - statementsLinkedList!!.resetLastStatement(statement) + if (statementsLinkedList.lastStatement is UpdateStatementWithoutWhereClause<*> + || statementsLinkedList.lastStatement is SelectStatement<*>) + statementsLinkedList.resetLastStatement(statement) else throw IllegalStateException("Current statement can't append clause.") } infix fun addStatement(statement: ExecutableStatement) { - if (statementsLinkedList != null) - statementsLinkedList!!.addStatement(statement) + if (::statementsLinkedList.isInitialized) + statementsLinkedList.addStatement(statement) else statementsLinkedList = StatementLinkedList(statement) } - fun prepareForExecution(): StatementLinkedList { - val executiveLinkedList = statementsLinkedList - statementsLinkedList = null - return executiveLinkedList ?: throw IllegalStateException("The statementsLinkedList can't be null") - } - - infix fun executeAllStatement(executiveLinkedList: StatementLinkedList) { - executiveLinkedList.forEach { + fun executeAllStatement() { + statementsLinkedList.forEach { when (it) { is SingleStatement -> { - println("SQL String: ${it.sqlStr}") + if (enableSimpleSQLLog) + println("SQL String: ${it.sqlStr}") it.execute() } is TransactionStatementsGroup -> it.execute() diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/SelectStatement.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/SelectStatement.kt index 4157726..666bf28 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/SelectStatement.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/SelectStatement.kt @@ -16,8 +16,8 @@ package com.ctrip.sqllin.dsl.sql.statement +import com.ctrip.sqllin.driver.CommonCursor import com.ctrip.sqllin.driver.DatabaseConnection -import com.ctrip.sqllin.driver.withQuery import com.ctrip.sqllin.dsl.sql.Table import com.ctrip.sqllin.dsl.sql.clause.* import com.ctrip.sqllin.dsl.sql.compiler.QueryDecoder @@ -39,19 +39,23 @@ public sealed class SelectStatement( @Volatile private var result: List? = null + @Volatile + private var cursor: CommonCursor? = null + final override fun execute() { - result = connection.withQuery(sqlStr) { - val decoder = QueryDecoder(it) - buildList { - it.forEachRows { - add(decoder.decodeSerializableValue(deserializer)) - } - } - } + cursor = connection.query(sqlStr) } - public fun getResults(): List = - result ?: throw IllegalStateException("You have to call 'execute' function before call 'getResults'!!!") + @OptIn(ExperimentalStdlibApi::class) + public fun getResults(): List = result ?: cursor?.use { + val decoder = QueryDecoder(it) + result = buildList { + it.forEachRow { + add(decoder.decodeSerializableValue(deserializer)) + } + } + result!! + } ?: throw IllegalStateException("You have to call 'execute' function before call 'getResults'!!!") protected fun buildSQL(clause: SelectClause): String = buildString { append(sqlStr) diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/TransactionStatementsGroup.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/TransactionStatementsGroup.kt index b9ff9e7..e6a39c1 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/TransactionStatementsGroup.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/TransactionStatementsGroup.kt @@ -26,6 +26,7 @@ import com.ctrip.sqllin.driver.withTransaction internal class TransactionStatementsGroup( private val databaseConnection: DatabaseConnection, + private val enableSimpleSQLLog: Boolean, ) : ExecutableStatement, StatementContainer { private lateinit var statementList: StatementLinkedList @@ -39,7 +40,8 @@ internal class TransactionStatementsGroup( override fun execute() = databaseConnection.withTransaction { statementList.forEach { - println("SQL String: ${it.sqlStr}") + if (enableSimpleSQLLog) + println("SQL String: ${it.sqlStr}") it.execute() } } 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 5fbd036..ae2fbfa 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 @@ -154,12 +154,16 @@ class CommonBasicTest(private val path: DatabasePath) { val book1 = Book(name = "Kotlin Cookbook", author = "Ken Kousen", pages = 251, price = 37.72) val book2 = Book(name = "The Lost Symbol", author = "Dan Brown", pages = 510, price = 19.95) var statementOfOrderBy: SelectStatement? = null + var statementOfOrderBy2: SelectStatement? = null var statementOfWhereAndOrderBy: SelectStatement? = null + var statementOfWhereAndOrderBy2: SelectStatement? = null database { BookTable { table -> table INSERT listOf(book0, book1, book2) statementOfOrderBy = table SELECT ORDER_BY(price to DESC) + statementOfOrderBy2 = table SELECT ORDER_BY(price) statementOfWhereAndOrderBy = table SELECT WHERE(author EQ "Dan Brown") ORDER_BY mapOf(pages to ASC) + statementOfWhereAndOrderBy2 = table SELECT WHERE(author EQ "Dan Brown") ORDER_BY pages } } assertEquals(3, statementOfOrderBy?.getResults()?.size) @@ -173,6 +177,17 @@ class CommonBasicTest(private val path: DatabasePath) { assertEquals(actualBook, book) } + assertEquals(3, statementOfOrderBy2?.getResults()?.size) + statementOfOrderBy2!!.getResults().forEachIndexed { index, book -> + val actualBook = when (index) { + 0 -> book0 + 1 -> book2 + 2 -> book1 + else -> throw IllegalStateException("Select got some wrong") + } + assertEquals(actualBook, book) + } + assertEquals(2, statementOfWhereAndOrderBy?.getResults()?.size) statementOfWhereAndOrderBy!!.getResults().forEachIndexed { index, book -> val actualBook = when (index) { @@ -182,6 +197,16 @@ class CommonBasicTest(private val path: DatabasePath) { } assertEquals(actualBook, book) } + + assertEquals(2, statementOfWhereAndOrderBy2?.getResults()?.size) + statementOfWhereAndOrderBy2!!.getResults().forEachIndexed { index, book -> + val actualBook = when (index) { + 0 -> book0 + 1 -> book2 + else -> throw IllegalStateException("Select got some wrong") + } + assertEquals(actualBook, book) + } } fun testSelectLimitAndOffsetClause() = Database(getDefaultDBConfig()).databaseAutoClose { database -> @@ -353,33 +378,32 @@ class CommonBasicTest(private val path: DatabasePath) { database suspendedScope { statement = BookTable { table -> table INSERT listOf(book1, book2) - delay(100) table SELECT X } } - assertEquals(true, statement!!.getResults().any { it == book1 }) - assertEquals(true, statement!!.getResults().any { it == book2 }) - } - 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? = null - delay(50) - 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 + 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? = 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 }) } Unit }