diff --git a/.github/workflows/db2luw.yml b/.github/workflows/db2luw.yml new file mode 100644 index 000000000..2f3981ea2 --- /dev/null +++ b/.github/workflows/db2luw.yml @@ -0,0 +1,38 @@ + +name: DB2 LUW + +on: + workflow_dispatch: + schedule: + - cron: '10 8 * * 2,5' + +jobs: + build: + + runs-on: ${{ matrix.os }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + java_version: [11] + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v2 + - name: Set up Java + uses: actions/setup-java@v2 + with: + java-version: ${{ matrix.java_version }} + distribution: 'adopt' + - name: Maven cache + uses: actions/cache@v2 + env: + cache-name: maven-cache + with: + path: + ~/.m2 + key: build-${{ env.cache-name }} + - name: db2 + run: mvn clean test -Dprops.file=testconfig/ebean-db2.properties diff --git a/.github/workflows/mariadb-10.6.yml b/.github/workflows/mariadb.yml similarity index 97% rename from .github/workflows/mariadb-10.6.yml rename to .github/workflows/mariadb.yml index cae97ac27..b7db361cd 100644 --- a/.github/workflows/mariadb-10.6.yml +++ b/.github/workflows/mariadb.yml @@ -1,5 +1,5 @@ -name: MariaDB 10.6 +name: MariaDB on: workflow_dispatch: diff --git a/.github/workflows/multi-platform-test.yml b/.github/workflows/multi-db-platform.yml similarity index 90% rename from .github/workflows/multi-platform-test.yml rename to .github/workflows/multi-db-platform.yml index f85636c9b..19603f174 100644 --- a/.github/workflows/multi-platform-test.yml +++ b/.github/workflows/multi-db-platform.yml @@ -1,5 +1,5 @@ -name: Multi-database platform test +name: Multi-database platform on: [workflow_dispatch] @@ -43,3 +43,5 @@ jobs: run: mvn clean test -Dprops.file=testconfig/ebean-sqlserver17.properties - name: mariadb run: mvn clean test -Dprops.file=testconfig/ebean-mariadb.properties + - name: db2 + run: mvn clean test -Dprops.file=testconfig/ebean-db2.properties diff --git a/.github/workflows/oracle18.yml b/.github/workflows/oracle18.yml new file mode 100644 index 000000000..20a8d01e2 --- /dev/null +++ b/.github/workflows/oracle18.yml @@ -0,0 +1,38 @@ + +name: Oracle18 + +on: + workflow_dispatch: + schedule: + - cron: '10 6 * * 1,4' + +jobs: + build: + + runs-on: ${{ matrix.os }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + java_version: [11] + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v2 + - name: Set up Java + uses: actions/setup-java@v2 + with: + java-version: ${{ matrix.java_version }} + distribution: 'zulu' + - name: Maven cache + uses: actions/cache@v2 + env: + cache-name: maven-cache + with: + path: + ~/.m2 + key: build-${{ env.cache-name }} + - name: oracle + run: mvn clean test -Dprops.file=testconfig/ebean-oracle.properties diff --git a/.github/workflows/sqlserver-2019-latest.yml b/.github/workflows/sqlserver-2019.yml similarity index 96% rename from .github/workflows/sqlserver-2019-latest.yml rename to .github/workflows/sqlserver-2019.yml index fd803655f..6b87b1332 100644 --- a/.github/workflows/sqlserver-2019-latest.yml +++ b/.github/workflows/sqlserver-2019.yml @@ -1,5 +1,5 @@ -name: SqlServer 2019 latest +name: SqlServer 2019 on: [workflow_dispatch] diff --git a/.github/workflows/sqlserver-2017-latest.yml b/.github/workflows/sqlserver.yml similarity index 96% rename from .github/workflows/sqlserver-2017-latest.yml rename to .github/workflows/sqlserver.yml index 6c72d8980..fd10bafe6 100644 --- a/.github/workflows/sqlserver-2017-latest.yml +++ b/.github/workflows/sqlserver.yml @@ -1,5 +1,5 @@ -name: SqlServer 2017 latest +name: SqlServer on: workflow_dispatch: diff --git a/README.md b/README.md index a560d8c82..25ad08f5f 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ [![Postgres](https://github.com/ebean-orm/ebean/actions/workflows/postgres.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/postgres.yml) [![MySql](https://github.com/ebean-orm/ebean/actions/workflows/mysql.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mysql.yml) -[![MariaDB](https://github.com/ebean-orm/ebean/actions/workflows/mariadb-10.6.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mariadb-10.6.yml) -[![SqlServer](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver-2017-latest.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver-2017-latest.yml) +[![MariaDB](https://github.com/ebean-orm/ebean/actions/workflows/mariadb.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mariadb.yml) +[![SqlServer](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver.yml) # Sponsors diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index ca3d094a5..9aac6017d 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -49,7 +49,7 @@ io.ebean ebean-annotation - 7.4-FOC3 + 7.6 diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java index d46f6dd37..3950411aa 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java @@ -55,12 +55,12 @@ public class DatabasePlatform { protected boolean supportsSavepointId = true; protected boolean useMigrationStoredProcedures = false; - + /** * Can we use native java time API objects in * {@link ResultSet#getObject(int, Class)} and * {@link PreparedStatement#setObject(int, Object)}. - * + * * Not all drivers (DB2 e.g.) will support this. */ protected boolean supportsNativeJavaTime = true; @@ -214,6 +214,8 @@ public class DatabasePlatform { */ protected PersistBatch persistBatchOnCascade = PersistBatch.ALL; + protected int maxInBinding; + /** * The maximum length of table names - used specifically when derived * default table names for intersection tables. @@ -370,6 +372,13 @@ public class DatabasePlatform { return inlineSqlUpdateLimit; } + /** + * Return the maximum number of bind values this database platform allows or zero for no limit. + */ + public int getMaxInBinding() { + return maxInBinding; + } + /** * Return the maximum table name length. *

diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java similarity index 85% rename from ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java rename to ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java index 2d2673b46..949e978c3 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java @@ -15,16 +15,11 @@ import io.ebean.config.dbplatform.SqlErrorCodes; /** * DB2 specific platform. */ -public class DB2Platform extends DatabasePlatform { +public abstract class BaseDB2Platform extends DatabasePlatform { - public DB2Platform() { + public BaseDB2Platform() { super(); this.platform = Platform.DB2; - // Note: DB2 (at least LUW supports length up to 128) - // TOOD: Check if we need to introduce a new platform (DB2_LUW_11 ?) - // FIXME: This differs to original ebean branch, but is required run tests. - this.maxTableNameLength = 128; - this.maxConstraintNameLength = 128; this.supportsNativeJavaTime = false; this.truncateTable = "truncate table %s reuse storage ignore delete triggers immediate"; this.likeClauseRaw = "like ?"; diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ForIPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ForIPlatform.java new file mode 100644 index 000000000..fa6028858 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ForIPlatform.java @@ -0,0 +1,39 @@ +package io.ebean.config.dbplatform.db2; + +import io.ebean.annotation.Platform; +import io.ebean.config.dbplatform.DbPlatformType; +import io.ebean.config.dbplatform.DbType; +import io.ebean.config.dbplatform.SqlErrorCodes; + +import java.sql.Types; + +/** + * DB2 specific platform for i Series. + * + * @author Cédric Sougné + */ +public class DB2ForIPlatform extends BaseDB2Platform { + + public DB2ForIPlatform() { + super(); + this.platform = Platform.DB2FORI; + // Note: IBM i from 7.1 allow up to to 128 + // TODO: Check if we need to introduce older platform (DB2ForI_6 ? but older + // documentation is not anymore published on ibm.com), + this.maxTableNameLength = 128; + this.maxConstraintNameLength = 128; + + this.dbIdentity.setSupportsIdentity(true); + + this.exceptionTranslator = new SqlErrorCodes().addAcquireLock("57033") // key -913 + .addDuplicateKey("23505") // -803 + // .addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667") + // we need SQLState, not code: + // https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/codes/src/tpc/db2z_n.html + .addDataIntegrity("23502", "23503", "23504", "23507", "23511", "23512", "23513", "42917", "23515") + .build(); + + booleanDbType = Types.SMALLINT; + dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("smallint default 0")); + } +} diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LegacyPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LegacyPlatform.java new file mode 100644 index 000000000..51f03c2a4 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LegacyPlatform.java @@ -0,0 +1,20 @@ +package io.ebean.config.dbplatform.db2; + +import io.ebean.annotation.Platform; + +/** + * DB2 specific platform for older DB2 versions. This platform is here for + * compatibility reasons. It uses a length limit of 18 chars for table and + * constraint names. Newer DB2 versions will support up to 128. It is strongly + * recommended to migrate to db2luw/DB2ForI or db2zos platform. + */ +public class DB2LegacyPlatform extends BaseDB2Platform { + public DB2LegacyPlatform() { + super(); + this.platform = Platform.DB2; + // Note: DB2 (at least LUW supports length up to 128) + // TOOD: Check if we need to introduce a new platform (DB2_LUW_11 ?) + this.maxTableNameLength = 18; + this.maxConstraintNameLength = 18; + } +} diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LuwPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LuwPlatform.java new file mode 100644 index 000000000..967cc2428 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LuwPlatform.java @@ -0,0 +1,17 @@ +package io.ebean.config.dbplatform.db2; + +import io.ebean.annotation.Platform; + +/** + * DB2 platform for Linux/Unix/Windows. + * @author Roland Praml, FOCONIS AG + * + */ +public class DB2LuwPlatform extends BaseDB2Platform { + public DB2LuwPlatform() { + super(); + this.platform = Platform.DB2LUW; + this.maxTableNameLength = 128; + this.maxConstraintNameLength = 128; + } +} diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ZosPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ZosPlatform.java new file mode 100644 index 000000000..2cb4cb35b --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ZosPlatform.java @@ -0,0 +1,18 @@ +package io.ebean.config.dbplatform.db2; + +import io.ebean.annotation.Platform; + +/** + * DB2 platform for z/OS. + * Note: This platform is currently not tested! + * @author Roland Praml, FOCONIS AG + * + */ +public class DB2ZosPlatform extends BaseDB2Platform { + public DB2ZosPlatform() { + super(); + this.platform = Platform.DB2ZOS; + this.maxTableNameLength = 128; + this.maxConstraintNameLength = 128; + } +} diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java index e9dd57207..74f68ced7 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java @@ -23,6 +23,7 @@ public class OraclePlatform extends DatabasePlatform { super(); this.platform = Platform.ORACLE; this.supportsDeleteTableAlias = true; + this.maxInBinding = 1000; this.maxTableNameLength = 30; this.maxConstraintNameLength = 30; this.dbEncrypt = new OracleDbEncrypt(); diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java index eb3dad7be..4dd2077b2 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java @@ -27,6 +27,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform { // SQL Server unless we are using sequences this.dbEncrypt = new SqlServerDbEncrypt(); this.persistBatchOnCascade = PersistBatch.NONE; + this.maxInBinding = 2000; this.idInExpandedForm = true; this.selectCountWithAlias = true; this.selectCountWithColumnAlias = true; diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java new file mode 100644 index 000000000..622f8e5f8 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java @@ -0,0 +1,12 @@ +package io.ebean.config.dbplatform.yugabyte; + +import io.ebean.annotation.Platform; +import io.ebean.config.dbplatform.postgres.PostgresPlatform; + +public class YugabytePlaform extends PostgresPlatform { + + public YugabytePlaform() { + super(); + this.platform = Platform.YUGABYTE; + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java index 96600609a..5b49d5292 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -4,7 +4,10 @@ import io.ebean.config.DatabaseConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform; import io.ebean.config.dbplatform.cockroach.CockroachPlatform; -import io.ebean.config.dbplatform.db2.DB2Platform; +import io.ebean.config.dbplatform.db2.DB2ForIPlatform; +import io.ebean.config.dbplatform.db2.DB2LegacyPlatform; +import io.ebean.config.dbplatform.db2.DB2LuwPlatform; +import io.ebean.config.dbplatform.db2.DB2ZosPlatform; import io.ebean.config.dbplatform.h2.H2Platform; import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; @@ -21,12 +24,14 @@ import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform; import io.ebean.config.dbplatform.sqlite.SQLitePlatform; import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform; import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform; +import io.ebean.config.dbplatform.yugabyte.YugabytePlaform; import io.ebeaninternal.api.CoreLog; import io.ebeaninternal.api.DbOffline; import javax.persistence.PersistenceException; import javax.sql.DataSource; import java.sql.*; +import java.util.Locale; /** * Create a DatabasePlatform from the configuration. @@ -103,7 +108,19 @@ public class DatabasePlatformFactory { return new SqlAnywherePlatform(); } if (dbName.equals("db2")) { - return new DB2Platform(); + throw new IllegalArgumentException("Please choose the more specific db2luw/db2zos/db2fori platform. Refer to issue #2514 for details"); + } + if (dbName.equals("db2legacy")) { + return new DB2LegacyPlatform(); + } + if (dbName.equals("db2zos")) { + return new DB2ZosPlatform(); + } + if (dbName.equals("db2fori")) { + return new DB2ForIPlatform(); + } + if (dbName.equals("db2luw")) { + return new DB2LuwPlatform(); } if (dbName.equals("clickhouse")) { return new ClickHousePlatform(); @@ -149,7 +166,8 @@ public class DatabasePlatformFactory { } else if (dbProductName.contains("hsql database engine")) { return new HsqldbPlatform(); } else if (dbProductName.contains("postgres")) { - return readPostgres(connection, majorVersion); + String productVersion = metaData.getDatabaseProductVersion().toLowerCase(Locale.ENGLISH); + return readPostgres(connection, majorVersion, productVersion); } else if (dbProductName.contains("mariadb")) { return new MariaDbPlatform(); } else if (dbProductName.contains("mysql")) { @@ -159,7 +177,7 @@ public class DatabasePlatformFactory { } else if (dbProductName.contains("sqlite")) { return new SQLitePlatform(); } else if (dbProductName.contains("db2")) { - return new DB2Platform(); + throw new IllegalArgumentException("For DB2 please explicitly choose either db2legacy/db2luw/db2zos/db2fori platform. Refer to issue #2514 for details"); } else if (dbProductName.contains("sql anywhere")) { return new SqlAnywherePlatform(); } else if (dbProductName.contains("hdb")) { @@ -192,11 +210,14 @@ public class DatabasePlatformFactory { /** * Use a select version() query as it could be Postgres or CockroachDB. */ - private static DatabasePlatform readPostgres(Connection connection, int majorVersion) { + private static DatabasePlatform readPostgres(Connection connection, int majorVersion, String productVersion) { + if (productVersion.contains("-yb-")) { + return new YugabytePlaform(); + } try (PreparedStatement statement = connection.prepareStatement("select version() as \"version\"")) { try (ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { - String productVersion = resultSet.getString("version").toLowerCase(); + productVersion = resultSet.getString("version").toLowerCase(); if (productVersion.contains("cockroach")) { return new CockroachPlatform(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java index bc80213c0..da3eee778 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java @@ -15,6 +15,8 @@ public final class DbExpressionHandlerFactory { case H2: return new H2DbExpression(); case POSTGRES: + case YUGABYTE: + case COCKROACH: return new PostgresDbExpression(); case MARIADB: return new MariaDbExpression(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java index eaa1e1c95..07410134b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java @@ -1,7 +1,6 @@ package io.ebeaninternal.server.persist; import io.ebean.*; -import io.ebean.annotation.Platform; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollection.ModifyListenMode; import io.ebean.bean.EntityBean; @@ -42,15 +41,15 @@ public final class DefaultPersister implements Persister { * Actually does the persisting work. */ private final PersistExecute persistExecute; - private final SpiEbeanServer server; - private final BeanDescriptorManager beanDescriptorManager; + private final int maxInBinding; public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr) { this.server = server; this.beanDescriptorManager = descMgr; this.persistExecute = new DefaultPersistExecute(binder, server.config().getPersistBatchSize()); + this.maxInBinding = server.databasePlatform().getMaxInBinding(); } @Override @@ -645,18 +644,18 @@ public final class DefaultPersister implements Persister { DeleteMode deleteMode = (permanent || !descriptor.isSoftDelete()) ? DeleteMode.HARD : DeleteMode.SOFT; return delete(descriptor, null, idList, transaction, deleteMode); } - + /** * Delete by Id or a List of Id's. */ private int delete(BeanDescriptor descriptor, Object id, List idList, Transaction transaction, DeleteMode deleteMode) { int rows = 0; - if (server.databasePlatform().isPlatform(Platform.SQLSERVER)) { + if (maxInBinding > 0) { // SqlServer has a 2100 parameter limit, so delete max 2000 ids at once // this gives space up to 100 more query parameters. - while (idList != null && idList.size() > 2000) { - rows += deleteBatch(descriptor, id, idList.subList(0, 2000), transaction, deleteMode); - idList = idList.subList(2000, idList.size()); + while (idList != null && idList.size() > maxInBinding) { + rows += deleteBatch(descriptor, id, idList.subList(0, maxInBinding), transaction, deleteMode); + idList = idList.subList(maxInBinding, idList.size()); } } rows += deleteBatch(descriptor, id, idList, transaction, deleteMode); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 8b596e05e..edcfeb290 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -25,7 +25,10 @@ import io.ebean.config.PropertiesWrapper; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform; import io.ebean.config.dbplatform.cockroach.CockroachPlatform; -import io.ebean.config.dbplatform.db2.DB2Platform; +import io.ebean.config.dbplatform.db2.DB2ForIPlatform; +import io.ebean.config.dbplatform.db2.DB2LegacyPlatform; +import io.ebean.config.dbplatform.db2.DB2LuwPlatform; +import io.ebean.config.dbplatform.db2.DB2ZosPlatform; import io.ebean.config.dbplatform.h2.H2Platform; import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; @@ -918,7 +921,14 @@ public class DefaultDbMigration implements DbMigration { case SQLSERVER: throw new IllegalArgumentException("Please choose the more specific SQLSERVER16 or SQLSERVER17 platform. Refer to issue #1340 for details"); case DB2: - return new DB2Platform(); + logger.warn("Using DB2LegacyPlatform. It is recommended to migrate to db2luw/db2zos/db2fori. Refer to issue #2514 for details"); + return new DB2LegacyPlatform(); + case DB2LUW: + return new DB2LuwPlatform(); + case DB2ZOS: + return new DB2ZosPlatform(); + case DB2FORI: + return new DB2ForIPlatform(); case SQLITE: return new SQLitePlatform(); case HANA: diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java index 08d4e3b40..07e0be2bf 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java @@ -1,21 +1,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration; import io.ebean.config.dbplatform.DatabasePlatform; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.ClickHouseDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.CockroachDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.DB2Ddl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.H2Ddl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.HanaColumnStoreDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.HsqldbDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.MariaDbDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.MySqlDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.NuoDbDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.OracleDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.Postgres9Ddl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.PostgresDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.SQLiteDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.SqlServerDdl; +import io.ebeaninternal.dbmigration.ddlgeneration.platform.*; /** * Builds platform specific DDL handler. @@ -31,6 +17,9 @@ public class PlatformDdlBuilder { case H2: return new H2Ddl(platform); case DB2: + case DB2LUW: + case DB2FORI: + case DB2ZOS: return new DB2Ddl(platform); case MARIADB: return new MariaDbDdl(platform); @@ -50,6 +39,8 @@ public class PlatformDdlBuilder { return new Postgres9Ddl(platform); case POSTGRES: return new PostgresDdl(platform); + case YUGABYTE: + return new YugabyteDdl(platform); case COCKROACH: return new CockroachDdl(platform); case SQLSERVER16: diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java index 575deb5a1..58bed4505 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java @@ -174,16 +174,21 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { } protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + createHistoryTableAs(apply, table); + createHistoryTableWithPeriod(apply); + } + protected void createHistoryTableAs(DdlBuffer apply, MTable table) throws IOException { apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(table.getName()).append(historySuffix).append("(").newLine(); - - Collection cols = table.allColumns(); - for (MColumn column : cols) { + for (MColumn column : table.allColumns()) { if (!column.isDraftOnly()) { writeColumnDefinition(apply, column.getName(), column.getType()); apply.append(",").newLine(); } } + } + + protected void createHistoryTableWithPeriod(DdlBuffer apply) throws IOException { writeColumnDefinition(apply, sysPeriodStart, sysPeriodType); apply.append(",").newLine(); writeColumnDefinition(apply, sysPeriodEnd, sysPeriodType); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java index fe9b17f1b..b0257f96a 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java @@ -22,11 +22,8 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { */ @Override protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { - - String baseTable = table.getName(); - apply - .append("create table ").append(baseTable).append(historySuffix) - .append("(like ").append(baseTable).append(")").endOfStatement(); + apply.append("create table ").append(table.getName()).append(historySuffix) + .append("(like ").append(table.getName()).append(")").endOfStatement(); } /** @@ -57,7 +54,6 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { @Override protected void createTriggers(DdlWrite writer, MTable table) throws IOException { - String baseTableName = table.getName(); String procedureName = procedureName(baseTableName); String triggerName = triggerName(baseTableName); @@ -110,7 +106,6 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { @Override protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException { - String procedureName = procedureName(table.getName()); String historyTable = historyTableName(table.getName()); @@ -120,16 +115,13 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { @Override protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException { - String procedureName = procedureName(update.getBaseTable()); - recreateHistoryView(update); createOrReplaceFunction(update.historyTriggerBuffer(), procedureName, update.getHistoryTable(), update.getColumns()); } @Override protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List columns) throws IOException { - buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(","); appendColumnNames(buffer, columns, ""); buffer.append(") values (tstzrange(lowerTs,upperTs), "); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java new file mode 100644 index 000000000..a78e5b339 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java @@ -0,0 +1,11 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.config.dbplatform.DatabasePlatform; + +public class YugabyteDdl extends PostgresDdl { + + public YugabyteDdl(DatabasePlatform platform) { + super(platform); + this.historyDdl = new YugabyteHistoryDdl(); + } +} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java new file mode 100644 index 000000000..3ba023881 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java @@ -0,0 +1,16 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.model.MTable; + +import java.io.IOException; + +public class YugabyteHistoryDdl extends PostgresHistoryDdl { + + @Override + protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + createHistoryTableAs(apply, table); + writeColumnDefinition(apply, sysPeriod, "tstzrange"); + apply.newLine().append(")").endOfStatement(); + } +} diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index c1ae47723..6b16324cd 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -16,7 +16,7 @@ redis.clients jedis - 3.8.0 + 4.1.0 diff --git a/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java b/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java index 39dfec865..a9ce387f1 100644 --- a/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java +++ b/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java @@ -14,7 +14,9 @@ import io.ebean.redis.encode.EncodePrefixKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; +import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SetParams; +import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.SafeEncoder; import java.util.*; diff --git a/ebean-redis/src/test/java/org/integration/ClusterTest.java b/ebean-redis/src/test/java/org/integration/ClusterTest.java index 6a8dc32ba..64cca33b9 100644 --- a/ebean-redis/src/test/java/org/integration/ClusterTest.java +++ b/ebean-redis/src/test/java/org/integration/ClusterTest.java @@ -114,6 +114,6 @@ public class ClusterTest { } private void allowAsyncMessaging() throws InterruptedException { - Thread.sleep(50); + Thread.sleep(100); } } diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 9a743b7e5..7f00c2e7f 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -56,7 +56,7 @@ io.ebean ebean-test-docker - 4.4 + 4.5 diff --git a/ebean-test/src/test/java/main/StartYugabyte.java b/ebean-test/src/test/java/main/StartYugabyte.java new file mode 100644 index 000000000..b45f2b7f2 --- /dev/null +++ b/ebean-test/src/test/java/main/StartYugabyte.java @@ -0,0 +1,29 @@ +package main; + +import io.ebean.docker.commands.YugabyteConfig; +import io.ebean.docker.commands.YugabyteContainer; + +public class StartYugabyte { + + public static void main(String[] args) { + + // Check add extensions ? + YugabyteConfig config = new YugabyteConfig("2.11.2.0-b89"); + config.setDbName("unit"); + config.setUser("unit"); + + YugabyteContainer container = new YugabyteContainer(config); + container.start(); + +// Run container ut_yugabyte with host:localhost port:6433 db:unit user:unit/test shutdown:None +// docker run -d --name ut_yugabyte -p 6433:5433 -p 7000:7000 -p 9000:9000 -p 9042:9042 yugabytedb/yugabyte:2.11.2.0-b89 bin/yugabyted start --daemon=false +// ... +// Commands - sqlRun: drop database if exists unit +// Commands - sqlRun: drop role if exists unit +// Commands - sqlRun: select 1 from pg_database where datname = 'unit' +// Commands - sqlRun: select rolname from pg_roles where rolname = 'unit' +// Commands - sqlRun: create role unit password 'test' login createrole +// Commands - sqlRun: create database unit with owner unit + + } +} diff --git a/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java b/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java index 967efb9ba..9460b1bf0 100644 --- a/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java +++ b/ebean-test/src/test/java/org/tests/query/TestQueryFilterMany.java @@ -282,7 +282,7 @@ public class TestQueryFilterMany extends BaseTestCase { List sql = LoggedSql.stop(); - assertThat(sql).hasSize(2); + assertThat(sql.size()).isGreaterThan(1); assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id where t1.first_name is not null order by t0.id; --bind()"); platformAssertIn(sql.get(1), " from contact_note t0 where (t0.contact_id)"); assertSql(sql.get(1)).contains(" and lower(t0.title) like"); diff --git a/ebean-test/src/test/resources/ebean.properties b/ebean-test/src/test/resources/ebean.properties index d7e1debae..2b8bdfa45 100644 --- a/ebean-test/src/test/resources/ebean.properties +++ b/ebean-test/src/test/resources/ebean.properties @@ -23,6 +23,7 @@ ebean.ddl.run=true ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp} ebean.packages=org.tests,org.etest datasource.default=h2 +#datasource.default=yugabyte #datasource.h2.capturestacktrace=true #ebean.dumpMetricsOnShutdown=true @@ -144,6 +145,10 @@ datasource.cockroach.password= datasource.cockroach.url=jdbc:postgresql://localhost:26257/unit?sslmode=disable datasource.cockroach.driver=org.postgresql.Driver +datasource.yugabyte.username=unit +datasource.yugabyte.password=test +datasource.yugabyte.url=jdbc:postgresql://localhost:6433/unit?sslmode=disable + datasource.oracle.username=test_ebean datasource.oracle.password=test datasource.oracle.url=jdbc:oracle:thin:@localhost:1521:XE diff --git a/ebean-test/testconfig/ebean-db2.properties b/ebean-test/testconfig/ebean-db2.properties index 2aa2944dd..40e375ef5 100644 --- a/ebean-test/testconfig/ebean-db2.properties +++ b/ebean-test/testconfig/ebean-db2.properties @@ -2,3 +2,4 @@ ebean.test.platform=db2 ebean.test.dbName=unit ebean.test.dbPassword=unit datasource.default=db2-11 +ebean.db2-11.databasePlatformName=db2luw diff --git a/ebean-test/testconfig/ebean-oracle.properties b/ebean-test/testconfig/ebean-oracle.properties index e9d4bf55b..32b3af249 100644 --- a/ebean-test/testconfig/ebean-oracle.properties +++ b/ebean-test/testconfig/ebean-oracle.properties @@ -1,3 +1,3 @@ ebean.test.platform=oracle -ebean.test.dbName=oracle -datasource.default=oracle-docker \ No newline at end of file +ebean.test.dbName=test_eb +datasource.default=oracle