From 2dd7a0e8e7e47c12a81148ecbf3185d788f081a7 Mon Sep 17 00:00:00 2001 From: Hideki Sugimoto Date: Mon, 23 Sep 2024 13:40:35 +0900 Subject: [PATCH] Add Dialect - Oracle12 - Oracle18 - Oracle19 - Oracle21 - Oracle23 - MariaDB5 - MariaDB10 --- .../uroborosql/dialect/MariaDb10Dialect.java | 51 +++++ .../uroborosql/dialect/MariaDb5Dialect.java | 31 +++ .../uroborosql/dialect/MariaDbDialect.java | 161 +++++++++++++ .../uroborosql/dialect/Oracle12Dialect.java | 22 +- .../uroborosql/dialect/Oracle18Dialect.java | 30 +++ .../uroborosql/dialect/Oracle19Dialect.java | 30 +++ .../uroborosql/dialect/Oracle21Dialect.java | 30 +++ .../uroborosql/dialect/Oracle23Dialect.java | 41 ++++ .../jp.co.future.uroborosql.dialect.Dialect | 6 + .../dialect/MariaDb10DialectTest.java | 211 ++++++++++++++++++ .../dialect/MariaDb5DialectTest.java | 189 ++++++++++++++++ .../dialect/Oracle11DialectTest.java | 2 +- .../dialect/Oracle12DialectTest.java | 4 +- .../dialect/Oracle18DialectTest.java | 204 +++++++++++++++++ .../dialect/Oracle19DialectTest.java | 204 +++++++++++++++++ .../dialect/Oracle21DialectTest.java | 204 +++++++++++++++++ .../dialect/Oracle23DialectTest.java | 204 +++++++++++++++++ .../dialect/PostgresqlDialectTest.java | 5 + 18 files changed, 1615 insertions(+), 14 deletions(-) create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/MariaDb10Dialect.java create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/MariaDb5Dialect.java create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/MariaDbDialect.java create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/Oracle18Dialect.java create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/Oracle19Dialect.java create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/Oracle21Dialect.java create mode 100644 src/main/java/jp/co/future/uroborosql/dialect/Oracle23Dialect.java create mode 100644 src/test/java/jp/co/future/uroborosql/dialect/MariaDb10DialectTest.java create mode 100644 src/test/java/jp/co/future/uroborosql/dialect/MariaDb5DialectTest.java create mode 100644 src/test/java/jp/co/future/uroborosql/dialect/Oracle18DialectTest.java create mode 100644 src/test/java/jp/co/future/uroborosql/dialect/Oracle19DialectTest.java create mode 100644 src/test/java/jp/co/future/uroborosql/dialect/Oracle21DialectTest.java create mode 100644 src/test/java/jp/co/future/uroborosql/dialect/Oracle23DialectTest.java diff --git a/src/main/java/jp/co/future/uroborosql/dialect/MariaDb10Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/MariaDb10Dialect.java new file mode 100644 index 00000000..d7044c35 --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/MariaDb10Dialect.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +/** + * MariaDB(ver10とそれ以降)用のDialect + * + * @author H.Sugimoto + */ +public class MariaDb10Dialect extends MariaDbDialect { + /** + * コンストラクタ + */ + public MariaDb10Dialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.MariaDbDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion >= 10; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.MariaDbDialect#supportsSequence() + */ + @Override + public boolean supportsSequence() { + return true; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#getSequenceNextValSql(java.lang.String) + */ + @Override + public String getSequenceNextValSql(final String sequenceName) { + return "nextval(" + sequenceName + ")"; + } + +} diff --git a/src/main/java/jp/co/future/uroborosql/dialect/MariaDb5Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/MariaDb5Dialect.java new file mode 100644 index 00000000..136dad77 --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/MariaDb5Dialect.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +/** + * MariaDB(ver5)用のDialect + * + * @author H.Sugimoto + */ +public class MariaDb5Dialect extends MariaDbDialect { + /** + * コンストラクタ + */ + public MariaDb5Dialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.MariaDbDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion == 5; + } + +} diff --git a/src/main/java/jp/co/future/uroborosql/dialect/MariaDbDialect.java b/src/main/java/jp/co/future/uroborosql/dialect/MariaDbDialect.java new file mode 100644 index 00000000..fbe7e1ab --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/MariaDbDialect.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.exception.UroborosqlRuntimeException; + +/** + * MariaDB用のデフォルト設定用Dialect + * + * @author H.Sugimoto + */ +public abstract class MariaDbDialect extends AbstractDialect { + /** + * 悲観ロックのErrorCode もしくは SqlState. MySQLの場合はErrorCodeで判定する. + *
ERROR 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set.
+ */ + private static final Set pessimisticLockingErrorCodes = Set.of("3572"); + + /** + * コンストラクタ + */ + protected MariaDbDialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#accept(jp.co.future.uroborosql.connection.ConnectionSupplier) + */ + @Override + public boolean accept(final ConnectionSupplier supplier) { + if (supplier == null) { + return false; + } + + var parts = supplier.getDatabaseName().split("-", 2); + var databaseName = parts[0]; + + if (!databaseName.startsWith(getDatabaseName())) { + return false; + } + + var databaseVersion = parts[1]; + + try { + var majorVersion = Integer.parseInt(databaseVersion.substring(0, databaseVersion.indexOf("."))); + return isTargetVersion(majorVersion); + } catch (NumberFormatException ex) { + return false; + } + } + + /** + * 対象のMariaDBバージョンかどうかを判定する + * + * @param majorVersion コネクションから取得したメジャーバージョン + * @return 対象のバージョンの場合true + */ + protected abstract boolean isTargetVersion(int majorVersion); + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#getDatabaseName() + */ + @Override + public String getDatabaseName() { + return "MariaDB"; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#supportsBulkInsert() + */ + @Override + public boolean supportsBulkInsert() { + return true; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#supportsLimitClause() + */ + @Override + public boolean supportsLimitClause() { + return true; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#supportsSequence() + */ + @Override + public boolean supportsSequence() { + return false; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#supportsForUpdateWait() + */ + @Override + public boolean supportsForUpdateWait() { + return false; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#supportsOptimizerHints() + */ + @Override + public boolean supportsOptimizerHints() { + return true; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#getSequenceNextValSql(java.lang.String) + */ + @Override + public String getSequenceNextValSql(final String sequenceName) { + throw new UroborosqlRuntimeException("MariaDB does not support Sequence."); + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#addOptimizerHints(java.lang.StringBuilder, java.util.List) + */ + @Override + public StringBuilder addOptimizerHints(final StringBuilder sql, final List hints) { + var hintStr = "$1 " + hints.stream().collect(Collectors.joining(" ")) + System.lineSeparator(); + return new StringBuilder(sql.toString().replaceFirst("((FROM|from)\\s+[^\\s]+)\\s*", hintStr)); + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#getPessimisticLockingErrorCodes() + */ + @Override + public Set getPessimisticLockingErrorCodes() { + return pessimisticLockingErrorCodes; + } + +} diff --git a/src/main/java/jp/co/future/uroborosql/dialect/Oracle12Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/Oracle12Dialect.java index 61217bb3..74e2cb78 100644 --- a/src/main/java/jp/co/future/uroborosql/dialect/Oracle12Dialect.java +++ b/src/main/java/jp/co/future/uroborosql/dialect/Oracle12Dialect.java @@ -7,7 +7,7 @@ package jp.co.future.uroborosql.dialect; /** - * Oracle12(以降のバージョンも含む)用のDialect + * Oracle12用のDialect * * @author H.Sugimoto */ @@ -19,6 +19,16 @@ public Oracle12Dialect() { super('\\', new char[] { '%', '_', '%', '_' }); } + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.OracleDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion == 12; + } + /** * {@inheritDoc} * @@ -61,14 +71,4 @@ public String getLimitClause(final long limit, final long offset) { } return builder.toString(); } - - /** - * {@inheritDoc} - * - * @see jp.co.future.uroborosql.dialect.OracleDialect#isTargetVersion(int) - */ - @Override - protected boolean isTargetVersion(final int majorVersion) { - return majorVersion >= 12; - } } diff --git a/src/main/java/jp/co/future/uroborosql/dialect/Oracle18Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/Oracle18Dialect.java new file mode 100644 index 00000000..17898f75 --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/Oracle18Dialect.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +/** + * Oracle18用のDialect + * + * @author H.Sugimoto + */ +public class Oracle18Dialect extends Oracle12Dialect { + /** + * コンストラクタ + */ + public Oracle18Dialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.OracleDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion == 18; + } +} diff --git a/src/main/java/jp/co/future/uroborosql/dialect/Oracle19Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/Oracle19Dialect.java new file mode 100644 index 00000000..4c244443 --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/Oracle19Dialect.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +/** + * Oracle19用のDialect + * + * @author H.Sugimoto + */ +public class Oracle19Dialect extends Oracle12Dialect { + /** + * コンストラクタ + */ + public Oracle19Dialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.OracleDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion == 19; + } +} diff --git a/src/main/java/jp/co/future/uroborosql/dialect/Oracle21Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/Oracle21Dialect.java new file mode 100644 index 00000000..bb9a1708 --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/Oracle21Dialect.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +/** + * Oracle21用のDialect + * + * @author H.Sugimoto + */ +public class Oracle21Dialect extends Oracle12Dialect { + /** + * コンストラクタ + */ + public Oracle21Dialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.OracleDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion == 21; + } +} diff --git a/src/main/java/jp/co/future/uroborosql/dialect/Oracle23Dialect.java b/src/main/java/jp/co/future/uroborosql/dialect/Oracle23Dialect.java new file mode 100644 index 00000000..9b5ca596 --- /dev/null +++ b/src/main/java/jp/co/future/uroborosql/dialect/Oracle23Dialect.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2017-present, Future Corporation + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package jp.co.future.uroborosql.dialect; + +/** + * Oracle23(以降のバージョンも含む)用のDialect + * + * @author H.Sugimoto + */ +public class Oracle23Dialect extends Oracle12Dialect { + /** + * コンストラクタ + */ + public Oracle23Dialect() { + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.OracleDialect#isTargetVersion(int) + */ + @Override + protected boolean isTargetVersion(final int majorVersion) { + return majorVersion >= 23; + } + + /** + * {@inheritDoc} + * + * @see jp.co.future.uroborosql.dialect.Dialect#supportsBulkInsert() + */ + @Override + public boolean supportsBulkInsert() { + return true; + } + +} diff --git a/src/main/resources/META-INF/services/jp.co.future.uroborosql.dialect.Dialect b/src/main/resources/META-INF/services/jp.co.future.uroborosql.dialect.Dialect index 18ca28ec..c6409291 100644 --- a/src/main/resources/META-INF/services/jp.co.future.uroborosql.dialect.Dialect +++ b/src/main/resources/META-INF/services/jp.co.future.uroborosql.dialect.Dialect @@ -1,7 +1,13 @@ +jp.co.future.uroborosql.dialect.Oracle23Dialect +jp.co.future.uroborosql.dialect.Oracle21Dialect +jp.co.future.uroborosql.dialect.Oracle19Dialect +jp.co.future.uroborosql.dialect.Oracle18Dialect jp.co.future.uroborosql.dialect.Oracle12Dialect jp.co.future.uroborosql.dialect.Oracle11Dialect jp.co.future.uroborosql.dialect.Oracle10Dialect jp.co.future.uroborosql.dialect.MsSqlDialect +jp.co.future.uroborosql.dialect.MariaDb10Dialect +jp.co.future.uroborosql.dialect.MariaDb5Dialect jp.co.future.uroborosql.dialect.MySqlDialect jp.co.future.uroborosql.dialect.PostgresqlDialect jp.co.future.uroborosql.dialect.H2Dialect \ No newline at end of file diff --git a/src/test/java/jp/co/future/uroborosql/dialect/MariaDb10DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/MariaDb10DialectTest.java new file mode 100644 index 00000000..5c109add --- /dev/null +++ b/src/test/java/jp/co/future/uroborosql/dialect/MariaDb10DialectTest.java @@ -0,0 +1,211 @@ +package jp.co.future.uroborosql.dialect; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import jp.co.future.uroborosql.connection.ConnectionContext; +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.enums.ForUpdateType; + +/** + * MariaDb10Dialectの個別実装部分のテストケース + * + * @author H.Sugimoto + * + */ +public class MariaDb10DialectTest { + private final Dialect dialect = new MariaDb10Dialect(); + + @Test + void testAccept5() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "MariaDB-5.0"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(MariaDb10Dialect.class))); + } + + @Test + void testAccept10() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "MariaDB-10.0"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(MariaDb10Dialect.class)); + } + + @Test + void testAccept11() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "MariaDB-11.0"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(MariaDb10Dialect.class)); + } + + @Test + void testGetSequenceNextValSql() { + assertThat(dialect.getSequenceNextValSql("test_sequence"), is("nextval(test_sequence)")); + } + + @Test + void testEscapeLikePattern() { + assertThat(dialect.escapeLikePattern(""), is("")); + assertThat(dialect.escapeLikePattern(null), nullValue()); + assertThat(dialect.escapeLikePattern("pattern"), is("pattern")); + assertThat(dialect.escapeLikePattern("%pattern"), is("$%pattern")); + assertThat(dialect.escapeLikePattern("_pattern"), is("$_pattern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat$%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat$_tern")); + assertThat(dialect.escapeLikePattern("pattern%"), is("pattern$%")); + assertThat(dialect.escapeLikePattern("pattern_"), is("pattern$_")); + assertThat(dialect.escapeLikePattern("pat[]tern"), is("pat[]tern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat_tern")); + } + + @Test + void testGetEscapeChar() { + assertThat(dialect.getEscapeChar(), is('$')); + } + + @Test + void testSupports() { + assertThat(dialect.supportsBulkInsert(), is(true)); + assertThat(dialect.supportsLimitClause(), is(true)); + assertThat(dialect.supportsNullValuesOrdering(), is(false)); + assertThat(dialect.supportsIdentity(), is(true)); + assertThat(dialect.supportsSequence(), is(true)); + assertThat(dialect.isRemoveTerminator(), is(true)); + assertThat(dialect.isRollbackToSavepointBeforeRetry(), is(false)); + assertThat(dialect.supportsForUpdate(), is(true)); + assertThat(dialect.supportsForUpdateNoWait(), is(true)); + assertThat(dialect.supportsForUpdateWait(), is(false)); + assertThat(dialect.supportsOptimizerHints(), is(true)); + assertThat(dialect.supportsEntityBulkUpdateOptimisticLock(), is(true)); + } + + @Test + void testGetLimitClause() { + assertThat(dialect.getLimitClause(3, 5), is("LIMIT 3 OFFSET 5" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 5), is("OFFSET 5" + System.lineSeparator())); + assertThat(dialect.getLimitClause(3, 0), is("LIMIT 3 " + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 0), is("")); + } + + @Test + void testAddForUpdateClause() { + var sql = new StringBuilder("SELECT * FROM test WHERE 1 = 1 ORDER id").append(System.lineSeparator()); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NORMAL, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NOWAIT, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE NOWAIT")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.WAIT, 10).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE WAIT 10")); + } + + @Test + void testAddOptimizerHints1() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM test") + .append(System.lineSeparator()) + .append("WHERE 1 = 1 ORDER id") + .append(System.lineSeparator()); + List hints = new ArrayList<>(); + hints.add("INDEX (test test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT" + System.lineSeparator() + + " * FROM test INDEX (test test_ix) USE_NL" + + System.lineSeparator() + + "WHERE 1 = 1 ORDER id" + + System.lineSeparator())); + } + + @Test + void testAddOptimizerHints2() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM PUBLIC.TEST_1"); + List hints = new ArrayList<>(); + hints.add("INDEX (PUBLIC.TEST_1 test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT" + + System.lineSeparator() + + " * FROM PUBLIC.TEST_1 INDEX (PUBLIC.TEST_1 test_ix) USE_NL" + + System.lineSeparator())); + } + + @Test + void testGetPessimisticLockingErrorCodes() { + assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("3572"))); + } + +} diff --git a/src/test/java/jp/co/future/uroborosql/dialect/MariaDb5DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/MariaDb5DialectTest.java new file mode 100644 index 00000000..dd727fcb --- /dev/null +++ b/src/test/java/jp/co/future/uroborosql/dialect/MariaDb5DialectTest.java @@ -0,0 +1,189 @@ +package jp.co.future.uroborosql.dialect; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import jp.co.future.uroborosql.connection.ConnectionContext; +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.enums.ForUpdateType; +import jp.co.future.uroborosql.exception.UroborosqlRuntimeException; + +/** + * MariaDb5Dialectの個別実装部分のテストケース + * + * @author H.Sugimoto + * + */ +public class MariaDb5DialectTest { + private final Dialect dialect = new MariaDb5Dialect(); + + @Test + void testAccept5() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "MariaDB-5.0"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(MariaDb5Dialect.class)); + } + + @Test + void testAccept10() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "MariaDB-10.0"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(MariaDb5Dialect.class))); + } + + @Test + void testGetSequenceNextValSql() { + assertThrows(UroborosqlRuntimeException.class, () -> { + dialect.getSequenceNextValSql("test_sequence"); + }); + } + + @Test + void testEscapeLikePattern() { + assertThat(dialect.escapeLikePattern(""), is("")); + assertThat(dialect.escapeLikePattern(null), nullValue()); + assertThat(dialect.escapeLikePattern("pattern"), is("pattern")); + assertThat(dialect.escapeLikePattern("%pattern"), is("$%pattern")); + assertThat(dialect.escapeLikePattern("_pattern"), is("$_pattern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat$%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat$_tern")); + assertThat(dialect.escapeLikePattern("pattern%"), is("pattern$%")); + assertThat(dialect.escapeLikePattern("pattern_"), is("pattern$_")); + assertThat(dialect.escapeLikePattern("pat[]tern"), is("pat[]tern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat_tern")); + } + + @Test + void testGetEscapeChar() { + assertThat(dialect.getEscapeChar(), is('$')); + } + + @Test + void testSupports() { + assertThat(dialect.supportsBulkInsert(), is(true)); + assertThat(dialect.supportsLimitClause(), is(true)); + assertThat(dialect.supportsNullValuesOrdering(), is(false)); + assertThat(dialect.supportsIdentity(), is(true)); + assertThat(dialect.supportsSequence(), is(false)); + assertThat(dialect.isRemoveTerminator(), is(true)); + assertThat(dialect.isRollbackToSavepointBeforeRetry(), is(false)); + assertThat(dialect.supportsForUpdate(), is(true)); + assertThat(dialect.supportsForUpdateNoWait(), is(true)); + assertThat(dialect.supportsForUpdateWait(), is(false)); + assertThat(dialect.supportsOptimizerHints(), is(true)); + assertThat(dialect.supportsEntityBulkUpdateOptimisticLock(), is(true)); + } + + @Test + void testGetLimitClause() { + assertThat(dialect.getLimitClause(3, 5), is("LIMIT 3 OFFSET 5" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 5), is("OFFSET 5" + System.lineSeparator())); + assertThat(dialect.getLimitClause(3, 0), is("LIMIT 3 " + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 0), is("")); + } + + @Test + void testAddForUpdateClause() { + var sql = new StringBuilder("SELECT * FROM test WHERE 1 = 1 ORDER id").append(System.lineSeparator()); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NORMAL, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NOWAIT, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE NOWAIT")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.WAIT, 10).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE WAIT 10")); + } + + @Test + void testAddOptimizerHints1() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM test") + .append(System.lineSeparator()) + .append("WHERE 1 = 1 ORDER id") + .append(System.lineSeparator()); + List hints = new ArrayList<>(); + hints.add("INDEX (test test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT" + System.lineSeparator() + + " * FROM test INDEX (test test_ix) USE_NL" + + System.lineSeparator() + + "WHERE 1 = 1 ORDER id" + + System.lineSeparator())); + } + + @Test + void testAddOptimizerHints2() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM PUBLIC.TEST_1"); + List hints = new ArrayList<>(); + hints.add("INDEX (PUBLIC.TEST_1 test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT" + + System.lineSeparator() + + " * FROM PUBLIC.TEST_1 INDEX (PUBLIC.TEST_1 test_ix) USE_NL" + + System.lineSeparator())); + } + + @Test + void testGetPessimisticLockingErrorCodes() { + assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("3572"))); + } + +} diff --git a/src/test/java/jp/co/future/uroborosql/dialect/Oracle11DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/Oracle11DialectTest.java index 4d7bdf22..6f523db0 100644 --- a/src/test/java/jp/co/future/uroborosql/dialect/Oracle11DialectTest.java +++ b/src/test/java/jp/co/future/uroborosql/dialect/Oracle11DialectTest.java @@ -20,7 +20,7 @@ import jp.co.future.uroborosql.enums.ForUpdateType; /** - * Oracle10Dialectの個別実装部分のテストケース + * Oracle11Dialectの個別実装部分のテストケース * * @author H.Sugimoto * diff --git a/src/test/java/jp/co/future/uroborosql/dialect/Oracle12DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/Oracle12DialectTest.java index bb57dc0a..2482b514 100644 --- a/src/test/java/jp/co/future/uroborosql/dialect/Oracle12DialectTest.java +++ b/src/test/java/jp/co/future/uroborosql/dialect/Oracle12DialectTest.java @@ -20,7 +20,7 @@ import jp.co.future.uroborosql.enums.ForUpdateType; /** - * Oracle10Dialectの個別実装部分のテストケース + * Oracle12Dialectの個別実装部分のテストケース * * @author H.Sugimoto * @@ -103,7 +103,7 @@ public String getDatabaseName() { var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); - assertThat(dialect, instanceOf(Oracle12Dialect.class)); + assertThat(dialect, not(instanceOf(Oracle12Dialect.class))); } @Test diff --git a/src/test/java/jp/co/future/uroborosql/dialect/Oracle18DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/Oracle18DialectTest.java new file mode 100644 index 00000000..6561b4d1 --- /dev/null +++ b/src/test/java/jp/co/future/uroborosql/dialect/Oracle18DialectTest.java @@ -0,0 +1,204 @@ +package jp.co.future.uroborosql.dialect; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import jp.co.future.uroborosql.connection.ConnectionContext; +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.enums.ForUpdateType; + +/** + * Oracle18Dialectの個別実装部分のテストケース + * + * @author H.Sugimoto + * + */ +public class Oracle18DialectTest { + private final Dialect dialect = new Oracle18Dialect(); + + @Test + void testAccept18() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-18.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(Oracle18Dialect.class)); + } + + @Test + void testAcceptUnder18() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-12.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle18Dialect.class))); + } + + @Test + void testAcceptOver18() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-19.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle18Dialect.class))); + } + + @Test + void testGetSequenceNextValSql() { + assertThat(dialect.getSequenceNextValSql("test_sequence"), is("test_sequence.nextval")); + } + + @Test + void testEscapeLikePattern() { + assertThat(dialect.escapeLikePattern(""), is("")); + assertThat(dialect.escapeLikePattern(null), nullValue()); + assertThat(dialect.escapeLikePattern("pattern"), is("pattern")); + assertThat(dialect.escapeLikePattern("%pattern"), is("\\%pattern")); + assertThat(dialect.escapeLikePattern("_pattern"), is("\\_pattern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + assertThat(dialect.escapeLikePattern("pattern%"), is("pattern\\%")); + assertThat(dialect.escapeLikePattern("pattern_"), is("pattern\\_")); + assertThat(dialect.escapeLikePattern("pat[]tern"), is("pat[]tern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + } + + @Test + void testGetEscapeChar() { + assertThat(dialect.getEscapeChar(), is('\\')); + } + + @Test + void testSupports() { + assertThat(dialect.supportsBulkInsert(), is(false)); + assertThat(dialect.supportsLimitClause(), is(true)); + assertThat(dialect.supportsNullValuesOrdering(), is(true)); + assertThat(dialect.supportsIdentity(), is(true)); + assertThat(dialect.supportsSequence(), is(true)); + assertThat(dialect.isRemoveTerminator(), is(true)); + assertThat(dialect.isRollbackToSavepointBeforeRetry(), is(false)); + assertThat(dialect.supportsForUpdate(), is(true)); + assertThat(dialect.supportsForUpdateNoWait(), is(true)); + assertThat(dialect.supportsForUpdateWait(), is(true)); + assertThat(dialect.supportsOptimizerHints(), is(true)); + assertThat(dialect.supportsEntityBulkUpdateOptimisticLock(), is(true)); + } + + @Test + void testGetLimitClause() { + assertThat(dialect.getLimitClause(3, 5), is("OFFSET 5 ROWS FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 5), is("OFFSET 5 ROWS" + System.lineSeparator())); + assertThat(dialect.getLimitClause(3, 0), is("FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 0), is("")); + } + + @Test + void testAddForUpdateClause() { + var sql = new StringBuilder("SELECT * FROM test WHERE 1 = 1 ORDER id").append(System.lineSeparator()); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NORMAL, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NOWAIT, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE NOWAIT")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.WAIT, 10).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE WAIT 10")); + } + + @Test + void testAddOptimizerHints1() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM test WHERE 1 = 1 ORDER id") + .append(System.lineSeparator()); + List hints = new ArrayList<>(); + hints.add("INDEX (test test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /*+ INDEX (test test_ix) USE_NL */" + System.lineSeparator() + + " * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator())); + } + + @Test + void testAddOptimizerHints2() { + var sql = new StringBuilder("SELECT /* SQL_ID */") + .append(System.lineSeparator()) + .append(" * FROM PUBLIC.TEST_1"); + List hints = new ArrayList<>(); + hints.add("INDEX (PUBLIC.TEST_1 test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /* SQL_ID */ /*+ INDEX (PUBLIC.TEST_1 test_ix) USE_NL */" + System.lineSeparator() + + " * FROM PUBLIC.TEST_1")); + } + + @Test + void testGetPessimisticLockingErrorCodes() { + assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("54", "30006"))); + } + +} diff --git a/src/test/java/jp/co/future/uroborosql/dialect/Oracle19DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/Oracle19DialectTest.java new file mode 100644 index 00000000..16d93d4f --- /dev/null +++ b/src/test/java/jp/co/future/uroborosql/dialect/Oracle19DialectTest.java @@ -0,0 +1,204 @@ +package jp.co.future.uroborosql.dialect; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import jp.co.future.uroborosql.connection.ConnectionContext; +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.enums.ForUpdateType; + +/** + * Oracle19Dialectの個別実装部分のテストケース + * + * @author H.Sugimoto + * + */ +public class Oracle19DialectTest { + private final Dialect dialect = new Oracle19Dialect(); + + @Test + void testAccept19() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-19.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(Oracle19Dialect.class)); + } + + @Test + void testAcceptUnder19() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-18.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle19Dialect.class))); + } + + @Test + void testAcceptOver19() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-21.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle19Dialect.class))); + } + + @Test + void testGetSequenceNextValSql() { + assertThat(dialect.getSequenceNextValSql("test_sequence"), is("test_sequence.nextval")); + } + + @Test + void testEscapeLikePattern() { + assertThat(dialect.escapeLikePattern(""), is("")); + assertThat(dialect.escapeLikePattern(null), nullValue()); + assertThat(dialect.escapeLikePattern("pattern"), is("pattern")); + assertThat(dialect.escapeLikePattern("%pattern"), is("\\%pattern")); + assertThat(dialect.escapeLikePattern("_pattern"), is("\\_pattern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + assertThat(dialect.escapeLikePattern("pattern%"), is("pattern\\%")); + assertThat(dialect.escapeLikePattern("pattern_"), is("pattern\\_")); + assertThat(dialect.escapeLikePattern("pat[]tern"), is("pat[]tern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + } + + @Test + void testGetEscapeChar() { + assertThat(dialect.getEscapeChar(), is('\\')); + } + + @Test + void testSupports() { + assertThat(dialect.supportsBulkInsert(), is(false)); + assertThat(dialect.supportsLimitClause(), is(true)); + assertThat(dialect.supportsNullValuesOrdering(), is(true)); + assertThat(dialect.supportsIdentity(), is(true)); + assertThat(dialect.supportsSequence(), is(true)); + assertThat(dialect.isRemoveTerminator(), is(true)); + assertThat(dialect.isRollbackToSavepointBeforeRetry(), is(false)); + assertThat(dialect.supportsForUpdate(), is(true)); + assertThat(dialect.supportsForUpdateNoWait(), is(true)); + assertThat(dialect.supportsForUpdateWait(), is(true)); + assertThat(dialect.supportsOptimizerHints(), is(true)); + assertThat(dialect.supportsEntityBulkUpdateOptimisticLock(), is(true)); + } + + @Test + void testGetLimitClause() { + assertThat(dialect.getLimitClause(3, 5), is("OFFSET 5 ROWS FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 5), is("OFFSET 5 ROWS" + System.lineSeparator())); + assertThat(dialect.getLimitClause(3, 0), is("FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 0), is("")); + } + + @Test + void testAddForUpdateClause() { + var sql = new StringBuilder("SELECT * FROM test WHERE 1 = 1 ORDER id").append(System.lineSeparator()); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NORMAL, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NOWAIT, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE NOWAIT")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.WAIT, 10).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE WAIT 10")); + } + + @Test + void testAddOptimizerHints1() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM test WHERE 1 = 1 ORDER id") + .append(System.lineSeparator()); + List hints = new ArrayList<>(); + hints.add("INDEX (test test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /*+ INDEX (test test_ix) USE_NL */" + System.lineSeparator() + + " * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator())); + } + + @Test + void testAddOptimizerHints2() { + var sql = new StringBuilder("SELECT /* SQL_ID */") + .append(System.lineSeparator()) + .append(" * FROM PUBLIC.TEST_1"); + List hints = new ArrayList<>(); + hints.add("INDEX (PUBLIC.TEST_1 test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /* SQL_ID */ /*+ INDEX (PUBLIC.TEST_1 test_ix) USE_NL */" + System.lineSeparator() + + " * FROM PUBLIC.TEST_1")); + } + + @Test + void testGetPessimisticLockingErrorCodes() { + assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("54", "30006"))); + } + +} diff --git a/src/test/java/jp/co/future/uroborosql/dialect/Oracle21DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/Oracle21DialectTest.java new file mode 100644 index 00000000..b8a85485 --- /dev/null +++ b/src/test/java/jp/co/future/uroborosql/dialect/Oracle21DialectTest.java @@ -0,0 +1,204 @@ +package jp.co.future.uroborosql.dialect; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import jp.co.future.uroborosql.connection.ConnectionContext; +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.enums.ForUpdateType; + +/** + * Oracle21Dialectの個別実装部分のテストケース + * + * @author H.Sugimoto + * + */ +public class Oracle21DialectTest { + private final Dialect dialect = new Oracle21Dialect(); + + @Test + void testAccept21() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-21.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(Oracle21Dialect.class)); + } + + @Test + void testAcceptUnder21() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-19.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle21Dialect.class))); + } + + @Test + void testAcceptOver21() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-23.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle21Dialect.class))); + } + + @Test + void testGetSequenceNextValSql() { + assertThat(dialect.getSequenceNextValSql("test_sequence"), is("test_sequence.nextval")); + } + + @Test + void testEscapeLikePattern() { + assertThat(dialect.escapeLikePattern(""), is("")); + assertThat(dialect.escapeLikePattern(null), nullValue()); + assertThat(dialect.escapeLikePattern("pattern"), is("pattern")); + assertThat(dialect.escapeLikePattern("%pattern"), is("\\%pattern")); + assertThat(dialect.escapeLikePattern("_pattern"), is("\\_pattern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + assertThat(dialect.escapeLikePattern("pattern%"), is("pattern\\%")); + assertThat(dialect.escapeLikePattern("pattern_"), is("pattern\\_")); + assertThat(dialect.escapeLikePattern("pat[]tern"), is("pat[]tern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + } + + @Test + void testGetEscapeChar() { + assertThat(dialect.getEscapeChar(), is('\\')); + } + + @Test + void testSupports() { + assertThat(dialect.supportsBulkInsert(), is(false)); + assertThat(dialect.supportsLimitClause(), is(true)); + assertThat(dialect.supportsNullValuesOrdering(), is(true)); + assertThat(dialect.supportsIdentity(), is(true)); + assertThat(dialect.supportsSequence(), is(true)); + assertThat(dialect.isRemoveTerminator(), is(true)); + assertThat(dialect.isRollbackToSavepointBeforeRetry(), is(false)); + assertThat(dialect.supportsForUpdate(), is(true)); + assertThat(dialect.supportsForUpdateNoWait(), is(true)); + assertThat(dialect.supportsForUpdateWait(), is(true)); + assertThat(dialect.supportsOptimizerHints(), is(true)); + assertThat(dialect.supportsEntityBulkUpdateOptimisticLock(), is(true)); + } + + @Test + void testGetLimitClause() { + assertThat(dialect.getLimitClause(3, 5), is("OFFSET 5 ROWS FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 5), is("OFFSET 5 ROWS" + System.lineSeparator())); + assertThat(dialect.getLimitClause(3, 0), is("FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 0), is("")); + } + + @Test + void testAddForUpdateClause() { + var sql = new StringBuilder("SELECT * FROM test WHERE 1 = 1 ORDER id").append(System.lineSeparator()); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NORMAL, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NOWAIT, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE NOWAIT")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.WAIT, 10).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE WAIT 10")); + } + + @Test + void testAddOptimizerHints1() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM test WHERE 1 = 1 ORDER id") + .append(System.lineSeparator()); + List hints = new ArrayList<>(); + hints.add("INDEX (test test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /*+ INDEX (test test_ix) USE_NL */" + System.lineSeparator() + + " * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator())); + } + + @Test + void testAddOptimizerHints2() { + var sql = new StringBuilder("SELECT /* SQL_ID */") + .append(System.lineSeparator()) + .append(" * FROM PUBLIC.TEST_1"); + List hints = new ArrayList<>(); + hints.add("INDEX (PUBLIC.TEST_1 test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /* SQL_ID */ /*+ INDEX (PUBLIC.TEST_1 test_ix) USE_NL */" + System.lineSeparator() + + " * FROM PUBLIC.TEST_1")); + } + + @Test + void testGetPessimisticLockingErrorCodes() { + assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("54", "30006"))); + } + +} diff --git a/src/test/java/jp/co/future/uroborosql/dialect/Oracle23DialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/Oracle23DialectTest.java new file mode 100644 index 00000000..c8afbdb5 --- /dev/null +++ b/src/test/java/jp/co/future/uroborosql/dialect/Oracle23DialectTest.java @@ -0,0 +1,204 @@ +package jp.co.future.uroborosql.dialect; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; + +import jp.co.future.uroborosql.connection.ConnectionContext; +import jp.co.future.uroborosql.connection.ConnectionSupplier; +import jp.co.future.uroborosql.enums.ForUpdateType; + +/** + * Oracle23Dialectの個別実装部分のテストケース + * + * @author H.Sugimoto + * + */ +public class Oracle23DialectTest { + private final Dialect dialect = new Oracle23Dialect(); + + @Test + void testAccept23() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-23.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(Oracle23Dialect.class)); + } + + @Test + void testAcceptUnder23() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-21.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, not(instanceOf(Oracle23Dialect.class))); + } + + @Test + void testAcceptOver23() { + ConnectionSupplier supplier = new ConnectionSupplier() { + + @Override + public Connection getConnection() { + return null; + } + + @Override + public Connection getConnection(final ConnectionContext ctx) { + return null; + } + + @Override + public String getDatabaseName() { + return "Oracle-24.1"; + } + }; + + var dialect = StreamSupport.stream(ServiceLoader.load(Dialect.class).spliterator(), false) + .filter(d -> d.accept(supplier)).findFirst().orElseGet(DefaultDialect::new); + + assertThat(dialect, instanceOf(Oracle23Dialect.class)); + } + + @Test + void testGetSequenceNextValSql() { + assertThat(dialect.getSequenceNextValSql("test_sequence"), is("test_sequence.nextval")); + } + + @Test + void testEscapeLikePattern() { + assertThat(dialect.escapeLikePattern(""), is("")); + assertThat(dialect.escapeLikePattern(null), nullValue()); + assertThat(dialect.escapeLikePattern("pattern"), is("pattern")); + assertThat(dialect.escapeLikePattern("%pattern"), is("\\%pattern")); + assertThat(dialect.escapeLikePattern("_pattern"), is("\\_pattern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + assertThat(dialect.escapeLikePattern("pattern%"), is("pattern\\%")); + assertThat(dialect.escapeLikePattern("pattern_"), is("pattern\\_")); + assertThat(dialect.escapeLikePattern("pat[]tern"), is("pat[]tern")); + assertThat(dialect.escapeLikePattern("pat%tern"), is("pat\\%tern")); + assertThat(dialect.escapeLikePattern("pat_tern"), is("pat\\_tern")); + } + + @Test + void testGetEscapeChar() { + assertThat(dialect.getEscapeChar(), is('\\')); + } + + @Test + void testSupports() { + assertThat(dialect.supportsBulkInsert(), is(true)); + assertThat(dialect.supportsLimitClause(), is(true)); + assertThat(dialect.supportsNullValuesOrdering(), is(true)); + assertThat(dialect.supportsIdentity(), is(true)); + assertThat(dialect.supportsSequence(), is(true)); + assertThat(dialect.isRemoveTerminator(), is(true)); + assertThat(dialect.isRollbackToSavepointBeforeRetry(), is(false)); + assertThat(dialect.supportsForUpdate(), is(true)); + assertThat(dialect.supportsForUpdateNoWait(), is(true)); + assertThat(dialect.supportsForUpdateWait(), is(true)); + assertThat(dialect.supportsOptimizerHints(), is(true)); + assertThat(dialect.supportsEntityBulkUpdateOptimisticLock(), is(true)); + } + + @Test + void testGetLimitClause() { + assertThat(dialect.getLimitClause(3, 5), is("OFFSET 5 ROWS FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 5), is("OFFSET 5 ROWS" + System.lineSeparator())); + assertThat(dialect.getLimitClause(3, 0), is("FETCH FIRST 3 ROWS ONLY" + System.lineSeparator())); + assertThat(dialect.getLimitClause(0, 0), is("")); + } + + @Test + void testAddForUpdateClause() { + var sql = new StringBuilder("SELECT * FROM test WHERE 1 = 1 ORDER id").append(System.lineSeparator()); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NORMAL, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.NOWAIT, -1).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE NOWAIT")); + assertThat(dialect.addForUpdateClause(sql, ForUpdateType.WAIT, 10).toString(), + is("SELECT * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator() + "FOR UPDATE WAIT 10")); + } + + @Test + void testAddOptimizerHints1() { + var sql = new StringBuilder("SELECT") + .append(System.lineSeparator()) + .append(" * FROM test WHERE 1 = 1 ORDER id") + .append(System.lineSeparator()); + List hints = new ArrayList<>(); + hints.add("INDEX (test test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /*+ INDEX (test test_ix) USE_NL */" + System.lineSeparator() + + " * FROM test WHERE 1 = 1 ORDER id" + System.lineSeparator())); + } + + @Test + void testAddOptimizerHints2() { + var sql = new StringBuilder("SELECT /* SQL_ID */") + .append(System.lineSeparator()) + .append(" * FROM PUBLIC.TEST_1"); + List hints = new ArrayList<>(); + hints.add("INDEX (PUBLIC.TEST_1 test_ix)"); + hints.add("USE_NL"); + + assertThat(dialect.addOptimizerHints(sql, hints).toString(), + is("SELECT /* SQL_ID */ /*+ INDEX (PUBLIC.TEST_1 test_ix) USE_NL */" + System.lineSeparator() + + " * FROM PUBLIC.TEST_1")); + } + + @Test + void testGetPessimisticLockingErrorCodes() { + assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("54", "30006"))); + } + +} diff --git a/src/test/java/jp/co/future/uroborosql/dialect/PostgresqlDialectTest.java b/src/test/java/jp/co/future/uroborosql/dialect/PostgresqlDialectTest.java index 867578e0..1edbd63b 100644 --- a/src/test/java/jp/co/future/uroborosql/dialect/PostgresqlDialectTest.java +++ b/src/test/java/jp/co/future/uroborosql/dialect/PostgresqlDialectTest.java @@ -177,4 +177,9 @@ void testGetPessimisticLockingErrorCodes() { assertThat(dialect.getPessimisticLockingErrorCodes(), is(containsInAnyOrder("55P03"))); } + @Test + void testGetSequenceNextValSql() { + assertThat(dialect.getSequenceNextValSql("test_sequence"), is("nextval('test_sequence')")); + } + }