Skip to content

Commit cf85c7c

Browse files
committed
Support MySQL 8
1 parent 3b73279 commit cf85c7c

File tree

18 files changed

+236
-26
lines changed

18 files changed

+236
-26
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
runs-on: ubuntu-latest
5656
strategy:
5757
matrix:
58-
driver: [h2, mysql, postgresql, sqlserver, oracle]
58+
driver: [h2, mysql, mysql8, postgresql, sqlserver, oracle]
5959
timeout-minutes: 15
6060

6161
steps:

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ configure(integrationTestProjects) {
312312
prepare("mysql")
313313
}
314314

315+
val mysql8 by registering(Test::class) {
316+
prepare("mysql8")
317+
}
318+
315319
val oracle by registering(Test::class) {
316320
prepare("oracle")
317321
}
Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
package org.seasar.doma.internal.jdbc.dialect;
22

3+
import java.util.Objects;
34
import org.seasar.doma.internal.jdbc.sql.node.ForUpdateClauseNode;
5+
import org.seasar.doma.internal.jdbc.sql.node.FragmentNode;
46
import org.seasar.doma.internal.jdbc.sql.node.SelectStatementNode;
57
import org.seasar.doma.jdbc.SelectForUpdateType;
68
import org.seasar.doma.jdbc.SqlNode;
9+
import org.seasar.doma.jdbc.dialect.MysqlDialect;
710

811
public class MysqlForUpdateTransformer extends StandardForUpdateTransformer {
912

13+
private final MysqlDialect.MySqlVersion version;
14+
1015
public MysqlForUpdateTransformer(
1116
SelectForUpdateType forUpdateType, int waitSeconds, String... aliases) {
17+
this(forUpdateType, waitSeconds, aliases, MysqlDialect.DEFAULT_VERSION);
18+
}
19+
20+
public MysqlForUpdateTransformer(
21+
SelectForUpdateType forUpdateType,
22+
int waitSeconds,
23+
String[] aliases,
24+
MysqlDialect.MySqlVersion version) {
1225
super(forUpdateType, waitSeconds, aliases);
26+
this.version = Objects.requireNonNull(version);
1327
}
1428

1529
@Override
@@ -19,17 +33,39 @@ public SqlNode visitSelectStatementNode(SelectStatementNode node, Void p) {
1933
}
2034
processed = true;
2135

22-
ForUpdateClauseNode forUpdate = new ForUpdateClauseNode("for update");
23-
2436
SelectStatementNode result = new SelectStatementNode();
2537
result.setSelectClauseNode(node.getSelectClauseNode());
2638
result.setFromClauseNode(node.getFromClauseNode());
2739
result.setWhereClauseNode(node.getWhereClauseNode());
2840
result.setGroupByClauseNode(node.getGroupByClauseNode());
2941
result.setHavingClauseNode(node.getHavingClauseNode());
3042
result.setOrderByClauseNode(node.getOrderByClauseNode());
31-
result.setForUpdateClauseNode(forUpdate);
43+
result.setForUpdateClauseNode(createForUpdateClauseNode());
3244
result.setOptionClauseNode(node.getOptionClauseNode());
3345
return result;
3446
}
47+
48+
protected ForUpdateClauseNode createForUpdateClauseNode() {
49+
ForUpdateClauseNode forUpdate = new ForUpdateClauseNode("for update");
50+
switch (version) {
51+
case V5:
52+
break;
53+
case V8:
54+
StringBuilder buf = new StringBuilder(100);
55+
if (aliases.length > 0) {
56+
buf.append(" of ");
57+
for (String alias : aliases) {
58+
buf.append(alias);
59+
buf.append(", ");
60+
}
61+
buf.setLength(buf.length() - 2);
62+
}
63+
if (forUpdateType == SelectForUpdateType.NOWAIT) {
64+
buf.append(" nowait ");
65+
}
66+
forUpdate.appendNode(new FragmentNode(buf.toString()));
67+
break;
68+
}
69+
return forUpdate;
70+
}
3571
}

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/MysqlDialect.java

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Arrays;
55
import java.util.Collections;
66
import java.util.HashSet;
7+
import java.util.Objects;
78
import java.util.Set;
89
import org.seasar.doma.DomaNullPointerException;
910
import org.seasar.doma.expr.ExpressionFunctions;
@@ -34,35 +35,84 @@ public class MysqlDialect extends StandardDialect {
3435
/** the quotation mark of the end */
3536
protected static final char CLOSE_QUOTE = '`';
3637

38+
public static final MySqlVersion DEFAULT_VERSION = MySqlVersion.V5;
39+
40+
protected final MySqlVersion version;
41+
3742
public MysqlDialect() {
43+
this(DEFAULT_VERSION);
44+
}
45+
46+
public MysqlDialect(MySqlVersion version) {
3847
this(
3948
new MysqlJdbcMappingVisitor(),
4049
new MysqlSqlLogFormattingVisitor(),
41-
new MysqlExpressionFunctions());
50+
new MysqlExpressionFunctions(),
51+
version);
4252
}
4353

4454
public MysqlDialect(JdbcMappingVisitor jdbcMappingVisitor) {
45-
this(jdbcMappingVisitor, new MysqlSqlLogFormattingVisitor(), new MysqlExpressionFunctions());
55+
this(jdbcMappingVisitor, DEFAULT_VERSION);
56+
}
57+
58+
public MysqlDialect(JdbcMappingVisitor jdbcMappingVisitor, MySqlVersion version) {
59+
this(
60+
jdbcMappingVisitor,
61+
new MysqlSqlLogFormattingVisitor(),
62+
new MysqlExpressionFunctions(),
63+
version);
4664
}
4765

4866
public MysqlDialect(SqlLogFormattingVisitor sqlLogFormattingVisitor) {
49-
this(new MysqlJdbcMappingVisitor(), sqlLogFormattingVisitor, new MysqlExpressionFunctions());
67+
this(sqlLogFormattingVisitor, DEFAULT_VERSION);
68+
}
69+
70+
public MysqlDialect(SqlLogFormattingVisitor sqlLogFormattingVisitor, MySqlVersion version) {
71+
this(
72+
new MysqlJdbcMappingVisitor(),
73+
sqlLogFormattingVisitor,
74+
new MysqlExpressionFunctions(),
75+
version);
5076
}
5177

5278
public MysqlDialect(ExpressionFunctions expressionFunctions) {
53-
this(new MysqlJdbcMappingVisitor(), new MysqlSqlLogFormattingVisitor(), expressionFunctions);
79+
this(expressionFunctions, DEFAULT_VERSION);
80+
}
81+
82+
public MysqlDialect(ExpressionFunctions expressionFunctions, MySqlVersion version) {
83+
this(
84+
new MysqlJdbcMappingVisitor(),
85+
new MysqlSqlLogFormattingVisitor(),
86+
expressionFunctions,
87+
version);
5488
}
5589

5690
public MysqlDialect(
5791
JdbcMappingVisitor jdbcMappingVisitor, SqlLogFormattingVisitor sqlLogFormattingVisitor) {
58-
this(jdbcMappingVisitor, sqlLogFormattingVisitor, new MysqlExpressionFunctions());
92+
this(jdbcMappingVisitor, sqlLogFormattingVisitor, DEFAULT_VERSION);
93+
}
94+
95+
public MysqlDialect(
96+
JdbcMappingVisitor jdbcMappingVisitor,
97+
SqlLogFormattingVisitor sqlLogFormattingVisitor,
98+
MySqlVersion version) {
99+
this(jdbcMappingVisitor, sqlLogFormattingVisitor, new MysqlExpressionFunctions(), version);
59100
}
60101

61102
public MysqlDialect(
62103
JdbcMappingVisitor jdbcMappingVisitor,
63104
SqlLogFormattingVisitor sqlLogFormattingVisitor,
64105
ExpressionFunctions expressionFunctions) {
106+
this(jdbcMappingVisitor, sqlLogFormattingVisitor, expressionFunctions, DEFAULT_VERSION);
107+
}
108+
109+
public MysqlDialect(
110+
JdbcMappingVisitor jdbcMappingVisitor,
111+
SqlLogFormattingVisitor sqlLogFormattingVisitor,
112+
ExpressionFunctions expressionFunctions,
113+
MySqlVersion version) {
65114
super(jdbcMappingVisitor, sqlLogFormattingVisitor, expressionFunctions);
115+
this.version = Objects.requireNonNull(version);
66116
}
67117

68118
@Override
@@ -91,7 +141,14 @@ public boolean supportsIdentity() {
91141

92142
@Override
93143
public boolean supportsSelectForUpdate(SelectForUpdateType type, boolean withTargets) {
94-
return type == SelectForUpdateType.NORMAL && !withTargets;
144+
switch (version) {
145+
case V5:
146+
return type == SelectForUpdateType.NORMAL && !withTargets;
147+
case V8:
148+
return type == SelectForUpdateType.NORMAL || type == SelectForUpdateType.NOWAIT;
149+
default:
150+
throw new IllegalStateException(version.toString());
151+
}
95152
}
96153

97154
@Override
@@ -101,8 +158,15 @@ public boolean supportsAliasInDeleteClause() {
101158

102159
@Override
103160
protected SqlNode toCountCalculatingSqlNode(SqlNode sqlNode) {
104-
MysqlCountCalculatingTransformer transformer = new MysqlCountCalculatingTransformer();
105-
return transformer.transform(sqlNode);
161+
switch (version) {
162+
case V5:
163+
MysqlCountCalculatingTransformer transformer = new MysqlCountCalculatingTransformer();
164+
return transformer.transform(sqlNode);
165+
case V8:
166+
return super.toCountCalculatingSqlNode(sqlNode);
167+
default:
168+
throw new IllegalStateException(version.toString());
169+
}
106170
}
107171

108172
@Override
@@ -121,8 +185,15 @@ protected SqlNode toForUpdateSqlNode(
121185

122186
@Override
123187
protected SqlNode toCountGettingSqlNode(SqlNode sqlNode) {
124-
MysqlCountGettingTransformer transformer = new MysqlCountGettingTransformer();
125-
return transformer.transform(sqlNode);
188+
switch (version) {
189+
case V5:
190+
MysqlCountGettingTransformer transformer = new MysqlCountGettingTransformer();
191+
return transformer.transform(sqlNode);
192+
case V8:
193+
return super.toCountGettingSqlNode(sqlNode);
194+
default:
195+
throw new IllegalStateException(version.toString());
196+
}
126197
}
127198

128199
@Override
@@ -179,6 +250,7 @@ protected MysqlScriptBlockContext() {
179250
}
180251

181252
public static class MysqlCriteriaBuilder extends StandardCriteriaBuilder {
253+
182254
@Override
183255
public void offsetAndFetch(PreparedSqlBuilder buf, int offset, int limit) {
184256
buf.appendSql(" limit ");
@@ -196,4 +268,9 @@ public void offsetAndFetch(PreparedSqlBuilder buf, int offset, int limit) {
196268
public UpsertAssembler getUpsertAssembler(UpsertAssemblerContext context) {
197269
return new MysqlUpsertAssembler(context);
198270
}
271+
272+
public enum MySqlVersion {
273+
V5,
274+
V8
275+
}
199276
}

doma-core/src/test/java/org/seasar/doma/internal/jdbc/dialect/MysqlForUpdateTransformerTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.seasar.doma.jdbc.SelectForUpdateType;
1212
import org.seasar.doma.jdbc.SqlKind;
1313
import org.seasar.doma.jdbc.SqlNode;
14+
import org.seasar.doma.jdbc.dialect.MysqlDialect;
1415

1516
public class MysqlForUpdateTransformerTest {
1617

@@ -26,4 +27,32 @@ public void testForUpdateNormal() {
2627
PreparedSql sql = sqlBuilder.build(sqlNode, Function.identity());
2728
assertEquals(expected, sql.getRawSql());
2829
}
30+
31+
@Test
32+
public void testForUpdateNowait_v8() {
33+
String expected = "select * from emp order by emp.id for update nowait";
34+
MysqlForUpdateTransformer transformer =
35+
new MysqlForUpdateTransformer(
36+
SelectForUpdateType.NOWAIT, 0, new String[] {}, MysqlDialect.MySqlVersion.V8);
37+
SqlParser parser = new SqlParser("select * from emp order by emp.id");
38+
SqlNode sqlNode = transformer.transform(parser.parse());
39+
NodePreparedSqlBuilder sqlBuilder =
40+
new NodePreparedSqlBuilder(new MockConfig(), SqlKind.SELECT, "dummyPath");
41+
PreparedSql sql = sqlBuilder.build(sqlNode, Function.identity());
42+
assertEquals(expected, sql.getRawSql());
43+
}
44+
45+
@Test
46+
public void testForUpdateNowait_alias_v8() {
47+
String expected = "select * from emp order by emp.id for update of emp nowait";
48+
MysqlForUpdateTransformer transformer =
49+
new MysqlForUpdateTransformer(
50+
SelectForUpdateType.NOWAIT, 0, new String[] {"emp"}, MysqlDialect.MySqlVersion.V8);
51+
SqlParser parser = new SqlParser("select * from emp order by emp.id");
52+
SqlNode sqlNode = transformer.transform(parser.parse());
53+
NodePreparedSqlBuilder sqlBuilder =
54+
new NodePreparedSqlBuilder(new MockConfig(), SqlKind.SELECT, "dummyPath");
55+
PreparedSql sql = sqlBuilder.build(sqlNode, Function.identity());
56+
assertEquals(expected, sql.getRawSql());
57+
}
2958
}

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ testUseModule=false
1717
driver=h2
1818
h2.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
1919
mysql.url=jdbc:tc:mysql:5.7:///test?TC_DAEMON=true&allowMultiQueries=true
20+
mysql8.url=jdbc:tc:mysql:8.0.36:///test?TC_MY_CNF=mysql_conf_override&TC_DAEMON=true&allowMultiQueries=true
2021
oracle.url=jdbc:tc:oracle:thin:@test?TC_DAEMON=true
2122
postgresql.url=jdbc:tc:postgresql:10.16:///test?TC_DAEMON=true
2223
sqlserver.url=jdbc:tc:sqlserver:2019-CU12-ubuntu-20.04:///test?TC_DAEMON=true

integration-test-common/src/main/java/org/seasar/doma/it/Dbms.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public enum Dbms {
99

1010
MYSQL,
1111

12+
MYSQL8,
13+
1214
POSTGRESQL,
1315

1416
SQLSERVER,

integration-test-common/src/main/java/org/seasar/doma/it/IntegrationTestEnvironment.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ private static Dbms determineDbms(String driver) {
6969
return Dbms.H2;
7070
case "mysql":
7171
return Dbms.MYSQL;
72+
case "mysql8":
73+
return Dbms.MYSQL8;
7274
case "oracle":
7375
return Dbms.ORACLE;
7476
case "postgresql":
@@ -90,6 +92,8 @@ private static Dialect createDialect(Dbms dbms) {
9092
return new SqliteDialect();
9193
case MYSQL:
9294
return new MysqlDialect();
95+
case MYSQL8:
96+
return new MysqlDialect(MysqlDialect.MySqlVersion.V8);
9397
case POSTGRESQL:
9498
return new PostgresDialect();
9599
case SQLSERVER:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[mysqld]
2+
log_bin_trust_function_creators=ON

integration-test-java/src/test/java/org/seasar/doma/it/auto/AutoBatchInsertTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public void testId_Identity_ignoreGeneratedKeys(Config config) throws Exception
209209
}
210210

211211
@Test
212-
@Run(unless = {Dbms.MYSQL, Dbms.SQLSERVER, Dbms.SQLITE})
212+
@Run(unless = {Dbms.MYSQL, Dbms.MYSQL8, Dbms.SQLSERVER, Dbms.SQLITE})
213213
public void testId_sequence(Config config) throws Exception {
214214
SequenceStrategyDao dao = new SequenceStrategyDaoImpl(config);
215215
for (int i = 0; i < 110; i++) {

0 commit comments

Comments
 (0)