Skip to content

Commit 3f119eb

Browse files
committed
Correct transaction statement building
1 parent f8832dc commit 3f119eb

File tree

4 files changed

+125
-17
lines changed

4 files changed

+125
-17
lines changed

r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlTransactionDefinition.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,35 +37,34 @@
3737
public final class MySqlTransactionDefinition implements TransactionDefinition {
3838

3939
/**
40-
* Use {@code WITH CONSISTENT SNAPSHOT} syntax, all MySQL-compatible servers should support this syntax.
40+
* Use {@code WITH CONSISTENT SNAPSHOT} property.
41+
* <p>
4142
* The option starts a consistent read for storage engines such as InnoDB and XtraDB that can do so, the
4243
* same as if a {@code START TRANSACTION} followed by a {@code SELECT ...} from any InnoDB table was
4344
* issued.
44-
* <p>
45-
* NOTICE: This option and {@link #READ_ONLY} cannot be enabled at the same definition.
4645
*/
4746
public static final Option<Boolean> WITH_CONSISTENT_SNAPSHOT = Option.valueOf("withConsistentSnapshot");
4847

4948
/**
50-
* Use {@code START TRANSACTION WITH CONSISTENT [engine] SNAPSHOT} for Facebook/MySQL or similar syntax.
51-
* Only available when {@link #WITH_CONSISTENT_SNAPSHOT} is set to {@code true}.
49+
* Use {@code WITH CONSISTENT [engine] SNAPSHOT} for Facebook/MySQL or similar property. Only available
50+
* when {@link #WITH_CONSISTENT_SNAPSHOT} is set to {@code true}.
5251
* <p>
53-
* NOTICE: This is an extended syntax for special servers. Before using it, check whether the server
54-
* supports the syntax.
52+
* Note: This is an extended syntax based on specific distributions. Please check whether the server
53+
* supports this property before using it.
5554
*/
5655
public static final Option<ConsistentSnapshotEngine> CONSISTENT_SNAPSHOT_ENGINE =
5756
Option.valueOf("consistentSnapshotEngine");
5857

5958
/**
60-
* Use {@code START TRANSACTION WITH CONSISTENT SNAPSHOT FROM SESSION [session_id]} for Percona/MySQL or
61-
* similar syntax. Only available when {@link #WITH_CONSISTENT_SNAPSHOT} is set to {@code true}.
59+
* Use {@code WITH CONSISTENT SNAPSHOT FROM SESSION [session_id]} for Percona/MySQL or similar property.
60+
* Only available when {@link #WITH_CONSISTENT_SNAPSHOT} is set to {@code true}.
6261
* <p>
63-
* The {@code session_id} is the session identifier reported in the {@code Id} column of the process list.
64-
* Reported by {@code SHOW COLUMNS FROM performance_schema.processlist}, it should be an unsigned 64-bit
65-
* integer. Use {@code SHOW PROCESSLIST} to find session identifier of the process list.
62+
* The {@code session_id} is received by {@code SHOW COLUMNS FROM performance_schema.processlist}, it
63+
* should be an unsigned 64-bit integer. Use {@code SHOW PROCESSLIST} to find session identifier of the
64+
* process list.
6665
* <p>
67-
* NOTICE: This is an extended syntax for special servers. Before using it, check whether the server
68-
* supports the syntax.
66+
* Note: This is an extended syntax based on specific distributions. Please check whether the server
67+
* supports this property before using it.
6968
*/
7069
public static final Option<Long> CONSISTENT_SNAPSHOT_FROM_SESSION =
7170
Option.valueOf("consistentSnapshotFromSession");

r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/QueryFlow.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,20 +1281,28 @@ protected boolean process(int task, SynchronousSink<Void> sink) {
12811281
return false;
12821282
}
12831283

1284-
private static String buildStartTransaction(TransactionDefinition definition) {
1284+
/**
1285+
* Visible for testing.
1286+
*
1287+
* @param definition the transaction definition
1288+
* @return the {@code START TRANSACTION} statement
1289+
*/
1290+
static String buildStartTransaction(TransactionDefinition definition) {
12851291
Boolean readOnly = definition.getAttribute(TransactionDefinition.READ_ONLY);
12861292
Boolean snapshot = definition.getAttribute(MySqlTransactionDefinition.WITH_CONSISTENT_SNAPSHOT);
12871293

1288-
if (readOnly == null && (snapshot == null || !snapshot)) {
1294+
if (readOnly == null && !Boolean.TRUE.equals(snapshot)) {
12891295
return "BEGIN";
12901296
}
12911297

12921298
StringBuilder builder = new StringBuilder(90).append("START TRANSACTION");
1299+
boolean first = true;
12931300

1294-
if (snapshot != null && snapshot) {
1301+
if (Boolean.TRUE.equals(snapshot)) {
12951302
ConsistentSnapshotEngine engine =
12961303
definition.getAttribute(MySqlTransactionDefinition.CONSISTENT_SNAPSHOT_ENGINE);
12971304

1305+
first = false;
12981306
builder.append(" WITH CONSISTENT ");
12991307

13001308
if (engine == null) {
@@ -1312,6 +1320,10 @@ private static String buildStartTransaction(TransactionDefinition definition) {
13121320
}
13131321

13141322
if (readOnly != null) {
1323+
if (!first) {
1324+
builder.append(',');
1325+
}
1326+
13151327
if (readOnly) {
13161328
builder.append(" READ ONLY");
13171329
} else {

r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/ConnectionIntegrationTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
import io.r2dbc.spi.ColumnMetadata;
2020
import io.r2dbc.spi.R2dbcPermissionDeniedException;
21+
import io.r2dbc.spi.TransactionDefinition;
2122
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.condition.DisabledIf;
2224
import org.junit.jupiter.params.ParameterizedTest;
2325
import org.junit.jupiter.params.provider.ValueSource;
2426
import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException;
@@ -71,6 +73,30 @@ void isInTransaction() {
7173
.doOnSuccess(ignored -> assertThat(connection.isInTransaction()).isFalse()));
7274
}
7375

76+
@DisabledIf("envIsLessThanMySql56")
77+
@Test
78+
void startTransaction() {
79+
TransactionDefinition readOnlyConsistent = MySqlTransactionDefinition.builder()
80+
.withConsistentSnapshot(true)
81+
.readOnly(true)
82+
.build();
83+
TransactionDefinition readWriteConsistent = MySqlTransactionDefinition.builder()
84+
.withConsistentSnapshot(true)
85+
.readOnly(false)
86+
.build();
87+
88+
complete(connection -> Mono.<Void>fromRunnable(() -> assertThat(connection.isInTransaction())
89+
.isFalse())
90+
.then(connection.beginTransaction(readOnlyConsistent))
91+
.doOnSuccess(ignored -> assertThat(connection.isInTransaction()).isTrue())
92+
.then(connection.rollbackTransaction())
93+
.doOnSuccess(ignored -> assertThat(connection.isInTransaction()).isFalse())
94+
.then(connection.beginTransaction(readWriteConsistent))
95+
.doOnSuccess(ignored -> assertThat(connection.isInTransaction()).isTrue())
96+
.then(connection.rollbackTransaction())
97+
.doOnSuccess(ignored -> assertThat(connection.isInTransaction()).isFalse()));
98+
}
99+
74100
@Test
75101
void autoRollbackPreRelease() {
76102
// Mock pool allocate/release.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2024 asyncer.io projects
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.asyncer.r2dbc.mysql;
18+
19+
import io.r2dbc.spi.IsolationLevel;
20+
import io.r2dbc.spi.TransactionDefinition;
21+
import org.junit.jupiter.params.ParameterizedTest;
22+
import org.junit.jupiter.params.provider.Arguments;
23+
import org.junit.jupiter.params.provider.MethodSource;
24+
25+
import java.util.stream.Stream;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Unit tests for {@link StartTransactionState}.
31+
*/
32+
class StartTransactionStateTest {
33+
34+
@ParameterizedTest
35+
@MethodSource
36+
void buildStartTransaction(TransactionDefinition definition, String excepted) {
37+
assertThat(StartTransactionState.buildStartTransaction(definition)).isEqualTo(excepted);
38+
}
39+
40+
static Stream<Arguments> buildStartTransaction() {
41+
return Stream.of(
42+
Arguments.of(MySqlTransactionDefinition.empty(), "BEGIN"),
43+
Arguments.of(MySqlTransactionDefinition.builder()
44+
.isolationLevel(IsolationLevel.READ_UNCOMMITTED)
45+
.build(), "BEGIN"),
46+
Arguments.of(MySqlTransactionDefinition.builder()
47+
.readOnly(true)
48+
.build(), "START TRANSACTION READ ONLY"),
49+
Arguments.of(MySqlTransactionDefinition.builder()
50+
.readOnly(false)
51+
.build(), "START TRANSACTION READ WRITE"),
52+
Arguments.of(MySqlTransactionDefinition.builder()
53+
.withConsistentSnapshot(true)
54+
.build(), "START TRANSACTION WITH CONSISTENT SNAPSHOT"),
55+
Arguments.of(MySqlTransactionDefinition.builder()
56+
.withConsistentSnapshot(true)
57+
.readOnly(true)
58+
.build(), "START TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY"),
59+
Arguments.of(MySqlTransactionDefinition.builder()
60+
.withConsistentSnapshot(true)
61+
.readOnly(false)
62+
.build(), "START TRANSACTION WITH CONSISTENT SNAPSHOT, READ WRITE"),
63+
Arguments.of(MySqlTransactionDefinition.builder()
64+
.withConsistentSnapshot(true)
65+
.consistentSnapshotEngine(ConsistentSnapshotEngine.ROCKSDB)
66+
.consistentSnapshotFromSession(3L)
67+
.readOnly(true)
68+
.build(), "START TRANSACTION WITH CONSISTENT ROCKSDB SNAPSHOT FROM SESSION 3, READ ONLY")
69+
);
70+
}
71+
}

0 commit comments

Comments
 (0)