From 907d36123dd93b88ec04aabb606d3e0617200305 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sat, 22 Feb 2020 09:28:34 +1300 Subject: [PATCH] OraclePlatform update (ANSI row limiting with OFFSET), Oracle11Platform for old behaviour (#1951) * #1947 - Oracle change to use concat operator rather than function * #1947 - setMaxRows(1).findIds() under Oracle returns wrong result * #1950 - Change OraclePlatform to use ANSI row limiting - OFFSET and NEXT ROWS, old platform as Oracle11Platform * Revert test default to H2 * #1947 - Oracle & Oracle11 platform --- pom.xml | 2 +- .../dbplatform/RowNumberSqlLimiter.java | 75 ----------------- .../config/dbplatform/RownumSqlLimiter.java | 81 ------------------- .../dbplatform/oracle/Oracle11Platform.java | 16 ++++ .../oracle/OracleAnsiSqlRowsLimiter.java | 34 ++++++++ .../dbplatform/oracle/OraclePlatform.java | 4 +- .../oracle/OracleRownumSqlLimiter.java | 54 +++++++++++++ .../dbmigration/DefaultDbMigration.java | 3 + .../server/core/DatabasePlatformFactory.java | 6 +- .../server/core/InternalConfiguration.java | 2 +- .../server/core/PlatformDdlBuilder.java | 1 + .../expression/platform/BaseDbExpression.java | 13 +++ .../platform/DbExpressionHandlerFactory.java | 1 + .../expression/platform/H2DbExpression.java | 1 - .../platform/OracleDbExpression.java | 5 ++ .../platform/PostgresDbExpression.java | 16 ++-- src/test/java/io/ebean/BaseTestCase.java | 6 +- .../java/io/ebean/EbeanServer_eqlTest.java | 18 ++--- .../platform/H2DbExpressionTest.java | 18 +++++ .../platform/OracleDbExpressionTest.java | 18 +++++ .../platform/PostgresDbExpressionTest.java | 17 ++++ src/test/java/main/StartOracle.java | 3 +- .../batchinsert/TestBatchInsertFlush.java | 2 +- .../org/tests/lazyforeignkeys/MainEntity.java | 2 +- .../lazyforeignkeys/TestLazyForeignKeys.java | 14 ++-- .../cache/TestCacheViaComplexNaturalKey3.java | 4 +- .../model/history/TestHistoryOneToMany.java | 3 + .../tests/model/nofk/EUserNoFkSoftDel.java | 2 +- .../java/org/tests/model/nofk/TestNoFk.java | 5 +- .../onetoone/TestOneToOneOrphanRemove.java | 2 +- .../org/tests/query/TestQueryOrderById.java | 4 +- .../query/finder/TestCustomerFinder.java | 6 +- .../query/other/TestQuerySingleAttribute.java | 24 +++--- .../org/tests/rawsql/TestRawSqlOrmQuery.java | 6 +- .../transaction/TestNestedSubTransaction.java | 8 +- 35 files changed, 252 insertions(+), 224 deletions(-) delete mode 100644 src/main/java/io/ebean/config/dbplatform/RowNumberSqlLimiter.java delete mode 100644 src/main/java/io/ebean/config/dbplatform/RownumSqlLimiter.java create mode 100644 src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java create mode 100644 src/main/java/io/ebean/config/dbplatform/oracle/OracleAnsiSqlRowsLimiter.java create mode 100644 src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumSqlLimiter.java create mode 100644 src/test/java/io/ebeaninternal/server/expression/platform/H2DbExpressionTest.java create mode 100644 src/test/java/io/ebeaninternal/server/expression/platform/OracleDbExpressionTest.java create mode 100644 src/test/java/io/ebeaninternal/server/expression/platform/PostgresDbExpressionTest.java diff --git a/pom.xml b/pom.xml index c47919e2e..f56f99055 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ io.ebean ebean-annotation - 6.5 + 6.7 diff --git a/src/main/java/io/ebean/config/dbplatform/RowNumberSqlLimiter.java b/src/main/java/io/ebean/config/dbplatform/RowNumberSqlLimiter.java deleted file mode 100644 index 9f6b4ad19..000000000 --- a/src/main/java/io/ebean/config/dbplatform/RowNumberSqlLimiter.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.ebean.config.dbplatform; - -/** - * Adds the ROW_NUMBER() OVER function to a query. - */ -public class RowNumberSqlLimiter implements SqlLimiter { - - /** - * ROW_NUMBER() OVER (ORDER BY - */ - private static final String ROW_NUMBER_OVER = "row_number() over (order by "; - - /** - * ) as rn, - */ - private static final String ROW_NUMBER_AS = ") as rn, "; - - private final String rowNumberWindowAlias; - - /** - * Specify the name of the rowNumberWindowAlias. - */ - public RowNumberSqlLimiter(String rowNumberWindowAlias) { - this.rowNumberWindowAlias = rowNumberWindowAlias; - } - - public RowNumberSqlLimiter() { - this("as limitresult"); - } - - @Override - public SqlLimitResponse limit(SqlLimitRequest request) { - - String dbSql = request.getDbSql(); - - StringBuilder sb = new StringBuilder(60 + dbSql.length()); - - int firstRow = request.getFirstRow(); - - int lastRow = request.getMaxRows(); - if (lastRow > 0) { - lastRow += firstRow; - } - - sb.append("select * from ( "); - - sb.append("select "); - if (request.isDistinct()) { - sb.append("distinct "); - } - - sb.append(ROW_NUMBER_OVER); - sb.append(request.getDbOrderBy()); - sb.append(ROW_NUMBER_AS); - - sb.append(dbSql); - - sb.append(" ) "); - sb.append(rowNumberWindowAlias); - sb.append(" where "); - if (firstRow > 0) { - sb.append(" rn > ").append(firstRow); - if (lastRow > 0) { - sb.append(" and "); - } - } - if (lastRow > 0) { - sb.append(" rn <= ").append(lastRow); - } - - String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery()); - - return new SqlLimitResponse(sql, true); - } -} diff --git a/src/main/java/io/ebean/config/dbplatform/RownumSqlLimiter.java b/src/main/java/io/ebean/config/dbplatform/RownumSqlLimiter.java deleted file mode 100644 index ef7656782..000000000 --- a/src/main/java/io/ebean/config/dbplatform/RownumSqlLimiter.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.ebean.config.dbplatform; - -/** - * Add ROWNUM column etc around SQL query to limit results. - */ -public class RownumSqlLimiter implements SqlLimiter { - - private final String rnum; - - private final boolean useFirstRowsHint; - - /** - * Create with default inner rownum column alias and used FIRST_ROWS hint. - */ - public RownumSqlLimiter() { - this("rn_", true); - } - - /** - * Specify the inner rownum column alias and whether to include the FIRST_ROWS - * hint. - */ - public RownumSqlLimiter(String rnum, boolean useFirstRowsHint) { - this.rnum = rnum; - this.useFirstRowsHint = useFirstRowsHint; - } - - @Override - public SqlLimitResponse limit(SqlLimitRequest request) { - - // select * - // from ( select /*+ FIRST_ROWS(n) */ ROWNUM rnum, a.* - // from ( your_query_goes_here, - // with order by ) a - // where ROWNUM <= - // :MAX_ROW_TO_FETCH ) - // where rnum >= :MIN_ROW_TO_FETCH; - - String dbSql = request.getDbSql(); - - StringBuilder sb = new StringBuilder(60 + dbSql.length()); - - int firstRow = request.getFirstRow(); - - int lastRow = request.getMaxRows(); - if (lastRow > 0) { - lastRow += firstRow; - } - - sb.append("select * from ( "); - - sb.append("select "); - if (useFirstRowsHint && request.getMaxRows() > 0) { - sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ "); - } - - sb.append("rownum ").append(rnum).append(", a.* "); - sb.append(" from ("); - - sb.append(" select "); - if (request.isDistinct()) { - sb.append("distinct "); - } - sb.append(dbSql); - - sb.append(NEW_LINE).append(" ) a "); - if (lastRow > 0) { - sb.append(" where rownum <= ").append(lastRow); - } - sb.append(" ) "); - if (firstRow > 0) { - sb.append(" where "); - sb.append(rnum).append(" > ").append(firstRow); - } - - String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery()); - - return new SqlLimitResponse(sql, true); - } - -} diff --git a/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java b/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java new file mode 100644 index 000000000..aa3ed1b2e --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java @@ -0,0 +1,16 @@ +package io.ebean.config.dbplatform.oracle; + +import io.ebean.annotation.Platform; + +/** + * Oracle 11 platform using rownum sql limiting. + */ +public class Oracle11Platform extends OraclePlatform { + + public Oracle11Platform() { + super(); + this.platform = Platform.ORACLE11; + this.columnAliasPrefix = "c"; + this.sqlLimiter = new OracleRownumSqlLimiter(); + } +} diff --git a/src/main/java/io/ebean/config/dbplatform/oracle/OracleAnsiSqlRowsLimiter.java b/src/main/java/io/ebean/config/dbplatform/oracle/OracleAnsiSqlRowsLimiter.java new file mode 100644 index 000000000..0a23ce87f --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/oracle/OracleAnsiSqlRowsLimiter.java @@ -0,0 +1,34 @@ +package io.ebean.config.dbplatform.oracle; + +import io.ebean.config.dbplatform.SqlLimitRequest; +import io.ebean.config.dbplatform.SqlLimitResponse; +import io.ebean.config.dbplatform.SqlLimiter; + +/** + * Use ANSI offset rows syntax. + */ +class OracleAnsiSqlRowsLimiter implements SqlLimiter { + + @Override + public SqlLimitResponse limit(SqlLimitRequest request) { + + String dbSql = request.getDbSql(); + StringBuilder sb = new StringBuilder(50 + dbSql.length()); + int firstRow = request.getFirstRow(); + int maxRows = request.getMaxRows(); + + sb.append("select "); + if (request.isDistinct()) { + sb.append("distinct "); + } + sb.append(dbSql); + if (firstRow > 0) { + sb.append(" offset ").append(firstRow).append(" rows"); + } + if (maxRows > 0) { + sb.append(" fetch next ").append(maxRows).append(" rows only"); + } + return new SqlLimitResponse(sb.toString(), false); + } + +} diff --git a/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java b/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java index 1112688ce..ab39dbf0d 100644 --- a/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java @@ -9,7 +9,6 @@ import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.config.dbplatform.DbType; import io.ebean.config.dbplatform.IdType; import io.ebean.config.dbplatform.PlatformIdGenerator; -import io.ebean.config.dbplatform.RownumSqlLimiter; import io.ebean.config.dbplatform.SqlErrorCodes; import javax.sql.DataSource; @@ -23,11 +22,12 @@ public class OraclePlatform extends DatabasePlatform { public OraclePlatform() { super(); this.platform = Platform.ORACLE; + this.columnAliasPrefix = null; this.supportsDeleteTableAlias = true; this.maxTableNameLength = 30; this.maxConstraintNameLength = 30; this.dbEncrypt = new OracleDbEncrypt(); - this.sqlLimiter = new RownumSqlLimiter(); + this.sqlLimiter = new OracleAnsiSqlRowsLimiter(); this.basicSqlLimiter = new BasicSqlAnsiLimiter(); this.historySupport = new OracleDbHistorySupport(); this.truncateTable = "truncate table %s cascade"; diff --git a/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumSqlLimiter.java b/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumSqlLimiter.java new file mode 100644 index 000000000..7f798f897 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumSqlLimiter.java @@ -0,0 +1,54 @@ +package io.ebean.config.dbplatform.oracle; + +import io.ebean.config.dbplatform.SqlLimitRequest; +import io.ebean.config.dbplatform.SqlLimitResponse; +import io.ebean.config.dbplatform.SqlLimiter; + +/** + * Add ROWNUM column etc around SQL query to limit results. + */ +class OracleRownumSqlLimiter implements SqlLimiter { + + @Override + public SqlLimitResponse limit(SqlLimitRequest request) { + + // select * + // from ( select /*+ FIRST_ROWS(n) */ a.*, ROWNUM rnum + // from ( your_query_goes_here, + // with order by ) a + // where ROWNUM <= + // :MAX_ROW_TO_FETCH ) + // where rnum >= :MIN_ROW_TO_FETCH; + + String dbSql = request.getDbSql(); + + StringBuilder sb = new StringBuilder(60 + dbSql.length()); + int firstRow = request.getFirstRow(); + int lastRow = request.getMaxRows(); + if (lastRow > 0) { + lastRow += firstRow; + } + + sb.append("select * from (select "); + if (request.getMaxRows() > 0) { + sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ "); + } + + sb.append("a.*, rownum rn_ from (select "); + if (request.isDistinct()) { + sb.append("distinct "); + } + sb.append(dbSql).append(") a "); + if (lastRow > 0) { + sb.append(" where rownum <= ").append(lastRow); + } + sb.append(") "); + if (firstRow > 0) { + sb.append(" where rn_ > ").append(firstRow); + } + + String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery()); + return new SqlLimitResponse(sql, false); + } + +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index dec46fef6..43def5067 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -18,6 +18,7 @@ import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; import io.ebean.config.dbplatform.mysql.MySql55Platform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.nuodb.NuoDbPlatform; +import io.ebean.config.dbplatform.oracle.Oracle11Platform; import io.ebean.config.dbplatform.oracle.OraclePlatform; import io.ebean.config.dbplatform.postgres.Postgres9Platform; import io.ebean.config.dbplatform.postgres.PostgresPlatform; @@ -826,6 +827,8 @@ public class DefaultDbMigration implements DbMigration { return new MySqlPlatform(); case ORACLE: return new OraclePlatform(); + case ORACLE11: + return new Oracle11Platform(); case SQLANYWHERE: return new SqlAnywherePlatform(); case SQLSERVER16: diff --git a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java index 9c0e4b439..5c6e2f3b0 100644 --- a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -11,6 +11,7 @@ import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; import io.ebean.config.dbplatform.mysql.MySql55Platform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.nuodb.NuoDbPlatform; +import io.ebean.config.dbplatform.oracle.Oracle11Platform; import io.ebean.config.dbplatform.oracle.OraclePlatform; import io.ebean.config.dbplatform.postgres.Postgres8Platform; import io.ebean.config.dbplatform.postgres.Postgres9Platform; @@ -93,7 +94,10 @@ public class DatabasePlatformFactory { if (dbName.equals("postgres8") || dbName.equals("postgres83")) { return new Postgres8Platform(); } - if (dbName.equals("oracle") || dbName.equals("oracle10") || dbName.equals("oracle9")) { + if (dbName.equals("oracle11") || dbName.equals("oracle10") || dbName.equals("oracle9")) { + return new Oracle11Platform(); + } + if (dbName.equals("oracle")) { return new OraclePlatform(); } if (dbName.equals("sqlserver16")) { diff --git a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java index 811a36acf..24b53f4e7 100644 --- a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java @@ -533,7 +533,7 @@ public class InternalConfiguration { } return new NoDataTimeZone(); } - if (getPlatform() == Platform.ORACLE) { + if (getPlatform().base() == Platform.ORACLE) { return new CloneDataTimeZone(tz); } else { return new SimpleDataTimeZone(tz); diff --git a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java index 543d9bf02..802e63662 100644 --- a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java +++ b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java @@ -39,6 +39,7 @@ public class PlatformDdlBuilder { case NUODB: return new NuoDbDdl(platform); case ORACLE: + case ORACLE11: return new Oracle10Ddl(platform); case SQLITE: return new SQLiteDdl(platform); diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/BaseDbExpression.java b/src/main/java/io/ebeaninternal/server/expression/platform/BaseDbExpression.java index 40ae04a1d..2c85e7963 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/BaseDbExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/BaseDbExpression.java @@ -56,4 +56,17 @@ abstract class BaseDbExpression implements DbExpressionHandler { sb.append(')'); return sb.toString(); } + + /** + * Concat using operator for Oracle and Postgres. + */ + String concatOperator(String property0, String separator, String property1, String suffix) { + StringBuilder sb = new StringBuilder(40); + sb.append("(").append(property0).append("||'").append(separator).append("'||").append(property1); + if (suffix != null && !suffix.isEmpty()) { + sb.append("||'").append(suffix).append('\''); + } + sb.append(')'); + return sb.toString(); + } } diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java b/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java index 0d7872416..5f1de2883 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java @@ -23,6 +23,7 @@ public class DbExpressionHandlerFactory { case MYSQL: return new MySqlDbExpression(); case ORACLE: + case ORACLE11: return new OracleDbExpression(); case SQLSERVER16: case SQLSERVER17: diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/H2DbExpression.java b/src/main/java/io/ebeaninternal/server/expression/platform/H2DbExpression.java index fb65c7c03..0c7e43b65 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/H2DbExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/H2DbExpression.java @@ -10,7 +10,6 @@ class H2DbExpression extends BasicDbExpression { @Override public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) { - bitwiseFunction(request, propName, operator, compare); } } diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/OracleDbExpression.java b/src/main/java/io/ebeaninternal/server/expression/platform/OracleDbExpression.java index 168f01aaf..3ec4b4071 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/OracleDbExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/OracleDbExpression.java @@ -9,6 +9,11 @@ import io.ebeaninternal.server.expression.Op; */ public class OracleDbExpression extends BaseDbExpression { + @Override + public String concat(String property0, String separator, String property1, String suffix) { + return concatOperator(property0, separator, property1, suffix); + } + @Override public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) { diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/PostgresDbExpression.java b/src/main/java/io/ebeaninternal/server/expression/platform/PostgresDbExpression.java index adac971b5..54270ee5c 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/PostgresDbExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/PostgresDbExpression.java @@ -8,6 +8,11 @@ import io.ebeaninternal.server.expression.Op; */ public class PostgresDbExpression extends BaseDbExpression { + @Override + public String concat(String property0, String separator, String property1, String suffix) { + return concatOperator(property0, separator, property1, suffix); + } + @Override public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) { @@ -62,15 +67,4 @@ public class PostgresDbExpression extends BaseDbExpression { } } - @Override - public String concat(String property0, String separator, String property1, String suffix) { - StringBuilder sb = new StringBuilder(); - sb.append("(").append(property0).append("||'").append(separator).append("'||").append(property1); - - if (suffix != null && !suffix.isEmpty()) { - sb.append("||'").append(suffix).append('\''); - } - sb.append(')'); - return sb.toString(); - } } diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index 6a1077bfc..6c1326d77 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -175,7 +175,7 @@ public abstract class BaseTestCase { * so tests that do this need to be skipped for SQL Server. */ public boolean isSqlServer() { - return Platform.SQLSERVER == platform().base(); + return Platform.SQLSERVER == platform(); } public boolean isH2() { @@ -203,7 +203,7 @@ public abstract class BaseTestCase { } public boolean isMySql() { - return Platform.MYSQL == platform().base(); + return Platform.MYSQL == platform(); } public boolean isHana() { @@ -242,7 +242,7 @@ public abstract class BaseTestCase { } protected Platform platform() { - return spiEbeanServer().getDatabasePlatform().getPlatform(); + return spiEbeanServer().getDatabasePlatform().getPlatform().base(); } protected IdType idType() { diff --git a/src/test/java/io/ebean/EbeanServer_eqlTest.java b/src/test/java/io/ebean/EbeanServer_eqlTest.java index d0f673b79..1455c4770 100644 --- a/src/test/java/io/ebean/EbeanServer_eqlTest.java +++ b/src/test/java/io/ebean/EbeanServer_eqlTest.java @@ -27,7 +27,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { assertSql(query).startsWith("select top 100 "); assertSql(query).endsWith("order by t0.id"); } else if (isOracle()) { - assertSql(query).contains("where rownum <= 100"); + assertSql(query).contains(" fetch next 100 rows only"); } else { assertSql(query).endsWith("order by t0.id limit 100"); } @@ -45,7 +45,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { assertSql(query).startsWith("select top 10 "); assertSql(query).endsWith("order by t0.id"); } else if (isOracle()) { - assertSql(query).contains("where rownum <= 10"); + assertSql(query).contains(" fetch next 10 rows only"); } else { assertSql(query).endsWith("order by t0.id limit 10"); } @@ -62,8 +62,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { if (isSqlServer()) { assertSql(query).endsWith("order by t0.id offset 3 rows fetch next 10 rows only"); } else if (isOracle()) { - assertSql(query).contains("where rownum <= 13"); - assertSql(query).contains("where rn_ > 3"); + assertSql(query).contains("offset 3 rows fetch next 10 rows only"); } else { assertSql(query).endsWith("order by t0.id limit 10 offset 3"); } @@ -83,8 +82,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { if (isSqlServer()) { assertSql(query).endsWith("order by t0.name offset 3 rows fetch next 10 rows only"); } else if (isOracle()) { - assertSql(query).contains("where rownum <= 13"); - assertSql(query).contains("where rn_ > 3"); + assertSql(query).contains("offset 3 rows fetch next 10 rows only"); } else { assertSql(query).endsWith("order by t0.name limit 10 offset 3"); } @@ -110,8 +108,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { if (isSqlServer()) { assertSql(query).endsWith("order by t0.name, t0.id offset 3 rows fetch next 10 rows only"); } else if (isOracle()) { - assertSql(query).contains("where rownum <= 13"); - assertSql(query).contains("where rn_ > 3"); + assertSql(query).contains("offset 3 rows fetch next 10 rows only"); } else { assertSql(query).endsWith("order by t0.name, t0.id limit 10 offset 3"); } @@ -140,8 +137,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { if (isSqlServer()) { assertSql(query).endsWith("from o_customer t0 order by t0.id offset 3 rows fetch next 10 rows only"); } else if (isOracle()) { - assertSql(query).contains("where rownum <= 13"); - assertSql(query).contains("where rn_ > 3"); + assertSql(query).contains("offset 3 rows fetch next 10 rows only"); } else { assertSql(query).endsWith("from o_customer t0 limit 10 offset 3"); } @@ -159,7 +155,7 @@ public class EbeanServer_eqlTest extends BaseTestCase { if (isSqlServer()) { assertSql(query).startsWith("select top 10 "); } else if (isOracle()) { - assertSql(query).contains(" a where rownum <= 10"); + assertSql(query).contains("fetch next 10 rows only"); } else { assertSql(query).endsWith("limit 10"); } diff --git a/src/test/java/io/ebeaninternal/server/expression/platform/H2DbExpressionTest.java b/src/test/java/io/ebeaninternal/server/expression/platform/H2DbExpressionTest.java new file mode 100644 index 000000000..9cd0f687c --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/expression/platform/H2DbExpressionTest.java @@ -0,0 +1,18 @@ +package io.ebeaninternal.server.expression.platform; + + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class H2DbExpressionTest { + + H2DbExpression expression = new H2DbExpression(); + + @Test + public void concat() { + assertThat(expression.concat("p0", ",", "q1", "suffix")).isEqualTo("concat(p0,',',q1,'suffix')"); + assertThat(expression.concat("p0", ",", "q1", null)).isEqualTo("concat(p0,',',q1)"); + assertThat(expression.concat("p0", ",", "q1", "")).isEqualTo("concat(p0,',',q1)"); + } +} diff --git a/src/test/java/io/ebeaninternal/server/expression/platform/OracleDbExpressionTest.java b/src/test/java/io/ebeaninternal/server/expression/platform/OracleDbExpressionTest.java new file mode 100644 index 000000000..6608927c2 --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/expression/platform/OracleDbExpressionTest.java @@ -0,0 +1,18 @@ +package io.ebeaninternal.server.expression.platform; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class OracleDbExpressionTest { + + OracleDbExpression expression = new OracleDbExpression(); + + @Test + public void concat() { + assertThat(expression.concat("p0", ",", "q1", "suffix")).isEqualTo("(p0||','||q1||'suffix')"); + assertThat(expression.concat("p0", ",", "q1", null)).isEqualTo("(p0||','||q1)"); + assertThat(expression.concat("p0", ",", "q1", "")).isEqualTo("(p0||','||q1)"); + } + +} diff --git a/src/test/java/io/ebeaninternal/server/expression/platform/PostgresDbExpressionTest.java b/src/test/java/io/ebeaninternal/server/expression/platform/PostgresDbExpressionTest.java new file mode 100644 index 000000000..e9e4ae2d4 --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/expression/platform/PostgresDbExpressionTest.java @@ -0,0 +1,17 @@ +package io.ebeaninternal.server.expression.platform; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PostgresDbExpressionTest { + + PostgresDbExpression expression = new PostgresDbExpression(); + + @Test + public void concat() { + assertThat(expression.concat("p0", ",", "p1", "suffix")).isEqualTo("(p0||','||p1||'suffix')"); + assertThat(expression.concat("p0", ",", "p1", null)).isEqualTo("(p0||','||p1)"); + assertThat(expression.concat("p0", ",", "p1", "")).isEqualTo("(p0||','||p1)"); + } +} diff --git a/src/test/java/main/StartOracle.java b/src/test/java/main/StartOracle.java index ab08b1b18..df489be07 100644 --- a/src/test/java/main/StartOracle.java +++ b/src/test/java/main/StartOracle.java @@ -8,10 +8,9 @@ public class StartOracle { public static void main(String[] args) { OracleConfig config = new OracleConfig(); -// config.setImage("quillbuilduser/oracle-18-xe:latest"); config.setUser("test_ebean"); OracleContainer container = new OracleContainer(config); - container.start();//WithDropCreate(); + container.startWithDropCreate(); } } diff --git a/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java b/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java index 8f5e667f4..dc04e0b79 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java +++ b/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java @@ -205,7 +205,7 @@ public class TestBatchInsertFlush extends BaseTestCase { } @Test - @IgnorePlatform(Platform.SQLSERVER) + @IgnorePlatform({Platform.SQLSERVER, Platform.ORACLE}) public void noflushWhenIdIsLoaded() { EbeanServer server = Ebean.getDefaultServer(); diff --git a/src/test/java/org/tests/lazyforeignkeys/MainEntity.java b/src/test/java/org/tests/lazyforeignkeys/MainEntity.java index 3fa760583..bacfbdf9f 100644 --- a/src/test/java/org/tests/lazyforeignkeys/MainEntity.java +++ b/src/test/java/org/tests/lazyforeignkeys/MainEntity.java @@ -21,7 +21,7 @@ public class MainEntity { @SoftDelete @Formula(select = "${ta}.id is null") - @Formula(select = "CASE WHEN ${ta}.id is null THEN 1 ELSE 0 END", platforms = Platform.SQLSERVER17) + @Formula(select = "CASE WHEN ${ta}.id is null THEN 1 ELSE 0 END", platforms = {Platform.SQLSERVER17, Platform.ORACLE}) // evaluates to true in a left join if bean has been deleted. boolean deleted; diff --git a/src/test/java/org/tests/lazyforeignkeys/TestLazyForeignKeys.java b/src/test/java/org/tests/lazyforeignkeys/TestLazyForeignKeys.java index 207d454dd..679556fe2 100644 --- a/src/test/java/org/tests/lazyforeignkeys/TestLazyForeignKeys.java +++ b/src/test/java/org/tests/lazyforeignkeys/TestLazyForeignKeys.java @@ -57,14 +57,14 @@ public class TestLazyForeignKeys extends BaseTestCase { assertFalse(rel1.getEntity1().isDeleted()); assertTrue(rel1.getEntity2().isDeleted()); - List loggedSql = LoggedSqlCollector.stop(); - assertThat(loggedSql).hasSize(3); - assertThat(loggedSql.get(0)).contains("select t0.id, t0.attr1, t0.id1, t0.id2 from main_entity_relation"); - if (isSqlServer()) { - assertThat(loggedSql.get(1)).contains("select t0.id, t0.attr1, t0.attr2, CASE WHEN t0.id is null THEN 1 ELSE 0 END from main_entity t0"); + List sql = LoggedSqlCollector.stop(); + assertThat(sql).hasSize(3); + assertSql(sql.get(0)).contains("select t0.id, t0.attr1, t0.id1, t0.id2 from main_entity_relation"); + if (isSqlServer() || isOracle()) { + assertSql(sql.get(1)).contains("select t0.id, t0.attr1, t0.attr2, CASE WHEN t0.id is null THEN 1 ELSE 0 END from main_entity t0"); } else { - assertThat(loggedSql.get(1)).contains("select t0.id, t0.attr1, t0.attr2, t0.id is null from main_entity t0"); - assertThat(loggedSql.get(2)).contains("select t0.id, t0.attr1, t0.attr2, t0.id is null from main_entity t0"); + assertSql(sql.get(1)).contains("select t0.id, t0.attr1, t0.attr2, t0.id is null from main_entity t0"); + assertSql(sql.get(2)).contains("select t0.id, t0.attr1, t0.attr2, t0.id is null from main_entity t0"); } } diff --git a/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java b/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java index bda886770..463f73d88 100644 --- a/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java +++ b/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java @@ -370,7 +370,7 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase { if (isH2()) { assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku,'-',t0.code) in (?,?) order by t0.sku desc; --bind(def,Array[2]={2-1000,3-1000})"); - } else if (isPostgres()) { + } else if (isPostgres() || isOracle()) { assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||'-'||t0.code)"); } else if (isHana()) { assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku, '-'||t0.code)"); @@ -413,7 +413,7 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase { if (isH2()) { assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku,':',t0.code,'-foo') in (?,?) order by t0.sku desc; --bind(def,Array[2]={2:1000-foo,3:1000-foo})"); - } else if (isPostgres()){ + } else if (isPostgres() || isOracle()){ assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||':'||t0.code||'-foo')"); } else if (isHana()){ assertSql(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku, ':'||t0.code||'-foo')"); diff --git a/src/test/java/org/tests/model/history/TestHistoryOneToMany.java b/src/test/java/org/tests/model/history/TestHistoryOneToMany.java index 50f0017f7..ea6d6f20f 100644 --- a/src/test/java/org/tests/model/history/TestHistoryOneToMany.java +++ b/src/test/java/org/tests/model/history/TestHistoryOneToMany.java @@ -2,6 +2,8 @@ package org.tests.model.history; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -12,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class TestHistoryOneToMany extends BaseTestCase { + @IgnorePlatform(Platform.ORACLE) @Test public void test() { diff --git a/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java b/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java index 214dd289c..f3eea2bcd 100644 --- a/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java +++ b/src/test/java/org/tests/model/nofk/EUserNoFkSoftDel.java @@ -19,7 +19,7 @@ public class EUserNoFkSoftDel { @SoftDelete @Formula(select = "${ta}.user_id is null") - @Formula(select = "CASE WHEN ${ta}.user_id is null THEN 1 ELSE 0 END", platforms = Platform.SQLSERVER17) + @Formula(select = "CASE WHEN ${ta}.user_id is null THEN 1 ELSE 0 END", platforms = {Platform.SQLSERVER17, Platform.ORACLE}) // evaluates to true in a left join if bean has been deleted. boolean deleted; diff --git a/src/test/java/org/tests/model/nofk/TestNoFk.java b/src/test/java/org/tests/model/nofk/TestNoFk.java index a2315eb05..e9ed12dbe 100644 --- a/src/test/java/org/tests/model/nofk/TestNoFk.java +++ b/src/test/java/org/tests/model/nofk/TestNoFk.java @@ -27,8 +27,9 @@ public class TestNoFk extends BaseTestCase { Ebean.find(EUserNoFk.class).delete(); Ebean.find(EUserNoFkSoftDel.class).delete(); - Ebean.createSqlUpdate("delete from efile_no_fk_euser_no_fk").execute(); - Ebean.createSqlUpdate("delete from efile_no_fk_euser_no_fk_soft_del").execute(); + DB.sqlUpdate("delete from efile_no_fk_euser_no_fk").execute(); + String tabName = isOracle() ? "efile_no_fk_euser_no_fk_soft_d" : "efile_no_fk_euser_no_fk_soft_del"; + DB.sqlUpdate("delete from " + tabName).execute(); // There are two user accounts persisted in our database EUserNoFk root = new EUserNoFk(); diff --git a/src/test/java/org/tests/model/onetoone/TestOneToOneOrphanRemove.java b/src/test/java/org/tests/model/onetoone/TestOneToOneOrphanRemove.java index 43c642add..7c0828c25 100644 --- a/src/test/java/org/tests/model/onetoone/TestOneToOneOrphanRemove.java +++ b/src/test/java/org/tests/model/onetoone/TestOneToOneOrphanRemove.java @@ -39,7 +39,7 @@ public class TestOneToOneOrphanRemove extends BaseTestCase { assertSqlBind(sql.get(1)); assertSql(sql.get(2)).contains("update oto_cust set version=? where cid=? and version=?"); assertThat(sql.get(3)).contains("insert into oto_cust_address "); - assertThat(sql.get(4)).contains("-- bind(other1"); + assertThat(sql.get(4)).contains("-- bind("); jack.setAddress(null); Ebean.save(jack); diff --git a/src/test/java/org/tests/query/TestQueryOrderById.java b/src/test/java/org/tests/query/TestQueryOrderById.java index 05e2a8318..3c536a6f8 100644 --- a/src/test/java/org/tests/query/TestQueryOrderById.java +++ b/src/test/java/org/tests/query/TestQueryOrderById.java @@ -22,7 +22,7 @@ public class TestQueryOrderById extends BaseTestCase { query.findList(); if (isSqlServer()) { assertSql(query).isEqualTo("select t0.id, t0.name from o_customer t0 order by t0.id offset 1 rows fetch next 5 rows only"); - } else { + } else if (!isOracle()) { assertSql(query).isEqualTo("select t0.id, t0.name from o_customer t0 order by t0.id limit 5 offset 1"); } } @@ -39,7 +39,7 @@ public class TestQueryOrderById extends BaseTestCase { query.findList(); if (isSqlServer()) { assertSql(query).isEqualTo("select t0.id, t0.name from o_customer t0 order by t0.id offset 1 rows fetch next 5 rows only"); - } else { + } else if (!isOracle()) { assertSql(query).isEqualTo("select t0.id, t0.name from o_customer t0 order by t0.id limit 5 offset 1"); } } diff --git a/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/src/test/java/org/tests/query/finder/TestCustomerFinder.java index 1e9e0cdbc..c1d323164 100644 --- a/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -229,8 +229,10 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(metricsJson).contains("\"name\":\"txn.main\""); assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\""); assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\""); - assertThat(metricsJson).contains("\"hash\":\"cc20eb930403cfd418db2d0475c6e26a\""); - assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); + if (!isOracle()) { + assertThat(metricsJson).contains("\"hash\":\"cc20eb930403cfd418db2d0475c6e26a\""); + assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); + } } @Test diff --git a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java index 10f2ad09d..0fbd0bfe5 100644 --- a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java +++ b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java @@ -191,8 +191,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { if (isSqlServer()) { assertThat(sqlOf(query)).contains("select distinct top 100 t0.id from o_customer t0"); } else if (isOracle()) { - assertThat(sqlOf(query)).contains("from ( select distinct t0.id from o_customer t0"); - assertThat(sqlOf(query)).contains("where rownum <= 100"); + assertThat(sqlOf(query)).contains("select distinct t0.id from o_customer t0 fetch next 100 rows only"); } else { assertThat(sqlOf(query)).contains("select distinct t0.id from o_customer t0 limit 100"); } @@ -271,7 +270,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { if (isSqlServer()) { assertThat(sqlOf(query)).contains("select top 100 t0.id from o_customer t0"); } else if (isOracle()) { - assertThat(sqlOf(query)).contains("where rownum <= 100"); + assertThat(sqlOf(query)).contains("fetch next 100 rows only"); } else { assertThat(sqlOf(query)).contains("select t0.id from o_customer t0 limit 100"); } @@ -583,16 +582,23 @@ public class TestQuerySingleAttribute extends BaseTestCase { .where().isNotNull("customer.billingAddress.line1").query() .setCountDistinct(CountDistinctOrder.ATTR_DESC) .findSingleAttributeList(); - assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) from (" - + "select t2.line_1 as attribute_ from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.billing_address_id " - + "where t2.line_1 is not null" - + ") r1 group by r1.attribute_ order by r1.attribute_ desc "); + + if (isOracle()) { + assertSql(query).contains("select r1.attribute_, count(*) from (select t2.line_1 as attribute_ from contact t0 join o_customer t1 on t1.id = t0.customer_id left join o_address t2 on t2.id = t1.billing_address_id where t2.line_1 is not null) r1 group by r1.attribute_ order by r1.attribute_ desc offset 1 rows fetch next 2 rows only"); + } else { + assertSql(query).contains("select r1.attribute_, count(*) from (" + + "select t2.line_1 as attribute_ from contact t0 " + + "join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.billing_address_id " + + "where t2.line_1 is not null" + + ") r1 group by r1.attribute_ order by r1.attribute_ desc "); + } if (isSqlServer()) { assertThat(sqlOf(query)).endsWith(" fetch next 2 rows only"); } else if (isDb2()) { assertSql(query).endsWith("FETCH FIRST 2 ROWS ONLY"); + } else if (isOracle()) { + assertSql(query).contains(" offset 1 rows fetch next 2 rows only"); } else { assertThat(sqlOf(query)).endsWith(" limit 2 offset 1"); } diff --git a/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java b/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java index 85a8a8796..58b8c2285 100644 --- a/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java +++ b/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java @@ -219,7 +219,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase { assertSql(query).contains("top 100 "); assertSql(query).contains("order by o.ship_date desc"); } else if (isOracle()) { - assertSql(query).contains("a where rownum <= 100 )"); + assertSql(query).contains("fetch next 100 rows only"); } else { assertSql(query).contains("order by o.ship_date desc limit 100"); } @@ -254,7 +254,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase { assertSql(query).contains("top 100 "); assertSql(query).contains("order by o.ship_date desc, o.id"); } else if (isOracle()) { - assertSql(query).contains("a where rownum <= 100 )"); + assertSql(query).contains("fetch next 100 rows only"); } else { assertSql(query).contains("order by o.ship_date desc, o.id limit 100"); } @@ -356,7 +356,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase { assertThat(sqlOf(query)).contains("select top 100 "); assertThat(sqlOf(query)).contains("order by o.id desc"); } else if (isOracle()) { - assertThat(sqlOf(query)).contains("a where rownum <= 100 )"); + assertThat(sqlOf(query)).contains("fetch next 100 rows only"); } else { assertThat(sqlOf(query)).contains("order by o.id desc limit 100"); } diff --git a/src/test/java/org/tests/transaction/TestNestedSubTransaction.java b/src/test/java/org/tests/transaction/TestNestedSubTransaction.java index a30d5a104..313019b5f 100644 --- a/src/test/java/org/tests/transaction/TestNestedSubTransaction.java +++ b/src/test/java/org/tests/transaction/TestNestedSubTransaction.java @@ -65,7 +65,7 @@ public class TestNestedSubTransaction extends BaseTestCase { } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA, Platform.ORACLE}) @Test public void nestedUseSavepoint_doubleNested_rollbackCommit() { @@ -102,7 +102,7 @@ public class TestNestedSubTransaction extends BaseTestCase { } } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA, Platform.ORACLE}) @Test public void nestedUseSavepoint_doubleNested_commitRollback() { @@ -139,7 +139,7 @@ public class TestNestedSubTransaction extends BaseTestCase { } } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA, Platform.ORACLE}) @Test public void nestedUseSavepoint_nested_RequiresNew() { @@ -175,7 +175,7 @@ public class TestNestedSubTransaction extends BaseTestCase { assertNull(after); } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA, Platform.ORACLE}) @Test public void nestedUseSavepoint() {