From 2870ffb200a1986edcfd5742be310d6003339b3f Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 30 Jan 2021 00:54:56 +0800 Subject: [PATCH 01/14] fix typo --- CONTRIBUTING.md | 2 +- ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt | 2 +- ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt | 2 +- .../src/main/kotlin/org/ktorm/expression/SqlExpressions.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 34d1cfb6..263ec503 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,6 @@ Pull requests are always welcome and can be a quick way to get your fix or impro - By contributing to Ktorm, you agree to uphold our [Code of Conduct](CODE_OF_CONDUCT.md). - By contributing to Ktorm, you agree that your contributions will be licensed under [Apache License 2.0](LICENSE). -- Coding Conventions are very import. Refer to the [Kotlin Style Guide](https://kotlinlang.org/docs/reference/coding-conventions.html) for the recommended coding standards of Ktorm. +- Coding Conventions are very important. Refer to the [Kotlin Style Guide](https://kotlinlang.org/docs/reference/coding-conventions.html) for the recommended coding standards of Ktorm. - If you've added code that should be tested, add tests and ensure they all pass. If you've changed APIs, update the documentation. - If it's your first time contributing to Ktorm, please also update the `build.gradle` file, add your GitHub ID to the developers info, which will let more people know your contributions. diff --git a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt index 71dbc793..bcde3b7f 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt @@ -763,7 +763,7 @@ public fun Query.joinToString( } /** - * Indicate that this query should aquire the record-lock, the generated SQL would be `select ... for update`. + * Indicate that this query should acquire the record-lock, the generated SQL would be `select ... for update`. * * @since 3.1.0 */ diff --git a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt index 2cc56c55..a9ccd119 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt @@ -1505,7 +1505,7 @@ public fun EntitySequence.joinToString( } /** - * Indicate that this query should aquire the record-lock, the generated SQL would be `select ... for update`. + * Indicate that this query should acquire the record-lock, the generated SQL would be `select ... for update`. * * @since 3.1.0 */ diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt index e15fe706..020cfd31 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt @@ -115,7 +115,7 @@ public sealed class QueryExpression : QuerySourceExpression() { * @property groupBy the grouping conditions, represents the `group by` clause of SQL. * @property having the having condition, represents the `having` clause of SQL. * @property isDistinct mark if this query is distinct, true means the SQL is `select distinct ...`. - * @property forUpdate mark if this query should aquire the record-lock, true means the SQL is `select ... for update`. + * @property forUpdate mark if this query should acquire the record-lock, true means the SQL is `select ... for update`. */ public data class SelectExpression( val columns: List> = emptyList(), From 343170dc7a5a2fe5707e79b39463f5564618a1ae Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 11:01:52 -0500 Subject: [PATCH 02/14] dialect specific for update options for mysql and postgres --- .../src/main/kotlin/org/ktorm/dsl/Query.kt | 6 +- .../kotlin/org/ktorm/entity/EntitySequence.kt | 8 ++- .../org/ktorm/expression/SqlExpressions.kt | 15 ++++- .../org/ktorm/expression/SqlFormatter.kt | 10 +++- .../test/kotlin/org/ktorm/dsl/QueryTest.kt | 27 --------- .../org/ktorm/support/mysql/MySqlDialect.kt | 58 +++++++++++++++++++ .../org/ktorm/support/mysql/MySqlTest.kt | 4 +- .../support/postgresql/PostgreSqlDialect.kt | 23 ++++++++ .../support/postgresql/PostgreSqlTest.kt | 2 +- 9 files changed, 114 insertions(+), 39 deletions(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt index 71dbc793..3b06ca2e 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt @@ -763,13 +763,13 @@ public fun Query.joinToString( } /** - * Indicate that this query should aquire the record-lock, the generated SQL would be `select ... for update`. + * Indicate that this query should acquire an update-lock, the generated SQL will depend on the SqlDialect. * * @since 3.1.0 */ -public fun Query.forUpdate(): Query { +public fun Query.forUpdate(forUpdateExpression: ForUpdateExpression?): Query { val expr = when (expression) { - is SelectExpression -> expression.copy(forUpdate = true) + is SelectExpression -> expression.copy(forUpdate = forUpdateExpression) is UnionExpression -> throw IllegalStateException("SELECT FOR UPDATE is not supported in a union expression.") } diff --git a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt index 2cc56c55..7f0f1722 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt @@ -19,6 +19,7 @@ package org.ktorm.entity import org.ktorm.database.Database import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.dsl.* +import org.ktorm.expression.ForUpdateExpression import org.ktorm.expression.OrderByExpression import org.ktorm.expression.SelectExpression import org.ktorm.schema.BaseTable @@ -1505,10 +1506,11 @@ public fun EntitySequence.joinToString( } /** - * Indicate that this query should aquire the record-lock, the generated SQL would be `select ... for update`. + * Indicate that this query should acquire an update-lock, the generated SQL will depend on the SqlDialect. * * @since 3.1.0 */ -public fun > EntitySequence.forUpdate(): EntitySequence { - return this.withExpression(expression.copy(forUpdate = true)) +public fun > EntitySequence.forUpdate( + forUpdateExpression: ForUpdateExpression?): EntitySequence { + return this.withExpression(expression.copy(forUpdate = forUpdateExpression)) } diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt index e15fe706..570a4af4 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt @@ -19,6 +19,7 @@ package org.ktorm.expression import org.ktorm.schema.BooleanSqlType import org.ktorm.schema.ColumnDeclaring import org.ktorm.schema.SqlType +import org.ktorm.schema.VarcharSqlType /** * Root class of SQL expressions or statements. @@ -124,7 +125,7 @@ public data class SelectExpression( val groupBy: List> = emptyList(), val having: ScalarExpression? = null, val isDistinct: Boolean = false, - val forUpdate: Boolean = false, + val forUpdate: ForUpdateExpression? = null, override val orderBy: List = emptyList(), override val offset: Int? = null, override val limit: Int? = null, @@ -132,6 +133,18 @@ public data class SelectExpression( override val extraProperties: Map = emptyMap() ) : QueryExpression() +/** + * For Update expression, implementations are in the MySql and Postgres Dialects. + */ +public abstract class ForUpdateExpression : ScalarExpression() { + override val isLeafNode: Boolean + get() = true + override val extraProperties: Map + get() = emptyMap() + override val sqlType: SqlType + get() = VarcharSqlType +} + /** * Union expression, represents a `union` statement of SQL. * diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt index 854b81f0..e6f097f8 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt @@ -18,6 +18,7 @@ package org.ktorm.expression import org.ktorm.database.Database import org.ktorm.database.DialectFeatureNotSupportedException +import org.ktorm.database.detectDialectImplementation /** * Subclass of [SqlExpressionVisitor], visiting SQL expression trees using visitor pattern. After the visit completes, @@ -388,12 +389,17 @@ public abstract class SqlFormatter( if (expr.offset != null || expr.limit != null) { writePagination(expr) } - if (expr.forUpdate) { - writeKeyword("for update ") + if (expr.forUpdate != null) { + visitForUpdate(expr.forUpdate) } return expr } + protected open fun visitForUpdate(expr: ForUpdateExpression): ForUpdateExpression = + throw DialectFeatureNotSupportedException( + "FOR UPDATE not supported in dialect ${detectDialectImplementation()::class.java.name}." + ) + override fun visitQuerySource(expr: QuerySourceExpression): QuerySourceExpression { when (expr) { is TableExpression -> { diff --git a/ktorm-core/src/test/kotlin/org/ktorm/dsl/QueryTest.kt b/ktorm-core/src/test/kotlin/org/ktorm/dsl/QueryTest.kt index c58e0bc1..85c5086a 100644 --- a/ktorm-core/src/test/kotlin/org/ktorm/dsl/QueryTest.kt +++ b/ktorm-core/src/test/kotlin/org/ktorm/dsl/QueryTest.kt @@ -259,33 +259,6 @@ class QueryTest : BaseTest() { println(query.sql) } - @Test - fun testSelctForUpdate() { - database.useTransaction { - val employee = database - .sequenceOf(Employees, withReferences = false) - .filter { it.id eq 1 } - .forUpdate() - .first() - - val future = Executors.newSingleThreadExecutor().submit { - employee.name = "vince" - employee.flushChanges() - } - - try { - future.get(5, TimeUnit.SECONDS) - throw AssertionError() - } catch (e: ExecutionException) { - // Expected, the record is locked. - e.printStackTrace() - } catch (e: TimeoutException) { - // Expected, the record is locked. - e.printStackTrace() - } - } - } - @Test fun testFlatMap() { val names = database diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index 3a6c8aa2..6b2eeac5 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -21,6 +21,11 @@ import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType import org.ktorm.schema.VarcharSqlType +import org.ktorm.support.mysql.MySqlForUpdateExpression.ForShare +import org.ktorm.support.mysql.MySqlForUpdateExpression.ForUpdate +import org.ktorm.support.mysql.Version.MySql5 +import org.ktorm.support.mysql.Version.MySql8 +import java.sql.DatabaseMetaData /** * [SqlDialect] implementation for MySQL database. @@ -32,12 +37,43 @@ public open class MySqlDialect : SqlDialect { } } +@Suppress("MagicNumber") +private enum class Version(val majorVersion: Int) { + MySql5(5), MySql8(8) +} + +/** + * Thrown to indicate that the MySql version is not supported by the current dialect. + * + * @param databaseMetaData used to format the exception's message. + */ +public class UnsupportedMySqlVersionException(databaseMetaData: DatabaseMetaData) : + UnsupportedOperationException( + "Unsupported SqlDialect for ${databaseMetaData.databaseProductName} v${databaseMetaData.databaseProductVersion}" + ) { + private companion object { + private const val serialVersionUID = 1L + } +} + /** * [SqlFormatter] implementation for MySQL, formatting SQL expressions as strings with their execution arguments. */ public open class MySqlFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { + private val version: Version + + init { + database.useConnection { + val metaData = it.metaData + version = when (metaData.databaseMajorVersion) { + MySql5.majorVersion -> MySql5 + MySql8.majorVersion -> MySql8 + else -> throw UnsupportedMySqlVersionException(metaData) + } + } + } override fun visit(expr: SqlExpression): SqlExpression { val result = when (expr) { @@ -129,6 +165,28 @@ public open class MySqlFormatter( write(") ") return expr } + + override fun visitForUpdate(expr: ForUpdateExpression): ForUpdateExpression { + when { + expr == ForUpdate -> writeKeyword("for update ") + expr == ForShare && version == MySql5 -> writeKeyword("lock in share mode ") + expr == ForShare && version == MySql8 -> writeKeyword("for share ") + else -> { /* no-op */ } + } + return expr + } +} + +/** + * MySql Specific ForUpdateExpressions. + */ +public sealed class MySqlForUpdateExpression : ForUpdateExpression() { + /** + * The generated SQL would be `select ... lock in share mode` for MySql 5 and `select .. for share` for MySql 8. + **/ + public object ForShare : MySqlForUpdateExpression() + /** The generated SQL would be `select ... for update`. */ + public object ForUpdate : MySqlForUpdateExpression() } /** diff --git a/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt b/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt index 4ca68ead..76feb6e9 100644 --- a/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt +++ b/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt @@ -417,12 +417,12 @@ class MySqlTest : BaseTest() { } @Test - fun testSelctForUpdate() { + fun testSelectForUpdate() { database.useTransaction { val employee = database .sequenceOf(Employees, withReferences = false) .filter { it.id eq 1 } - .forUpdate() + .forUpdate(MySqlForUpdateExpression.ForUpdate) .first() val future = Executors.newSingleThreadExecutor().submit { diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 7aae5851..7dd6ff4c 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -20,6 +20,9 @@ import org.ktorm.database.Database import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType +import org.ktorm.support.postgresql.PostgresForUpdateExpression.NoWait +import org.ktorm.support.postgresql.PostgresForUpdateExpression.SkipLocked +import org.ktorm.support.postgresql.PostgresForUpdateExpression.Wait /** * [SqlDialect] implementation for PostgreSQL database. @@ -31,12 +34,32 @@ public open class PostgreSqlDialect : SqlDialect { } } +/** + * Postgres Specific ForUpdateExpressions. + */ +public sealed class PostgresForUpdateExpression : ForUpdateExpression() { + /** The generated SQL would be `select ... for update skip locked`. */ + public object SkipLocked : PostgresForUpdateExpression() + /** The generated SQL would be `select ... for update nowait`. */ + public object NoWait : PostgresForUpdateExpression() + /** The generated SQL would be `select ... for update wait `. */ + public data class Wait(val seconds: Int) : PostgresForUpdateExpression() +} + /** * [SqlFormatter] implementation for PostgreSQL, formatting SQL expressions as strings with their execution arguments. */ public open class PostgreSqlFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { + override fun visitForUpdate(forUpdate: ForUpdateExpression): ForUpdateExpression { + when (forUpdate) { + SkipLocked -> writeKeyword("for update skip locked ") + NoWait -> writeKeyword("for update nowait ") + is Wait -> writeKeyword("for update wait ${forUpdate.seconds} ") + } + return forUpdate + } override fun checkColumnName(name: String) { val maxLength = database.maxColumnNameLength diff --git a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt index 2a026e7d..6b45988a 100644 --- a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt +++ b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt @@ -398,7 +398,7 @@ class PostgreSqlTest : BaseTest() { val employee = database .sequenceOf(Employees, withReferences = false) .filter { it.id eq 1 } - .forUpdate() + .forUpdate(PostgresForUpdateExpression.NoWait) .first() val future = Executors.newSingleThreadExecutor().submit { From 92a0f88e61414b5df6ca4a6ca8fcf5e2ce465eac Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 11:07:55 -0500 Subject: [PATCH 03/14] cleaned up UnsupportedMySqlVersionException --- .../src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index 6b2eeac5..e7c9fff5 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -43,13 +43,13 @@ private enum class Version(val majorVersion: Int) { } /** - * Thrown to indicate that the MySql version is not supported by the current dialect. + * Thrown to indicate that the MySql version is not supported by the dialect. * * @param databaseMetaData used to format the exception's message. */ public class UnsupportedMySqlVersionException(databaseMetaData: DatabaseMetaData) : UnsupportedOperationException( - "Unsupported SqlDialect for ${databaseMetaData.databaseProductName} v${databaseMetaData.databaseProductVersion}" + "Unsupported MySql version ${databaseMetaData.databaseProductVersion}." ) { private companion object { private const val serialVersionUID = 1L From fc670a28fc1fd29c806a03e16f06a30ddbc26187 Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 11:20:36 -0500 Subject: [PATCH 04/14] renamed visitForUpdate -> writeForUpdate to match writePagination --- .../src/main/kotlin/org/ktorm/expression/SqlFormatter.kt | 8 ++------ .../main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt | 3 +-- .../org/ktorm/support/postgresql/PostgreSqlDialect.kt | 8 ++++---- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt index e6f097f8..4a696edb 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt @@ -18,7 +18,6 @@ package org.ktorm.expression import org.ktorm.database.Database import org.ktorm.database.DialectFeatureNotSupportedException -import org.ktorm.database.detectDialectImplementation /** * Subclass of [SqlExpressionVisitor], visiting SQL expression trees using visitor pattern. After the visit completes, @@ -390,15 +389,12 @@ public abstract class SqlFormatter( writePagination(expr) } if (expr.forUpdate != null) { - visitForUpdate(expr.forUpdate) + writeForUpdate(expr.forUpdate) } return expr } - protected open fun visitForUpdate(expr: ForUpdateExpression): ForUpdateExpression = - throw DialectFeatureNotSupportedException( - "FOR UPDATE not supported in dialect ${detectDialectImplementation()::class.java.name}." - ) + protected abstract fun writeForUpdate(expr: ForUpdateExpression) override fun visitQuerySource(expr: QuerySourceExpression): QuerySourceExpression { when (expr) { diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index e7c9fff5..f07099a8 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -166,14 +166,13 @@ public open class MySqlFormatter( return expr } - override fun visitForUpdate(expr: ForUpdateExpression): ForUpdateExpression { + override fun writeForUpdate(expr: ForUpdateExpression) { when { expr == ForUpdate -> writeKeyword("for update ") expr == ForShare && version == MySql5 -> writeKeyword("lock in share mode ") expr == ForShare && version == MySql8 -> writeKeyword("for share ") else -> { /* no-op */ } } - return expr } } diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 7dd6ff4c..302c5994 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -52,13 +52,13 @@ public sealed class PostgresForUpdateExpression : ForUpdateExpression() { public open class PostgreSqlFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { - override fun visitForUpdate(forUpdate: ForUpdateExpression): ForUpdateExpression { - when (forUpdate) { + override fun writeForUpdate(expr: ForUpdateExpression) { + when (expr) { SkipLocked -> writeKeyword("for update skip locked ") NoWait -> writeKeyword("for update nowait ") - is Wait -> writeKeyword("for update wait ${forUpdate.seconds} ") + is Wait -> writeKeyword("for update wait ${expr.seconds} ") + else -> { /* no-op */ } } - return forUpdate } override fun checkColumnName(name: String) { From dfd9d58b45ab45d4aeb5a4b2a340a2b38dae81fd Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 11:25:33 -0500 Subject: [PATCH 05/14] renamed ForUpdateExpression to ForUpdateOption --- .../src/main/kotlin/org/ktorm/dsl/Query.kt | 4 ++-- .../kotlin/org/ktorm/entity/EntitySequence.kt | 6 +++--- .../org/ktorm/expression/SqlExpressions.kt | 16 ++++------------ .../kotlin/org/ktorm/expression/SqlFormatter.kt | 2 +- .../org/ktorm/support/mysql/MySqlDialect.kt | 12 ++++++------ .../kotlin/org/ktorm/support/mysql/MySqlTest.kt | 2 +- .../support/postgresql/PostgreSqlDialect.kt | 16 ++++++++-------- .../ktorm/support/postgresql/PostgreSqlTest.kt | 2 +- 8 files changed, 26 insertions(+), 34 deletions(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt index 3b06ca2e..ae0a8b2d 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt @@ -767,9 +767,9 @@ public fun Query.joinToString( * * @since 3.1.0 */ -public fun Query.forUpdate(forUpdateExpression: ForUpdateExpression?): Query { +public fun Query.forUpdate(forUpdate: ForUpdateOption?): Query { val expr = when (expression) { - is SelectExpression -> expression.copy(forUpdate = forUpdateExpression) + is SelectExpression -> expression.copy(forUpdate = forUpdate) is UnionExpression -> throw IllegalStateException("SELECT FOR UPDATE is not supported in a union expression.") } diff --git a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt index 7f0f1722..ef5cf1a9 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt @@ -19,7 +19,7 @@ package org.ktorm.entity import org.ktorm.database.Database import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.dsl.* -import org.ktorm.expression.ForUpdateExpression +import org.ktorm.expression.ForUpdateOption import org.ktorm.expression.OrderByExpression import org.ktorm.expression.SelectExpression import org.ktorm.schema.BaseTable @@ -1511,6 +1511,6 @@ public fun EntitySequence.joinToString( * @since 3.1.0 */ public fun > EntitySequence.forUpdate( - forUpdateExpression: ForUpdateExpression?): EntitySequence { - return this.withExpression(expression.copy(forUpdate = forUpdateExpression)) + forUpdate: ForUpdateOption?): EntitySequence { + return this.withExpression(expression.copy(forUpdate = forUpdate)) } diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt index 570a4af4..3abf4283 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt @@ -19,7 +19,6 @@ package org.ktorm.expression import org.ktorm.schema.BooleanSqlType import org.ktorm.schema.ColumnDeclaring import org.ktorm.schema.SqlType -import org.ktorm.schema.VarcharSqlType /** * Root class of SQL expressions or statements. @@ -125,7 +124,7 @@ public data class SelectExpression( val groupBy: List> = emptyList(), val having: ScalarExpression? = null, val isDistinct: Boolean = false, - val forUpdate: ForUpdateExpression? = null, + val forUpdate: ForUpdateOption? = null, override val orderBy: List = emptyList(), override val offset: Int? = null, override val limit: Int? = null, @@ -134,16 +133,9 @@ public data class SelectExpression( ) : QueryExpression() /** - * For Update expression, implementations are in the MySql and Postgres Dialects. - */ -public abstract class ForUpdateExpression : ScalarExpression() { - override val isLeafNode: Boolean - get() = true - override val extraProperties: Map - get() = emptyMap() - override val sqlType: SqlType - get() = VarcharSqlType -} + * ForUpdateOption, implementations are in the MySql and Postgres Dialects. + */ +public interface ForUpdateOption /** * Union expression, represents a `union` statement of SQL. diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt index 4a696edb..7945a9b8 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt @@ -394,7 +394,7 @@ public abstract class SqlFormatter( return expr } - protected abstract fun writeForUpdate(expr: ForUpdateExpression) + protected abstract fun writeForUpdate(expr: ForUpdateOption) override fun visitQuerySource(expr: QuerySourceExpression): QuerySourceExpression { when (expr) { diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index f07099a8..378b2540 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -21,8 +21,8 @@ import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType import org.ktorm.schema.VarcharSqlType -import org.ktorm.support.mysql.MySqlForUpdateExpression.ForShare -import org.ktorm.support.mysql.MySqlForUpdateExpression.ForUpdate +import org.ktorm.support.mysql.MySqlForUpdateOption.ForShare +import org.ktorm.support.mysql.MySqlForUpdateOption.ForUpdate import org.ktorm.support.mysql.Version.MySql5 import org.ktorm.support.mysql.Version.MySql8 import java.sql.DatabaseMetaData @@ -166,7 +166,7 @@ public open class MySqlFormatter( return expr } - override fun writeForUpdate(expr: ForUpdateExpression) { + override fun writeForUpdate(expr: ForUpdateOption) { when { expr == ForUpdate -> writeKeyword("for update ") expr == ForShare && version == MySql5 -> writeKeyword("lock in share mode ") @@ -179,13 +179,13 @@ public open class MySqlFormatter( /** * MySql Specific ForUpdateExpressions. */ -public sealed class MySqlForUpdateExpression : ForUpdateExpression() { +public sealed class MySqlForUpdateOption : ForUpdateOption { /** * The generated SQL would be `select ... lock in share mode` for MySql 5 and `select .. for share` for MySql 8. **/ - public object ForShare : MySqlForUpdateExpression() + public object ForShare : MySqlForUpdateOption() /** The generated SQL would be `select ... for update`. */ - public object ForUpdate : MySqlForUpdateExpression() + public object ForUpdate : MySqlForUpdateOption() } /** diff --git a/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt b/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt index 76feb6e9..cf106007 100644 --- a/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt +++ b/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt @@ -422,7 +422,7 @@ class MySqlTest : BaseTest() { val employee = database .sequenceOf(Employees, withReferences = false) .filter { it.id eq 1 } - .forUpdate(MySqlForUpdateExpression.ForUpdate) + .forUpdate(MySqlForUpdateOption.ForUpdate) .first() val future = Executors.newSingleThreadExecutor().submit { diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 302c5994..5b642bd3 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -20,9 +20,9 @@ import org.ktorm.database.Database import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType -import org.ktorm.support.postgresql.PostgresForUpdateExpression.NoWait -import org.ktorm.support.postgresql.PostgresForUpdateExpression.SkipLocked -import org.ktorm.support.postgresql.PostgresForUpdateExpression.Wait +import org.ktorm.support.postgresql.PostgresForUpdateOption.NoWait +import org.ktorm.support.postgresql.PostgresForUpdateOption.SkipLocked +import org.ktorm.support.postgresql.PostgresForUpdateOption.Wait /** * [SqlDialect] implementation for PostgreSQL database. @@ -37,13 +37,13 @@ public open class PostgreSqlDialect : SqlDialect { /** * Postgres Specific ForUpdateExpressions. */ -public sealed class PostgresForUpdateExpression : ForUpdateExpression() { +public sealed class PostgresForUpdateOption : ForUpdateOption { /** The generated SQL would be `select ... for update skip locked`. */ - public object SkipLocked : PostgresForUpdateExpression() + public object SkipLocked : PostgresForUpdateOption() /** The generated SQL would be `select ... for update nowait`. */ - public object NoWait : PostgresForUpdateExpression() + public object NoWait : PostgresForUpdateOption() /** The generated SQL would be `select ... for update wait `. */ - public data class Wait(val seconds: Int) : PostgresForUpdateExpression() + public data class Wait(val seconds: Int) : PostgresForUpdateOption() } /** @@ -52,7 +52,7 @@ public sealed class PostgresForUpdateExpression : ForUpdateExpression() { public open class PostgreSqlFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { - override fun writeForUpdate(expr: ForUpdateExpression) { + override fun writeForUpdate(expr: ForUpdateOption) { when (expr) { SkipLocked -> writeKeyword("for update skip locked ") NoWait -> writeKeyword("for update nowait ") diff --git a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt index 6b45988a..307d8819 100644 --- a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt +++ b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt @@ -398,7 +398,7 @@ class PostgreSqlTest : BaseTest() { val employee = database .sequenceOf(Employees, withReferences = false) .filter { it.id eq 1 } - .forUpdate(PostgresForUpdateExpression.NoWait) + .forUpdate(PostgresForUpdateOption.NoWait) .first() val future = Executors.newSingleThreadExecutor().submit { From 58a1d8f0e0558580f3698cf90fdc669e586259d1 Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 11:30:32 -0500 Subject: [PATCH 06/14] throw DialectFeatureNotSupportedException instead of no-op when dialect does not support the ForUpdateOption --- .../main/kotlin/org/ktorm/expression/SqlFormatter.kt | 2 +- .../kotlin/org/ktorm/support/mysql/MySqlDialect.kt | 11 ++++++----- .../org/ktorm/support/postgresql/PostgreSqlDialect.kt | 9 +++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt index 7945a9b8..06f9757b 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt @@ -394,7 +394,7 @@ public abstract class SqlFormatter( return expr } - protected abstract fun writeForUpdate(expr: ForUpdateOption) + protected abstract fun writeForUpdate(forUpdate: ForUpdateOption) override fun visitQuerySource(expr: QuerySourceExpression): QuerySourceExpression { when (expr) { diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index 378b2540..7e919926 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -17,6 +17,7 @@ package org.ktorm.support.mysql import org.ktorm.database.Database +import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType @@ -166,12 +167,12 @@ public open class MySqlFormatter( return expr } - override fun writeForUpdate(expr: ForUpdateOption) { + override fun writeForUpdate(forUpdate: ForUpdateOption) { when { - expr == ForUpdate -> writeKeyword("for update ") - expr == ForShare && version == MySql5 -> writeKeyword("lock in share mode ") - expr == ForShare && version == MySql8 -> writeKeyword("for share ") - else -> { /* no-op */ } + forUpdate == ForUpdate -> writeKeyword("for update ") + forUpdate == ForShare && version == MySql5 -> writeKeyword("lock in share mode ") + forUpdate == ForShare && version == MySql8 -> writeKeyword("for share ") + else -> throw DialectFeatureNotSupportedException("Unsupported ForUpdateOption ${forUpdate::class.java.name}.") } } } diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 5b642bd3..4ac3bc82 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -17,6 +17,7 @@ package org.ktorm.support.postgresql import org.ktorm.database.Database +import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType @@ -52,12 +53,12 @@ public sealed class PostgresForUpdateOption : ForUpdateOption { public open class PostgreSqlFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { - override fun writeForUpdate(expr: ForUpdateOption) { - when (expr) { + override fun writeForUpdate(forUpdate: ForUpdateOption) { + when (forUpdate) { SkipLocked -> writeKeyword("for update skip locked ") NoWait -> writeKeyword("for update nowait ") - is Wait -> writeKeyword("for update wait ${expr.seconds} ") - else -> { /* no-op */ } + is Wait -> writeKeyword("for update wait ${forUpdate.seconds} ") + else -> throw DialectFeatureNotSupportedException("Unsupported ForUpdateOption ${forUpdate::class.java.name}.") } } From 5c5aa90ef4be3c20d6c3cefac29d8738c6689443 Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 12:56:17 -0500 Subject: [PATCH 07/14] basic for update options for Oracle and SqlServer; formatting --- .../org/ktorm/support/mysql/MySqlDialect.kt | 6 ++++-- .../org/ktorm/support/oracle/OracleDialect.kt | 20 ++++++++++++++++++- .../org/ktorm/support/oracle/OracleTest.kt | 2 +- .../support/postgresql/PostgreSqlDialect.kt | 6 ++++-- .../org/ktorm/support/sqlite/SQLiteDialect.kt | 4 ++++ .../support/sqlserver/SqlServerDialect.kt | 20 ++++++++++++++++++- 6 files changed, 51 insertions(+), 7 deletions(-) diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index 7e919926..6551c66f 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -172,13 +172,15 @@ public open class MySqlFormatter( forUpdate == ForUpdate -> writeKeyword("for update ") forUpdate == ForShare && version == MySql5 -> writeKeyword("lock in share mode ") forUpdate == ForShare && version == MySql8 -> writeKeyword("for share ") - else -> throw DialectFeatureNotSupportedException("Unsupported ForUpdateOption ${forUpdate::class.java.name}.") + else -> throw DialectFeatureNotSupportedException( + "Unsupported ForUpdateOption ${forUpdate::class.java.name}." + ) } } } /** - * MySql Specific ForUpdateExpressions. + * MySql Specific ForUpdateOptions. */ public sealed class MySqlForUpdateOption : ForUpdateOption { /** diff --git a/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt b/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt index 834530d1..669b3802 100644 --- a/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt +++ b/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt @@ -21,6 +21,7 @@ import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType +import org.ktorm.support.oracle.OracleForUpdateOption.ForUpdate /** * [SqlDialect] implementation for Oracle database. @@ -32,6 +33,14 @@ public open class OracleDialect : SqlDialect { } } +/** + * Oracle Specific ForUpdateOptions. + */ +public sealed class OracleForUpdateOption : ForUpdateOption { + /** The generated SQL would be `select ... for update`. */ + public object ForUpdate : OracleForUpdateOption() +} + /** * [SqlFormatter] implementation for Oracle, formatting SQL expressions as strings with their execution arguments. */ @@ -51,11 +60,20 @@ public open class OracleFormatter( return identifier.startsWith('_') || super.shouldQuote(identifier) } + override fun writeForUpdate(forUpdate: ForUpdateOption) { + when (forUpdate) { + ForUpdate -> writeKeyword("for update ") + else -> throw DialectFeatureNotSupportedException( + "Unsupported ForUpdateOption ${forUpdate::class.java.name}." + ) + } + } + override fun visitQuery(expr: QueryExpression): QueryExpression { if (expr.offset == null && expr.limit == null) { return super.visitQuery(expr) } - if (expr is SelectExpression && expr.forUpdate) { + if (expr is SelectExpression && expr.forUpdate != null) { throw DialectFeatureNotSupportedException("SELECT FOR UPDATE not supported when using offset/limit params.") } diff --git a/ktorm-support-oracle/src/test/kotlin/org/ktorm/support/oracle/OracleTest.kt b/ktorm-support-oracle/src/test/kotlin/org/ktorm/support/oracle/OracleTest.kt index beb868f1..6a465f2c 100644 --- a/ktorm-support-oracle/src/test/kotlin/org/ktorm/support/oracle/OracleTest.kt +++ b/ktorm-support-oracle/src/test/kotlin/org/ktorm/support/oracle/OracleTest.kt @@ -155,7 +155,7 @@ class OracleTest : BaseTest() { val employee = database .sequenceOf(Employees, withReferences = false) .filter { it.id eq 1 } - .forUpdate() + .forUpdate(OracleForUpdateOption.ForUpdate) .single() val future = Executors.newSingleThreadExecutor().submit { diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 4ac3bc82..a4f63306 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -36,7 +36,7 @@ public open class PostgreSqlDialect : SqlDialect { } /** - * Postgres Specific ForUpdateExpressions. + * Postgres Specific ForUpdateOptions. */ public sealed class PostgresForUpdateOption : ForUpdateOption { /** The generated SQL would be `select ... for update skip locked`. */ @@ -58,7 +58,9 @@ public open class PostgreSqlFormatter( SkipLocked -> writeKeyword("for update skip locked ") NoWait -> writeKeyword("for update nowait ") is Wait -> writeKeyword("for update wait ${forUpdate.seconds} ") - else -> throw DialectFeatureNotSupportedException("Unsupported ForUpdateOption ${forUpdate::class.java.name}.") + else -> throw DialectFeatureNotSupportedException( + "Unsupported ForUpdateOption ${forUpdate::class.java.name}." + ) } } diff --git a/ktorm-support-sqlite/src/main/kotlin/org/ktorm/support/sqlite/SQLiteDialect.kt b/ktorm-support-sqlite/src/main/kotlin/org/ktorm/support/sqlite/SQLiteDialect.kt index d6d90cd9..8b3a51cb 100644 --- a/ktorm-support-sqlite/src/main/kotlin/org/ktorm/support/sqlite/SQLiteDialect.kt +++ b/ktorm-support-sqlite/src/main/kotlin/org/ktorm/support/sqlite/SQLiteDialect.kt @@ -18,6 +18,7 @@ package org.ktorm.support.sqlite import org.ktorm.database.* import org.ktorm.expression.ArgumentExpression +import org.ktorm.expression.ForUpdateOption import org.ktorm.expression.QueryExpression import org.ktorm.expression.SqlFormatter import org.ktorm.schema.IntSqlType @@ -62,6 +63,9 @@ public open class SQLiteDialect : SqlDialect { public open class SQLiteFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { + override fun writeForUpdate(forUpdate: ForUpdateOption) { + throw DialectFeatureNotSupportedException("SQLite does not support SELECT ... FOR UPDATE.") + } override fun writePagination(expr: QueryExpression) { newLine(Indentation.SAME) diff --git a/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt b/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt index 062cae3a..636d7cb4 100644 --- a/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt +++ b/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt @@ -20,6 +20,7 @@ import org.ktorm.database.Database import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.database.SqlDialect import org.ktorm.expression.* +import org.ktorm.support.sqlserver.SqlServerForUpdateOption.ForUpdate /** * [SqlDialect] implementation for Microsoft SqlServer database. @@ -31,6 +32,14 @@ public open class SqlServerDialect : SqlDialect { } } +/** + * SqlServer Specific ForUpdateOptions. + */ +public sealed class SqlServerForUpdateOption : ForUpdateOption { + /** The generated SQL would be `select ... for update`. */ + public object ForUpdate : SqlServerForUpdateOption() +} + /** * [SqlFormatter] implementation for SqlServer, formatting SQL expressions as strings with their execution arguments. */ @@ -45,11 +54,20 @@ public open class SqlServerFormatter( } } + override fun writeForUpdate(forUpdate: ForUpdateOption) { + when (forUpdate) { + ForUpdate -> writeKeyword("for update ") + else -> throw DialectFeatureNotSupportedException( + "Unsupported ForUpdateOption ${forUpdate::class.java.name}." + ) + } + } + override fun visitQuery(expr: QueryExpression): QueryExpression { if (expr.offset == null && expr.limit == null) { return super.visitQuery(expr) } - if (expr is SelectExpression && expr.forUpdate) { + if (expr is SelectExpression && expr.forUpdate != null) { throw DialectFeatureNotSupportedException("SELECT FOR UPDATE not supported when using offset/limit params.") } From 893803adc5764609680a63fe283dc4424bc2cedd Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 13:01:50 -0500 Subject: [PATCH 08/14] minor typo in comment --- .../src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index 6551c66f..72d879fa 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -184,7 +184,7 @@ public open class MySqlFormatter( */ public sealed class MySqlForUpdateOption : ForUpdateOption { /** - * The generated SQL would be `select ... lock in share mode` for MySql 5 and `select .. for share` for MySql 8. + * The generated SQL would be `select ... lock in share mode` for MySql 5 and `select ... for share` for MySql 8. **/ public object ForShare : MySqlForUpdateOption() /** The generated SQL would be `select ... for update`. */ From db764ad907a0a5c0ec755d8e2a1e9c2b638fd58c Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 13:05:51 -0500 Subject: [PATCH 09/14] better comment on ForUpdateOption --- .../src/main/kotlin/org/ktorm/expression/SqlExpressions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt index 3abf4283..d1247b42 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt @@ -133,7 +133,7 @@ public data class SelectExpression( ) : QueryExpression() /** - * ForUpdateOption, implementations are in the MySql and Postgres Dialects. + * ForUpdateOption, database-specific implementations are in support module for each database dialect. */ public interface ForUpdateOption From ed418ac7f646becc3eed73cd454a2c7be75b45bc Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 14:02:06 -0500 Subject: [PATCH 10/14] implemented Postgres for update as described in postgresql.org docs --- .../support/postgresql/PostgreSqlDialect.kt | 43 +++++++++++++------ .../support/postgresql/PostgreSqlTest.kt | 6 ++- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index a4f63306..59b7fb22 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -21,9 +21,7 @@ import org.ktorm.database.DialectFeatureNotSupportedException import org.ktorm.database.SqlDialect import org.ktorm.expression.* import org.ktorm.schema.IntSqlType -import org.ktorm.support.postgresql.PostgresForUpdateOption.NoWait -import org.ktorm.support.postgresql.PostgresForUpdateOption.SkipLocked -import org.ktorm.support.postgresql.PostgresForUpdateOption.Wait +import org.ktorm.schema.Table /** * [SqlDialect] implementation for PostgreSQL database. @@ -36,15 +34,34 @@ public open class PostgreSqlDialect : SqlDialect { } /** - * Postgres Specific ForUpdateOptions. + * Postgres Specific ForUpdateOption. */ -public sealed class PostgresForUpdateOption : ForUpdateOption { - /** The generated SQL would be `select ... for update skip locked`. */ - public object SkipLocked : PostgresForUpdateOption() - /** The generated SQL would be `select ... for update nowait`. */ - public object NoWait : PostgresForUpdateOption() - /** The generated SQL would be `select ... for update wait `. */ - public data class Wait(val seconds: Int) : PostgresForUpdateOption() +public class PostgresForUpdateOption( + private val lockStrength: LockStrength, + private val onLock: OnLock, + private vararg val tables: Table<*> = emptyArray() +) : ForUpdateOption { + public fun toLockingClause(): String { + val lockingClause = StringBuilder(lockStrength.keywords) + if (tables.isNotEmpty()) { + tables.joinTo(lockingClause, prefix = "of ", postfix = " ") { it.tableName } + } + onLock.keywords?.let { lockingClause.append(it) } + return lockingClause.toString() + } + + public enum class LockStrength(public val keywords: String) { + Update("for update "), + NoKeyUpdate("for no key update "), + Share("for share "), + KeyShare("for key share ") + } + + public enum class OnLock(public val keywords: String?) { + Wait(null), + NoWait("no wait "), + SkipLocked("skip locked ") + } } /** @@ -55,9 +72,7 @@ public open class PostgreSqlFormatter( ) : SqlFormatter(database, beautifySql, indentSize) { override fun writeForUpdate(forUpdate: ForUpdateOption) { when (forUpdate) { - SkipLocked -> writeKeyword("for update skip locked ") - NoWait -> writeKeyword("for update nowait ") - is Wait -> writeKeyword("for update wait ${forUpdate.seconds} ") + is PostgresForUpdateOption -> writeKeyword(forUpdate.toLockingClause()) else -> throw DialectFeatureNotSupportedException( "Unsupported ForUpdateOption ${forUpdate::class.java.name}." ) diff --git a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt index 307d8819..cbf55f61 100644 --- a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt +++ b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt @@ -16,6 +16,8 @@ import org.ktorm.schema.ColumnDeclaring import org.ktorm.schema.Table import org.ktorm.schema.int import org.ktorm.schema.varchar +import org.ktorm.support.postgresql.PostgresForUpdateOption.LockStrength.Update +import org.ktorm.support.postgresql.PostgresForUpdateOption.OnLock.Wait import org.testcontainers.containers.PostgreSQLContainer import java.time.LocalDate import java.util.concurrent.ExecutionException @@ -393,12 +395,12 @@ class PostgreSqlTest : BaseTest() { } @Test - fun testSelctForUpdate() { + fun testSelectForUpdate() { database.useTransaction { val employee = database .sequenceOf(Employees, withReferences = false) .filter { it.id eq 1 } - .forUpdate(PostgresForUpdateOption.NoWait) + .forUpdate(PostgresForUpdateOption(lockStrength = Update, onLock = Wait)) .first() val future = Executors.newSingleThreadExecutor().submit { From 366399fdc441ae42aa63d87da70d86f164135ec4 Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 14:08:18 -0500 Subject: [PATCH 11/14] minor clean up --- ktorm-core/src/main/kotlin/org/ktorm/database/SqlDialect.kt | 5 +++++ .../kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/database/SqlDialect.kt b/ktorm-core/src/main/kotlin/org/ktorm/database/SqlDialect.kt index 584d83f2..2c619283 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/database/SqlDialect.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/database/SqlDialect.kt @@ -17,6 +17,7 @@ package org.ktorm.database import org.ktorm.expression.ArgumentExpression +import org.ktorm.expression.ForUpdateOption import org.ktorm.expression.QueryExpression import org.ktorm.expression.SqlFormatter import java.sql.Statement @@ -50,6 +51,10 @@ public interface SqlDialect { */ public fun createSqlFormatter(database: Database, beautifySql: Boolean, indentSize: Int): SqlFormatter { return object : SqlFormatter(database, beautifySql, indentSize) { + override fun writeForUpdate(forUpdate: ForUpdateOption) { + throw DialectFeatureNotSupportedException("ForUpdate is not supported in Standard SQL.") + } + override fun writePagination(expr: QueryExpression) { throw DialectFeatureNotSupportedException("Pagination is not supported in Standard SQL.") } diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 59b7fb22..91252f94 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -39,7 +39,7 @@ public open class PostgreSqlDialect : SqlDialect { public class PostgresForUpdateOption( private val lockStrength: LockStrength, private val onLock: OnLock, - private vararg val tables: Table<*> = emptyArray() + private vararg val tables: Table<*> ) : ForUpdateOption { public fun toLockingClause(): String { val lockingClause = StringBuilder(lockStrength.keywords) From fc5368efc51249c39abd73ac70ab6a8eb901fa1d Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Fri, 5 Feb 2021 14:17:27 -0500 Subject: [PATCH 12/14] fix typo in comment and update to explain new forUpdate behavior --- .../src/main/kotlin/org/ktorm/expression/SqlExpressions.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt index d1247b42..a14beeac 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt @@ -115,7 +115,8 @@ public sealed class QueryExpression : QuerySourceExpression() { * @property groupBy the grouping conditions, represents the `group by` clause of SQL. * @property having the having condition, represents the `having` clause of SQL. * @property isDistinct mark if this query is distinct, true means the SQL is `select distinct ...`. - * @property forUpdate mark if this query should aquire the record-lock, true means the SQL is `select ... for update`. + * @property forUpdate mark if this query should acquire an update-lock, non-null will generate a dialect-specific + * `for update` clause. */ public data class SelectExpression( val columns: List> = emptyList(), From 0f17096e3182efc22e768165907e92e571f21b01 Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Mon, 8 Feb 2021 07:24:44 -0500 Subject: [PATCH 13/14] use database.productVersion instead of getting a new connection for metaData --- .../org/ktorm/support/mysql/MySqlDialect.kt | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt index 72d879fa..79113620 100644 --- a/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt +++ b/ktorm-support-mysql/src/main/kotlin/org/ktorm/support/mysql/MySqlDialect.kt @@ -26,7 +26,6 @@ import org.ktorm.support.mysql.MySqlForUpdateOption.ForShare import org.ktorm.support.mysql.MySqlForUpdateOption.ForUpdate import org.ktorm.support.mysql.Version.MySql5 import org.ktorm.support.mysql.Version.MySql8 -import java.sql.DatabaseMetaData /** * [SqlDialect] implementation for MySQL database. @@ -38,9 +37,8 @@ public open class MySqlDialect : SqlDialect { } } -@Suppress("MagicNumber") -private enum class Version(val majorVersion: Int) { - MySql5(5), MySql8(8) +private enum class Version { + MySql5, MySql8 } /** @@ -48,10 +46,8 @@ private enum class Version(val majorVersion: Int) { * * @param databaseMetaData used to format the exception's message. */ -public class UnsupportedMySqlVersionException(databaseMetaData: DatabaseMetaData) : - UnsupportedOperationException( - "Unsupported MySql version ${databaseMetaData.databaseProductVersion}." - ) { +public class UnsupportedMySqlVersionException(productVersion: String) : + UnsupportedOperationException("Unsupported MySql version $productVersion.") { private companion object { private const val serialVersionUID = 1L } @@ -63,17 +59,10 @@ public class UnsupportedMySqlVersionException(databaseMetaData: DatabaseMetaData public open class MySqlFormatter( database: Database, beautifySql: Boolean, indentSize: Int ) : SqlFormatter(database, beautifySql, indentSize) { - private val version: Version - - init { - database.useConnection { - val metaData = it.metaData - version = when (metaData.databaseMajorVersion) { - MySql5.majorVersion -> MySql5 - MySql8.majorVersion -> MySql8 - else -> throw UnsupportedMySqlVersionException(metaData) - } - } + private val version: Version = when { + database.productVersion.startsWith("5") -> MySql5 + database.productVersion.startsWith("8") -> MySql8 + else -> throw UnsupportedMySqlVersionException(database.productVersion) } override fun visit(expr: SqlExpression): SqlExpression { @@ -187,6 +176,7 @@ public sealed class MySqlForUpdateOption : ForUpdateOption { * The generated SQL would be `select ... lock in share mode` for MySql 5 and `select ... for share` for MySql 8. **/ public object ForShare : MySqlForUpdateOption() + /** The generated SQL would be `select ... for update`. */ public object ForUpdate : MySqlForUpdateOption() } From 368b0db0306187a5617427171e42d0972b6248bb Mon Sep 17 00:00:00 2001 From: Eric Fenderbosch Date: Mon, 8 Feb 2021 08:57:06 -0500 Subject: [PATCH 14/14] made ForUpdateOption non-nullable; update testcontainers and detekt dependencies --- build.gradle | 2 +- ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt | 2 +- .../main/kotlin/org/ktorm/entity/EntitySequence.kt | 2 +- .../kotlin/org/ktorm/expression/SqlExpressions.kt | 8 ++++++-- .../main/kotlin/org/ktorm/expression/SqlFormatter.kt | 2 +- ktorm-support-mysql/ktorm-support-mysql.gradle | 2 +- .../test/kotlin/org/ktorm/support/mysql/MySqlTest.kt | 2 +- ktorm-support-oracle/ktorm-support-oracle.gradle | 2 +- .../kotlin/org/ktorm/support/oracle/OracleDialect.kt | 2 +- .../ktorm-support-postgresql.gradle | 2 +- .../ktorm/support/postgresql/PostgreSqlDialect.kt | 12 +++++++++++- .../org/ktorm/support/postgresql/PostgreSqlTest.kt | 2 +- ktorm-support-sqlite/ktorm-support-sqlite.gradle | 2 +- .../ktorm-support-sqlserver.gradle | 2 +- .../org/ktorm/support/sqlserver/SqlServerDialect.kt | 2 +- .../org/ktorm/support/sqlserver/SqlServerTest.kt | 2 +- 16 files changed, 31 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index dff43a29..55c9ce83 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { kotlinVersion = "1.4.21" - detektVersion = "1.12.0-RC1" + detektVersion = "1.15.0" } repositories { jcenter() diff --git a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt index ae0a8b2d..e1325173 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt @@ -767,7 +767,7 @@ public fun Query.joinToString( * * @since 3.1.0 */ -public fun Query.forUpdate(forUpdate: ForUpdateOption?): Query { +public fun Query.forUpdate(forUpdate: ForUpdateOption): Query { val expr = when (expression) { is SelectExpression -> expression.copy(forUpdate = forUpdate) is UnionExpression -> throw IllegalStateException("SELECT FOR UPDATE is not supported in a union expression.") diff --git a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt index ef5cf1a9..c163ca30 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt @@ -1511,6 +1511,6 @@ public fun EntitySequence.joinToString( * @since 3.1.0 */ public fun > EntitySequence.forUpdate( - forUpdate: ForUpdateOption?): EntitySequence { + forUpdate: ForUpdateOption): EntitySequence { return this.withExpression(expression.copy(forUpdate = forUpdate)) } diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt index a14beeac..4d03eed6 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt @@ -125,7 +125,7 @@ public data class SelectExpression( val groupBy: List> = emptyList(), val having: ScalarExpression? = null, val isDistinct: Boolean = false, - val forUpdate: ForUpdateOption? = null, + val forUpdate: ForUpdateOption = ForUpdateOption.None, override val orderBy: List = emptyList(), override val offset: Int? = null, override val limit: Int? = null, @@ -136,7 +136,11 @@ public data class SelectExpression( /** * ForUpdateOption, database-specific implementations are in support module for each database dialect. */ -public interface ForUpdateOption +public interface ForUpdateOption { + public companion object { + public val None: ForUpdateOption = object : ForUpdateOption {} + } +} /** * Union expression, represents a `union` statement of SQL. diff --git a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt index 06f9757b..199e45ce 100644 --- a/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt +++ b/ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt @@ -388,7 +388,7 @@ public abstract class SqlFormatter( if (expr.offset != null || expr.limit != null) { writePagination(expr) } - if (expr.forUpdate != null) { + if (expr.forUpdate != ForUpdateOption.None) { writeForUpdate(expr.forUpdate) } return expr diff --git a/ktorm-support-mysql/ktorm-support-mysql.gradle b/ktorm-support-mysql/ktorm-support-mysql.gradle index 3e2f1f00..6b401a15 100644 --- a/ktorm-support-mysql/ktorm-support-mysql.gradle +++ b/ktorm-support-mysql/ktorm-support-mysql.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation project(path: ":ktorm-core", configuration: "testOutput") testImplementation project(":ktorm-jackson") testImplementation "mysql:mysql-connector-java:8.0.13" - testImplementation "org.testcontainers:mysql:1.11.3" + testImplementation "org.testcontainers:mysql:1.15.1" } \ No newline at end of file diff --git a/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt b/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt index cf106007..125a240f 100644 --- a/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt +++ b/ktorm-support-mysql/src/test/kotlin/org/ktorm/support/mysql/MySqlTest.kt @@ -36,7 +36,7 @@ class MySqlTest : BaseTest() { const val ID_3 = 3 const val ID_4 = 4 - class KMySqlContainer : MySQLContainer() + class KMySqlContainer : MySQLContainer("mysql:8") @ClassRule @JvmField diff --git a/ktorm-support-oracle/ktorm-support-oracle.gradle b/ktorm-support-oracle/ktorm-support-oracle.gradle index 6231376e..0c4bdc8c 100644 --- a/ktorm-support-oracle/ktorm-support-oracle.gradle +++ b/ktorm-support-oracle/ktorm-support-oracle.gradle @@ -4,5 +4,5 @@ dependencies { testImplementation project(path: ":ktorm-core", configuration: "testOutput") testImplementation fileTree(dir: "lib", includes: ["*.jar"]) - testImplementation "org.testcontainers:oracle-xe:1.11.3" + testImplementation "org.testcontainers:oracle-xe:1.15.1" } \ No newline at end of file diff --git a/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt b/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt index 669b3802..8cc7e843 100644 --- a/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt +++ b/ktorm-support-oracle/src/main/kotlin/org/ktorm/support/oracle/OracleDialect.kt @@ -73,7 +73,7 @@ public open class OracleFormatter( if (expr.offset == null && expr.limit == null) { return super.visitQuery(expr) } - if (expr is SelectExpression && expr.forUpdate != null) { + if (expr is SelectExpression && expr.forUpdate != ForUpdateOption.None) { throw DialectFeatureNotSupportedException("SELECT FOR UPDATE not supported when using offset/limit params.") } diff --git a/ktorm-support-postgresql/ktorm-support-postgresql.gradle b/ktorm-support-postgresql/ktorm-support-postgresql.gradle index c2917f96..a7c8e612 100644 --- a/ktorm-support-postgresql/ktorm-support-postgresql.gradle +++ b/ktorm-support-postgresql/ktorm-support-postgresql.gradle @@ -4,5 +4,5 @@ dependencies { testImplementation project(path: ":ktorm-core", configuration: "testOutput") testImplementation "org.postgresql:postgresql:42.2.5" - testImplementation "org.testcontainers:postgresql:1.11.3" + testImplementation "org.testcontainers:postgresql:1.15.1" } \ No newline at end of file diff --git a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt index 91252f94..829c88da 100644 --- a/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt +++ b/ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/PostgreSqlDialect.kt @@ -34,13 +34,17 @@ public open class PostgreSqlDialect : SqlDialect { } /** - * Postgres Specific ForUpdateOption. + * Postgres Specific ForUpdateOption. See docs: https://www.postgresql.org/docs/13/sql-select.html#SQL-FOR-UPDATE-SHARE */ public class PostgresForUpdateOption( private val lockStrength: LockStrength, private val onLock: OnLock, private vararg val tables: Table<*> ) : ForUpdateOption { + + /** + * Generates SQL locking clause. + */ public fun toLockingClause(): String { val lockingClause = StringBuilder(lockStrength.keywords) if (tables.isNotEmpty()) { @@ -50,6 +54,9 @@ public class PostgresForUpdateOption( return lockingClause.toString() } + /** + * Lock strength. + */ public enum class LockStrength(public val keywords: String) { Update("for update "), NoKeyUpdate("for no key update "), @@ -57,6 +64,9 @@ public class PostgresForUpdateOption( KeyShare("for key share ") } + /** + * Behavior when a lock is detected. + */ public enum class OnLock(public val keywords: String?) { Wait(null), NoWait("no wait "), diff --git a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt index cbf55f61..b3eed969 100644 --- a/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt +++ b/ktorm-support-postgresql/src/test/kotlin/org/ktorm/support/postgresql/PostgreSqlTest.kt @@ -31,7 +31,7 @@ import java.util.concurrent.TimeoutException class PostgreSqlTest : BaseTest() { companion object { - class KPostgreSqlContainer : PostgreSQLContainer() + class KPostgreSqlContainer : PostgreSQLContainer("postgres:13-alpine") @ClassRule @JvmField diff --git a/ktorm-support-sqlite/ktorm-support-sqlite.gradle b/ktorm-support-sqlite/ktorm-support-sqlite.gradle index 03952d87..9c274ae2 100644 --- a/ktorm-support-sqlite/ktorm-support-sqlite.gradle +++ b/ktorm-support-sqlite/ktorm-support-sqlite.gradle @@ -3,5 +3,5 @@ dependencies { api project(":ktorm-core") testImplementation project(path: ":ktorm-core", configuration: "testOutput") - testImplementation "org.xerial:sqlite-jdbc:3.28.0" + testImplementation "org.xerial:sqlite-jdbc:3.34.0" } diff --git a/ktorm-support-sqlserver/ktorm-support-sqlserver.gradle b/ktorm-support-sqlserver/ktorm-support-sqlserver.gradle index 24c74768..bcdb531e 100644 --- a/ktorm-support-sqlserver/ktorm-support-sqlserver.gradle +++ b/ktorm-support-sqlserver/ktorm-support-sqlserver.gradle @@ -4,5 +4,5 @@ dependencies { api "com.microsoft.sqlserver:mssql-jdbc:7.2.2.jre8" testImplementation project(path: ":ktorm-core", configuration: "testOutput") - testImplementation "org.testcontainers:mssqlserver:1.11.3" + testImplementation "org.testcontainers:mssqlserver:1.15.1" } diff --git a/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt b/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt index 636d7cb4..e7cd298e 100644 --- a/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt +++ b/ktorm-support-sqlserver/src/main/kotlin/org/ktorm/support/sqlserver/SqlServerDialect.kt @@ -67,7 +67,7 @@ public open class SqlServerFormatter( if (expr.offset == null && expr.limit == null) { return super.visitQuery(expr) } - if (expr is SelectExpression && expr.forUpdate != null) { + if (expr is SelectExpression && expr.forUpdate != ForUpdateOption.None) { throw DialectFeatureNotSupportedException("SELECT FOR UPDATE not supported when using offset/limit params.") } diff --git a/ktorm-support-sqlserver/src/test/kotlin/org/ktorm/support/sqlserver/SqlServerTest.kt b/ktorm-support-sqlserver/src/test/kotlin/org/ktorm/support/sqlserver/SqlServerTest.kt index 93a5cb0d..b4205390 100644 --- a/ktorm-support-sqlserver/src/test/kotlin/org/ktorm/support/sqlserver/SqlServerTest.kt +++ b/ktorm-support-sqlserver/src/test/kotlin/org/ktorm/support/sqlserver/SqlServerTest.kt @@ -37,7 +37,7 @@ class SqlServerTest : BaseTest() { const val ID_3 = 3 const val ID_4 = 4 - class KSqlServerContainer : MSSQLServerContainer() + class KSqlServerContainer : MSSQLServerContainer("mcr.microsoft.com/mssql/server:2017-CU12") @ClassRule @JvmField