+
Skip to content

For update #247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Feb 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
buildscript {
ext {
kotlinVersion = "1.4.21"
detektVersion = "1.12.0-RC1"
detektVersion = "1.15.0"
}
repositories {
jcenter()
Expand Down
5 changes: 5 additions & 0 deletions ktorm-core/src/main/kotlin/org/ktorm/database/SqlDialect.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.")
}
Expand Down
6 changes: 3 additions & 3 deletions ktorm-core/src/main/kotlin/org/ktorm/dsl/Query.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 the record-lock, the generated SQL will depend on the SqlDialect.
*
* @since 3.1.0
*/
public fun Query.forUpdate(): Query {
public fun Query.forUpdate(forUpdate: ForUpdateOption): Query {
val expr = when (expression) {
is SelectExpression -> expression.copy(forUpdate = true)
is SelectExpression -> expression.copy(forUpdate = forUpdate)
is UnionExpression -> throw IllegalStateException("SELECT FOR UPDATE is not supported in a union expression.")
}

Expand Down
8 changes: 5 additions & 3 deletions ktorm-core/src/main/kotlin/org/ktorm/entity/EntitySequence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.ForUpdateOption
import org.ktorm.expression.OrderByExpression
import org.ktorm.expression.SelectExpression
import org.ktorm.schema.BaseTable
Expand Down Expand Up @@ -1505,10 +1506,11 @@ public fun <E : Any> EntitySequence<E, *>.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 will depend on the SqlDialect.
*
* @since 3.1.0
*/
public fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.forUpdate(): EntitySequence<E, T> {
return this.withExpression(expression.copy(forUpdate = true))
public fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.forUpdate(
forUpdate: ForUpdateOption): EntitySequence<E, T> {
return this.withExpression(expression.copy(forUpdate = forUpdate))
}
14 changes: 12 additions & 2 deletions ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 the record-lock, non-null will generate a dialect-specific
* `for update` clause.
*/
public data class SelectExpression(
val columns: List<ColumnDeclaringExpression<*>> = emptyList(),
Expand All @@ -124,14 +125,23 @@ public data class SelectExpression(
val groupBy: List<ScalarExpression<*>> = emptyList(),
val having: ScalarExpression<Boolean>? = null,
val isDistinct: Boolean = false,
val forUpdate: Boolean = false,
val forUpdate: ForUpdateOption = ForUpdateOption.None,
override val orderBy: List<OrderByExpression> = emptyList(),
override val offset: Int? = null,
override val limit: Int? = null,
override val tableAlias: String? = null,
override val extraProperties: Map<String, Any> = emptyMap()
) : QueryExpression()

/**
* ForUpdateOption, database-specific implementations are in support module for each database dialect.
*/
public interface ForUpdateOption {
public companion object {
public val None: ForUpdateOption = object : ForUpdateOption {}
}
}

/**
* Union expression, represents a `union` statement of SQL.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,14 @@ public abstract class SqlFormatter(
if (expr.offset != null || expr.limit != null) {
writePagination(expr)
}
if (expr.forUpdate) {
writeKeyword("for update ")
if (expr.forUpdate != ForUpdateOption.None) {
writeForUpdate(expr.forUpdate)
}
return expr
}

protected abstract fun writeForUpdate(forUpdate: ForUpdateOption)

override fun visitQuerySource(expr: QuerySourceExpression): QuerySourceExpression {
when (expr) {
is TableExpression -> {
Expand Down
27 changes: 0 additions & 27 deletions ktorm-core/src/test/kotlin/org/ktorm/dsl/QueryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ktorm-support-mysql/ktorm-support-mysql.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
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
import org.ktorm.schema.VarcharSqlType
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

/**
* [SqlDialect] implementation for MySQL database.
Expand All @@ -32,12 +37,33 @@ public open class MySqlDialect : SqlDialect {
}
}

private enum class Version {
MySql5, MySql8
}

/**
* 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(productVersion: String) :
UnsupportedOperationException("Unsupported MySql version $productVersion.") {
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 = when {
database.productVersion.startsWith("5") -> MySql5
database.productVersion.startsWith("8") -> MySql8
else -> throw UnsupportedMySqlVersionException(database.productVersion)
}

override fun visit(expr: SqlExpression): SqlExpression {
val result = when (expr) {
Expand Down Expand Up @@ -129,6 +155,30 @@ public open class MySqlFormatter(
write(") ")
return expr
}

override fun writeForUpdate(forUpdate: ForUpdateOption) {
when {
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}."
)
}
}
}

/**
* MySql Specific ForUpdateOptions.
*/
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()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MySqlTest : BaseTest() {
const val ID_3 = 3
const val ID_4 = 4

class KMySqlContainer : MySQLContainer<KMySqlContainer>()
class KMySqlContainer : MySQLContainer<KMySqlContainer>("mysql:8")

@ClassRule
@JvmField
Expand Down Expand Up @@ -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(MySqlForUpdateOption.ForUpdate)
.first()

val future = Executors.newSingleThreadExecutor().submit {
Expand Down
2 changes: 1 addition & 1 deletion ktorm-support-oracle/ktorm-support-oracle.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
*/
Expand All @@ -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 != ForUpdateOption.None) {
throw DialectFeatureNotSupportedException("SELECT FOR UPDATE not supported when using offset/limit params.")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion ktorm-support-postgresql/ktorm-support-postgresql.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载