diff --git a/CHANGELOG.md b/CHANGELOG.md index f467b7d..3edceeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ * Update `kotlinx.coroutines`'s version to `1.8.0` * Update `kotlinx.serialization`'s version to `1.6.3` -* Modified the SQL statements' splicing method, that fixed the [issue#77](https://github.com/ctripcorp/SQLlin/issues/77) that users can't read/write special symbols as the values in SQL statements. +* Modify the SQL statements' splicing method, that fixed the [issue#77](https://github.com/ctripcorp/SQLlin/issues/77) that users can't read/write special symbols as the values in SQL statements. +* Performance optimization, use `ArrayDeque` to replace the LinkedList for SQL statements management (self-implemented) ### sqllin-driver 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 index 99ec305..bc56333 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/DatabaseScope.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/DatabaseScope.kt @@ -86,8 +86,8 @@ public class DatabaseScope internal constructor( } private fun addSelectStatement(statement: SelectStatement) { - if (unionSelectStatementGroupStack.isNotEmpty) - (unionSelectStatementGroupStack.top as UnionSelectStatementGroup).addSelectStatement(statement) + if (unionSelectStatementGroupStack.isNotEmpty()) + (unionSelectStatementGroupStack.last() as UnionSelectStatementGroup).addSelectStatement(statement) else addStatement(statement) } @@ -223,9 +223,9 @@ public class DatabaseScope internal constructor( * The 'UNION' clause of Select. */ - private val unionSelectStatementGroupStack by lazy { Stack>() } + private val unionSelectStatementGroupStack by lazy { ArrayDeque>() } - private fun getSelectStatementGroup(): StatementContainer = unionSelectStatementGroupStack.top ?: transactionStatementsGroup ?: executiveEngine + private fun getSelectStatementGroup(): StatementContainer = unionSelectStatementGroupStack.lastOrNull() ?: transactionStatementsGroup ?: executiveEngine public inline fun Table.UNION(block: Table.(Table) -> Unit): FinalSelectStatement { beginUnion() @@ -252,16 +252,16 @@ public class DatabaseScope internal constructor( } public fun beginUnion() { - unionSelectStatementGroupStack.push(UnionSelectStatementGroup()) + unionSelectStatementGroupStack.add(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) + check(unionSelectStatementGroupStack.isNotEmpty()) { "Please invoke the 'beginUnion' before you invoke this function!!!" } + return (unionSelectStatementGroupStack.last() as UnionSelectStatementGroup).unionStatements(isUnionAll) } public fun endUnion(selectStatement: SelectStatement?) { - unionSelectStatementGroupStack.pop() + unionSelectStatementGroupStack.removeLast() selectStatement?.let { addSelectStatement(it) } } 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 f21bd94..dd98d59 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 @@ -25,25 +25,23 @@ internal class DatabaseExecuteEngine( private val enableSimpleSQLLog: Boolean, ) : StatementContainer { - private lateinit var statementsLinkedList: StatementLinkedList + private val statementList = ArrayDeque() override infix fun changeLastStatement(statement: SingleStatement) { - if (statementsLinkedList.lastStatement is UpdateStatementWithoutWhereClause<*> - || statementsLinkedList.lastStatement is SelectStatement<*>) - statementsLinkedList.resetLastStatement(statement) - else + if (statementList.lastOrNull() is UpdateStatementWithoutWhereClause<*> + || statementList.lastOrNull() is SelectStatement<*>) { + statementList.removeLast() + statementList.add(statement) + } else throw IllegalStateException("Current statement can't append clause.") } infix fun addStatement(statement: ExecutableStatement) { - if (::statementsLinkedList.isInitialized) - statementsLinkedList.addStatement(statement) - else - statementsLinkedList = StatementLinkedList(statement) + statementList.add(statement) } fun executeAllStatement() { - statementsLinkedList.forEach { + statementList.forEach { when (it) { is SingleStatement -> { if (enableSimpleSQLLog) diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/Node.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/Node.kt deleted file mode 100644 index f9420b2..0000000 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/Node.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.sql.statement - -/** - * A sample data struct that has two pointers - * @author yaqiao - */ - -internal class Node( - val element: T, - var pre: Node? = null, - var next: Node? = null, -) \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/Stack.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/Stack.kt deleted file mode 100644 index e8050a8..0000000 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/Stack.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.sql.statement - -import kotlin.concurrent.Volatile - -/** - * A sample Stack implementation - * @author yaqiao - */ - -internal class Stack { - - @Volatile - private var topNode: Node? = null - - val isEmpty - get() = topNode == null - - val isNotEmpty - get() = !isEmpty - - val top: T? - get() = topNode?.element - - fun pop(): T? { - var value: T? = null - topNode = topNode?.let { - value = it.element - val newTopNode = it.next?.apply { pre = null } - it.next = null - newTopNode - } - return value - } - - fun push(e: T) { - val newNode = Node(e) - if (isEmpty) - topNode = newNode - else { - topNode!!.pre = newNode - newNode.next = topNode - topNode = newNode - } - } -} \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/StatementLinkedList.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/StatementLinkedList.kt deleted file mode 100644 index 77338aa..0000000 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/StatementLinkedList.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.sql.statement - -/** - * Single LinkedList that used for store 'CompleteStatement' - * @author yaqiao - */ - -internal class StatementLinkedList(firstStatement: T) : Iterator { - - private var firstNode = Node(firstStatement) - private var lastNode = firstNode - - val lastStatement: T - get() = lastNode.element - - infix fun addStatement(statement: T) { - val node = Node(statement) - lastNode.next = node - node.pre = lastNode - lastNode = node - } - - private var forEachNode: Node? = firstNode - - override fun hasNext(): Boolean = forEachNode != null - - override fun next(): T = forEachNode?.apply { - forEachNode = next - }?.element ?: throw IllegalStateException("Engine must not be empty!!") - - infix fun resetLastStatement(statement: T) { - val secondLastNode = lastNode.pre - val isOnlyOneNode = firstNode === lastNode - lastNode.pre = null - lastNode = Node(statement) - if (isOnlyOneNode) { - firstNode = lastNode - forEachNode = lastNode - } - secondLastNode?.run { - next = lastNode - lastNode.pre = this - } - } -} \ No newline at end of file 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 e6a39c1..ce04053 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 @@ -29,13 +29,10 @@ internal class TransactionStatementsGroup( private val enableSimpleSQLLog: Boolean, ) : ExecutableStatement, StatementContainer { - private lateinit var statementList: StatementLinkedList + private val statementList = ArrayDeque() infix fun addStatement(statement: SingleStatement) { - if (this::statementList.isInitialized) - statementList.addStatement(statement) - else - statementList = StatementLinkedList(statement) + statementList.add(statement) } override fun execute() = databaseConnection.withTransaction { @@ -47,10 +44,11 @@ internal class TransactionStatementsGroup( } override infix fun changeLastStatement(statement: SingleStatement) { - if (statementList.lastStatement is UpdateStatementWithoutWhereClause<*> - || statementList.lastStatement is SelectStatement<*>) - statementList resetLastStatement statement - else + if (statementList.lastOrNull() is UpdateStatementWithoutWhereClause<*> + || statementList.lastOrNull() is SelectStatement<*>) { + statementList.removeLast() + statementList.add(statement) + } else throw IllegalStateException("Current statement can't append clause.") } } \ No newline at end of file diff --git a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/UnionSelectStatementGroup.kt b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/UnionSelectStatementGroup.kt index 7a6e704..5db43a1 100644 --- a/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/UnionSelectStatementGroup.kt +++ b/sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/statement/UnionSelectStatementGroup.kt @@ -23,43 +23,31 @@ package com.ctrip.sqllin.dsl.sql.statement internal class UnionSelectStatementGroup : StatementContainer { - private var statementLinkedList: StatementLinkedList>? = null + private val statementList = ArrayDeque>() infix fun addSelectStatement(selectStatement: SelectStatement) { - if (statementLinkedList != null) - statementLinkedList!!.addStatement(selectStatement) - else - statementLinkedList = StatementLinkedList(selectStatement) + statementList.add(selectStatement) } internal fun unionStatements(isUnionAll: Boolean): FinalSelectStatement { - require(statementLinkedList?.hasNext() == true) { "Please write at least two 'select' statements on 'UNION' scope" } - var firstStatement: SelectStatement? = null + require(statementList.isNotEmpty()) { "Please write at least two 'select' statements on 'UNION' scope" } var parameters: MutableList? = null val unionSqlStr = buildString { - statementLinkedList!!.run { - val unionKeyWord = if (isUnionAll) " UNION ALL " else " UNION " - do { - val next = next() - append(next.sqlStr) - val hasNext = hasNext() - if (firstStatement == null) { - firstStatement = next - if (!hasNext) - throw IllegalStateException("Please write at least two 'select' statements on 'UNION' scope") - } - if (parameters == null) { - parameters = next.parameters - } else next.parameters?.let { - parameters!!.addAll(it) - } - if (hasNext) - append(unionKeyWord) - } while (hasNext) + check(statementList.size > 1) { "Please write at least two 'select' statements on 'UNION' scope" } + val unionKeyWord = if (isUnionAll) " UNION ALL " else " UNION " + statementList.forEachIndexed { index, statement -> + append(statement.sqlStr) + if (parameters == null) + parameters = statement.parameters + else statement.parameters?.let { + parameters!!.addAll(it) + } + if (index != statementList.lastIndex) + append(unionKeyWord) } } - return firstStatement!!.run { + return statementList.first().run { FinalSelectStatement( sqlStr = unionSqlStr, deserializer = deserializer, @@ -72,9 +60,10 @@ internal class UnionSelectStatementGroup : StatementContainer { @Suppress("UNCHECKED_CAST") override fun changeLastStatement(statement: SingleStatement) { - if (statementLinkedList?.lastStatement is SelectStatement<*>) - statementLinkedList!!.resetLastStatement(statement as SelectStatement) - else + if (statementList.lastOrNull() is SelectStatement<*>) { + statementList.removeLast() + statementList.add(statement as SelectStatement) + } else throw IllegalStateException("Current statement can't append clause") } } \ No newline at end of file diff --git a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/TestPrimitiveTypeForKSP.kt b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/TestPrimitiveTypeForKSP.kt index cc8f974..7ef58e4 100644 --- a/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/TestPrimitiveTypeForKSP.kt +++ b/sqllin-dsl/src/commonTest/kotlin/com/ctrip/sqllin/dsl/TestPrimitiveTypeForKSP.kt @@ -37,7 +37,7 @@ data class TestPrimitiveTypeForKSP( val testULong: ULong, val testUShort: UShort, val testUByte: UByte, - val testBoolean: Boolean, - val testChar: Char, + val testBoolean: Boolean?, + val testChar: Char?, val testString: String, ) \ No newline at end of file