diff --git a/howto-test.md b/howto-test.md index dcb2c727d..ed159cac2 100644 --- a/howto-test.md +++ b/howto-test.md @@ -151,3 +151,18 @@ In `InsertHandler.getPstmt` the `meta.getIdentityDbColumns()` contains TODO: not yet tested + +'hana' platform +-------------------- + +Requires an installed SAP HANA server - e.g. https://store.docker.com/images/sap-hana-express-edition + +- Create a user `EBEAN_TEST` with password `Eb3an_test` in the tenant database `HXE` +- Adjust the connection string and/or username in `ebean.properties` + +run: `mvn clean test -Ddatasource.default=hana` + +Current status: PASS +Tests run: 2486, Failures: 0, Errors: 0, Skipped: 84 + + diff --git a/pom.xml b/pom.xml index b89db10dc..c08c66ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ io.ebean ebean-annotation - 4.2 + 4.3 @@ -270,6 +270,13 @@ 5.1.43 test + + + com.sap.cloud.db.jdbc + ngdbc + 2.3.48 + test + org.avaje.composite diff --git a/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java index 152cd3588..035673c10 100644 --- a/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java @@ -176,9 +176,17 @@ public class DatabasePlatform { * findIterate() and findVisit(). */ protected boolean forwardOnlyHintOnFindIterate; + + /** + * If set then use the CONCUR_UPDATABLE hint when creating ResultSets. + * + * This is {@code false} for HANA + */ + protected boolean supportsResultSetConcurrencyModeUpdatable = true; + /** - * By default we use JDBC batch when cascading (except for SQL Server). + * By default we use JDBC batch when cascading (except for SQL Server and HANA). */ protected PersistBatch persistBatchOnCascade = PersistBatch.ALL; @@ -516,6 +524,24 @@ public class DatabasePlatform { public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) { this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate; } + + /** + * Return true if the ResultSet CONCUR_UPDATABLE Hint should be used on + * createNativeSqlTree() PreparedStatements. + *

+ * This specifically is required for Hana which doesn't support CONCUR_UPDATABLE + *

+ */ + public boolean isSupportsResultSetConcurrencyModeUpdatable() { + return supportsResultSetConcurrencyModeUpdatable; + } + + /** + * Set to true if the ResultSet CONCUR_UPDATABLE Hint should be used by default on createNativeSqlTree() PreparedStatements. + */ + public void setSupportsResultSetConcurrencyModeUpdatable(boolean supportsResultSetConcurrencyModeUpdatable) { + this.supportsResultSetConcurrencyModeUpdatable = supportsResultSetConcurrencyModeUpdatable; + } /** * Normally not needed - overridden in CockroachPlatform. diff --git a/src/main/java/io/ebean/config/dbplatform/hana/HanaBasicSqlLimiter.java b/src/main/java/io/ebean/config/dbplatform/hana/HanaBasicSqlLimiter.java new file mode 100644 index 000000000..42239fc1a --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/hana/HanaBasicSqlLimiter.java @@ -0,0 +1,24 @@ +package io.ebean.config.dbplatform.hana; + +import io.ebean.config.dbplatform.BasicSqlLimiter; + +public class HanaBasicSqlLimiter implements BasicSqlLimiter { + @Override + public String limit(String dbSql, int firstRow, int maxRows) { + StringBuilder sb = new StringBuilder(50 + dbSql.length()); + + sb.append(dbSql); + + if (maxRows > 0) { + sb.append(" ").append("limit"); + sb.append(" ").append(maxRows); + + if (firstRow > 0) { + sb.append(" ").append("offset").append(" "); + sb.append(firstRow); + } + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/io/ebean/config/dbplatform/hana/HanaHistorySupport.java b/src/main/java/io/ebean/config/dbplatform/hana/HanaHistorySupport.java new file mode 100644 index 000000000..ea2c20d18 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/hana/HanaHistorySupport.java @@ -0,0 +1,27 @@ +package io.ebean.config.dbplatform.hana; + +import io.ebean.config.dbplatform.DbStandardHistorySupport; + +public class HanaHistorySupport extends DbStandardHistorySupport { + + @Override + public String getAsOfViewSuffix(String asOfViewSuffix) { + return " for system_time as of ?"; + } + + @Override + public String getVersionsBetweenSuffix(String asOfViewSuffix) { + return " for system_time between ? and ?"; + } + + @Override + public String getSysPeriodLower(String tableAlias, String sysPeriod) { + return tableAlias + "." + sysPeriod + "_start"; + } + + @Override + public String getSysPeriodUpper(String tableAlias, String sysPeriod) { + return tableAlias + "." + sysPeriod + "_end"; + } + +} diff --git a/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java b/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java new file mode 100644 index 000000000..f266bb931 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java @@ -0,0 +1,101 @@ +package io.ebean.config.dbplatform.hana; + +import io.ebean.Query.ForUpdate; +import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; +import io.ebean.config.PlatformConfig; +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.config.dbplatform.DbPlatformType; +import io.ebean.config.dbplatform.DbType; +import io.ebean.config.dbplatform.IdType; +import io.ebean.config.dbplatform.SqlErrorCodes; + +public class HanaPlatform extends DatabasePlatform { + public HanaPlatform() { + this.basicSqlLimiter = new HanaBasicSqlLimiter(); + + this.columnAliasPrefix = null; + + this.dbDefaultValue.setNow("current_timestamp"); + + this.dbIdentity.setIdType(IdType.IDENTITY); + this.dbIdentity.setSelectLastInsertedIdTemplate("select current_identity_value() from sys.dummy"); + this.dbIdentity.setSupportsGetGeneratedKeys(false); + this.dbIdentity.setSupportsIdentity(true); + + this.dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false)); + this.dbTypeMap.put(DbType.BINARY, new DbPlatformType("varbinary", 255)); + this.dbTypeMap.put(DbType.BIT, new DbPlatformType("smallint", false)); + this.dbTypeMap.put(DbType.BLOB, new DbPlatformType("blob", false)); + this.dbTypeMap.put(DbType.CHAR, new DbPlatformType("nvarchar", 255)); + this.dbTypeMap.put(DbType.CLOB, new DbPlatformType("nclob", false)); + this.dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false)); + this.dbTypeMap.put(DbType.JSONVARCHAR, new DbPlatformType("nvarchar", 255)); + this.dbTypeMap.put(DbType.LINESTRING, new DbPlatformType("st_geometry")); + this.dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("blob", false)); + this.dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("nclob", false)); + this.dbTypeMap.put(DbType.MULTILINESTRING, new DbPlatformType("st_geometry")); + this.dbTypeMap.put(DbType.MULTIPOINT, new DbPlatformType("st_geometry")); + this.dbTypeMap.put(DbType.MULTIPOLYGON, new DbPlatformType("st_geometry")); + this.dbTypeMap.put(DbType.POINT, new DbPlatformType("st_point")); + this.dbTypeMap.put(DbType.POLYGON, new DbPlatformType("st_geometry")); + this.dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("smallint", false)); + this.dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint", false)); + this.dbTypeMap.put(DbType.UUID, new DbPlatformType("varchar", 40)); + this.dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255)); + this.dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("nvarchar", 255)); + + this.exceptionTranslator = new SqlErrorCodes().addAcquireLock("131", "133", "146") + .addDataIntegrity("130", "429", "461", "462").addDuplicateKey("144", "301", "349").build(); + + this.historySupport = new HanaHistorySupport(); + + this.likeClauseRaw = "like ?"; + + this.maxConstraintNameLength = 127; + this.maxTableNameLength = 127; + + this.persistBatchOnCascade = PersistBatch.NONE; + + this.platform = Platform.HANA; + + this.sqlLimiter = new HanaSqlLimiter(); + + this.supportsResultSetConcurrencyModeUpdatable = false; + } + + @Override + protected void addGeoTypes(int srid) { + this.dbTypeMap.put(DbType.LINESTRING, new DbPlatformType("st_geometry(" + srid + ")", false)); + this.dbTypeMap.put(DbType.MULTILINESTRING, new DbPlatformType("st_geometry(" + srid + ")", false)); + this.dbTypeMap.put(DbType.MULTIPOINT, new DbPlatformType("st_geometry(" + srid + ")", false)); + this.dbTypeMap.put(DbType.MULTIPOLYGON, new DbPlatformType("st_geometry(" + srid + ")", false)); + this.dbTypeMap.put(DbType.POINT, new DbPlatformType("st_point(" + srid + ")", false)); + this.dbTypeMap.put(DbType.POLYGON, new DbPlatformType("st_geometry(" + srid + ")", false)); + } + + @Override + protected String withForUpdate(String sql, ForUpdate forUpdateMode) { + switch (forUpdateMode) { + case BASE: + return sql + " for update"; + case NOWAIT: + return sql + " for update nowait"; + case SKIPLOCKED: + return sql + " for update ignore locked"; + default: + throw new IllegalArgumentException("Unknown update mode: " + forUpdateMode.name()); + } + } + + @Override + protected void configure(PlatformConfig config, boolean allQuotedIdentifiers) { + super.configure(config, allQuotedIdentifiers); + if (config.getDbUuid().useBinary()) { + this.dbTypeMap.put(DbType.UUID, new DbPlatformType("varbinary", 16)); + } else { + this.dbTypeMap.put(DbType.UUID, new DbPlatformType("varchar", 40)); + } + } + +} diff --git a/src/main/java/io/ebean/config/dbplatform/hana/HanaSqlLimiter.java b/src/main/java/io/ebean/config/dbplatform/hana/HanaSqlLimiter.java new file mode 100644 index 000000000..bf8ca4e7b --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/hana/HanaSqlLimiter.java @@ -0,0 +1,35 @@ +package io.ebean.config.dbplatform.hana; + +import io.ebean.config.dbplatform.SqlLimitRequest; +import io.ebean.config.dbplatform.SqlLimitResponse; +import io.ebean.config.dbplatform.SqlLimiter; + +public class HanaSqlLimiter implements SqlLimiter { + @Override + public SqlLimitResponse limit(SqlLimitRequest request) { + String dbSql = request.getDbSql(); + + StringBuilder sb = new StringBuilder(50 + dbSql.length()); + sb.append("select "); + if (request.isDistinct()) { + sb.append("distinct "); + } + + sb.append(dbSql); + + int firstRow = request.getFirstRow(); + int maxRows = request.getMaxRows(); + + if (maxRows > 0) { + sb.append(" ").append("limit ").append(maxRows); + if (firstRow > 0) { + sb.append(" ").append("offset "); + sb.append(firstRow); + } + } + + String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery()); + + return new SqlLimitResponse(sql, false); + } +} \ No newline at end of file diff --git a/src/main/java/io/ebean/config/dbplatform/hana/package-info.java b/src/main/java/io/ebean/config/dbplatform/hana/package-info.java new file mode 100644 index 000000000..55fc12c5e --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/hana/package-info.java @@ -0,0 +1,4 @@ +/** + * SAP HANA specific support. + */ +package io.ebean.config.dbplatform.hana; diff --git a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index bf662a91a..19817a924 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -10,6 +10,7 @@ import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.db2.DB2Platform; import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.oracle.OraclePlatform; @@ -679,6 +680,8 @@ public class DefaultDbMigration implements DbMigration { return new DB2Platform(); case SQLITE: return new SQLitePlatform(); + case HANA: + return new HanaPlatform(); case GENERIC: return new DatabasePlatform(); diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java index ac5f64557..29ee263bc 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java @@ -28,7 +28,11 @@ public class BaseDdlHandler implements DdlHandler { protected final TableDdl tableDdl; public BaseDdlHandler(ServerConfig serverConfig, PlatformDdl platformDdl) { - this.tableDdl = new BaseTableDdl(serverConfig, platformDdl); + this(serverConfig, platformDdl, new BaseTableDdl(serverConfig, platformDdl)); + } + + protected BaseDdlHandler(ServerConfig serverConfig, PlatformDdl platformDdl, TableDdl tableDdl) { + this.tableDdl = tableDdl; } @Override diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java new file mode 100644 index 000000000..b898c02a6 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java @@ -0,0 +1,226 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import java.io.IOException; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import io.ebean.config.ServerConfig; +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.config.dbplatform.DbPlatformType; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; +import io.ebeaninternal.dbmigration.migration.AlterColumn; + +public abstract class AbstractHanaDdl extends PlatformDdl { + + private static final Pattern ARRAY_PATTERN = Pattern.compile("(\\w+)\\s*\\[\\s*\\]\\s*(\\(\\d+\\))?", + Pattern.CASE_INSENSITIVE); + + public AbstractHanaDdl(DatabasePlatform platform) { + super(platform); + this.addColumn = "add ("; + this.addColumnSuffix = ")"; + this.alterColumn = "alter ("; + this.alterColumnSuffix = ")"; + this.columnDropDefault = " default null"; + this.columnSetDefault = " default"; + this.columnSetNotnull = " not null"; + this.columnSetNull = " null"; + this.dropColumn = "drop ("; + this.dropColumnSuffix = ")"; + this.dropConstraintIfExists = "drop constraint "; + this.dropIndexIfExists = "drop index "; + this.dropSequenceIfExists = "drop sequence "; + this.dropTableCascade = " cascade"; + this.dropTableIfExists = "drop table "; + this.fallbackArrayType = "nvarchar(1000)"; + this.historyDdl = new HanaHistoryDdl(); + this.identitySuffix = " generated by default as identity"; + } + + @Override + public String alterColumnBaseAttributes(AlterColumn alter) { + String tableName = alter.getTableName(); + String columnName = alter.getColumnName(); + String currentType = alter.getCurrentType(); + String type = alter.getType() != null ? alter.getType() : currentType; + type = convert(type, false); + currentType = convert(currentType, false); + boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull()); + String notnullClause = notnull ? " not null" : ""; + String defaultValue = DdlHelp.isDropDefault(alter.getDefaultValue()) ? "null" + : (alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue()); + String defaultValueClause = (defaultValue == null || defaultValue.isEmpty()) ? "" : " default " + defaultValue; + + try { + DdlBuffer buffer = new BaseDdlBuffer(null); + if (!isConvertible(currentType, type)) { + // add an intermediate conversion if possible + if (isNumberType(currentType)) { + // numbers can always be converted to decimal + buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName) + .append(" decimal ").append(defaultValueClause).append(notnullClause).append(alterColumnSuffix) + .endOfStatement(); + } else if (isStringType(currentType)) { + // strings can always be converted to nclob + buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName) + .append(" nclob ").append(defaultValueClause).append(notnullClause).append(alterColumnSuffix) + .endOfStatement(); + } + } + + buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName) + .append(" ").append(type).append(defaultValueClause).append(notnullClause).append(alterColumnSuffix); + + return buffer.getBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) { + throw new UnsupportedOperationException(); + } + + @Override + public String alterColumnNotnull(String tableName, String columnName, boolean notnull) { + return null; + } + + @Override + public DdlHandler createDdlHandler(ServerConfig serverConfig) { + return new HanaDdlHandler(serverConfig, this); + } + + @Override + public String alterColumnType(String tableName, String columnName, String type) { + return null; + } + + @Override + protected String convertArrayType(String logicalArrayType) { + Matcher matcher = ARRAY_PATTERN.matcher(logicalArrayType); + if (matcher.matches()) { + return convert(matcher.group(1), false) + " array" + (matcher.group(2) == null ? "" : matcher.group(2)); + } else { + return fallbackArrayType; + } + } + + @Override + public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, + String[] nullableColumns) { + if (nullableColumns == null || nullableColumns.length == 0) { + return super.alterTableAddUniqueConstraint(tableName, uqName, columns, nullableColumns); + } else { + return "-- cannot create unique index \"" + uqName + "\" on table \"" + tableName + "\" with nullable columns"; + } + } + + @Override + public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) { + DdlBuffer buffer = new BaseDdlBuffer(null); + try { + buffer.append("delimiter $$").newLine(); + buffer.append("do").newLine(); + buffer.append("begin").newLine(); + buffer.append("declare exit handler for sql_error_code 397 begin end").endOfStatement(); + buffer.append("exec 'alter table ").append(tableName).append(" ").append(dropUniqueConstraint).append(" ") + .append(maxConstraintName(uniqueConstraintName)).append("'").endOfStatement(); + buffer.append("end").endOfStatement(); + buffer.append("$$"); + return buffer.getBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String alterTableDropConstraint(String tableName, String constraintName) { + return alterTableDropUniqueConstraint(tableName, constraintName); + } + + /** + * It is rather complex to delete a column on HANA as there must not exist any + * foreign keys. That's why we call a user stored procedure here + */ + @Override + public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException { + buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')") + .endOfStatement(); + } + + /** + * Check if a data type can be converted to another data type. Data types can't + * be converted if the target type has a lower precision than the source type. + * + * @param sourceType The source data type + * @param targetType the target data type + * @return {@code true} if the type can be converted, {@code false} otherwise + */ + private boolean isConvertible(String sourceType, String targetType) { + if (Objects.equals(sourceType, targetType)) { + return true; + } + + if (sourceType == null || targetType == null) { + return true; + } + + if ("bigint".equals(sourceType)) { + if ("integer".equals(targetType) || "smallint".equals(targetType) || "tinyint".equals(targetType)) { + return false; + } + } else if ("integer".equals(sourceType)) { + if ("smallint".equals(targetType) || "tinyint".equals(targetType)) { + return false; + } + } else if ("smallint".equals(sourceType)) { + if ("tinyint".equals(targetType)) { + return false; + } + } else if ("double".equals(sourceType)) { + if ("real".equals(targetType)) { + return false; + } + } + + DbPlatformType dbPlatformSourceType = DbPlatformType.parse(sourceType); + + if ("float".equals(dbPlatformSourceType.getName())) { + if ("real".equals(targetType)) { + return false; + } + } else if ("varchar".equals(dbPlatformSourceType.getName()) || "nvarchar".equals(dbPlatformSourceType.getName())) { + DbPlatformType dbPlatformTargetType = DbPlatformType.parse(targetType); + if ("varchar".equals(dbPlatformTargetType.getName()) || "nvarchar".equals(dbPlatformTargetType.getName())) { + if (dbPlatformSourceType.getDefaultLength() > dbPlatformTargetType.getDefaultLength()) { + return false; + } + } + } else if ("decimal".equals(dbPlatformSourceType.getName())) { + DbPlatformType dbPlatformTargetType = DbPlatformType.parse(targetType); + if ("decimal".equals(dbPlatformTargetType.getName())) { + if (dbPlatformSourceType.getDefaultLength() > dbPlatformTargetType.getDefaultLength() + || dbPlatformSourceType.getDefaultScale() > dbPlatformTargetType.getDefaultScale()) { + return false; + } + } + } + + return true; + } + + private boolean isNumberType(String type) { + return type != null + && ("bigint".equals(type) || "integer".equals(type) || "smallint".equals(type) || "tinyint".equals(type) + || type.startsWith("float") || "real".equals(type) || "double".equals(type) || type.startsWith("decimal")); + } + + private boolean isStringType(String type) { + return type != null + && (type.startsWith("varchar") || type.startsWith("nvarchar") || "clob".equals(type) || "nclob".equals(type)); + } +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java index 7b3c72bb9..2a46f83a0 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -260,7 +260,7 @@ public class BaseTableDdl implements TableDdl { String partitionMode = createTable.getPartitionMode(); DdlBuffer apply = writer.apply(); - apply.append("create table ").append(tableName).append(" ("); + apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(tableName).append(" ("); writeTableColumns(apply, columns, useIdentity); writeCheckConstraints(apply, createTable); writeUniqueConstraints(apply, createTable); diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java index 5eb3d6978..a425cf9be 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java @@ -175,7 +175,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { - apply.append("create table ").append(table.getName()).append(historySuffix).append("(").newLine(); + apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(table.getName()).append(historySuffix).append("(").newLine(); Collection cols = table.allColumns(); for (MColumn column : cols) { @@ -238,8 +238,8 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { } protected void dropSysPeriodColumns(DdlBuffer buffer, String baseTableName) throws IOException { - buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriodStart).endOfStatement(); - buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriodEnd).endOfStatement(); + platformDdl.alterTableDropColumn(buffer, baseTableName, sysPeriodStart); + platformDdl.alterTableDropColumn(buffer, baseTableName, sysPeriodEnd); } protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List columns) throws IOException { diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java new file mode 100644 index 000000000..b3b36c3ef --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java @@ -0,0 +1,51 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import java.io.IOException; + +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; + +public class HanaColumnStoreDdl extends AbstractHanaDdl { + + public HanaColumnStoreDdl(DatabasePlatform platform) { + super(platform); + this.createTable = "create column table"; + } + + @Override + public String createIndex(String indexName, String tableName, String[] columns) { + if (columns == null || columns.length == 0) { + return "-- cannot create index: no columns given"; + } + + if (columns.length == 1) { + return "-- explicit index \"" + indexName + "\" for single column \"" + columns[0] + "\" of table \"" + tableName + + "\" is not necessary"; + } + + StringBuilder buffer = new StringBuilder(); + buffer.append("create inverted hash index ").append(maxConstraintName(indexName)).append(" on ").append(tableName); + appendColumns(columns, buffer); + + return buffer.toString(); + } + + @Override + public String dropIndex(String indexName, String tableName) { + DdlBuffer buffer = new BaseDdlBuffer(null); + try { + buffer.append("delimiter $$").newLine(); + buffer.append("do").newLine(); + buffer.append("begin").newLine(); + buffer.append("declare exit handler for sql_error_code 261 begin end").endOfStatement(); + buffer.append("exec '").append(dropIndexIfExists).append(maxConstraintName(indexName)).append("'") + .endOfStatement(); + buffer.append("end").endOfStatement(); + buffer.append("$$"); + return buffer.getBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlHandler.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlHandler.java new file mode 100644 index 000000000..b3355e280 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlHandler.java @@ -0,0 +1,11 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.config.ServerConfig; +import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler; + +public class HanaDdlHandler extends BaseDdlHandler { + + public HanaDdlHandler(ServerConfig serverConfig, PlatformDdl platformDdl) { + super(serverConfig, platformDdl, new HanaTableDdl(serverConfig, platformDdl)); + } +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java new file mode 100644 index 000000000..0ea165ba3 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java @@ -0,0 +1,160 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import io.ebean.config.ServerConfig; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.migration.AddHistoryTable; +import io.ebeaninternal.dbmigration.migration.DropHistoryTable; +import io.ebeaninternal.dbmigration.model.MColumn; +import io.ebeaninternal.dbmigration.model.MTable; + +public class HanaHistoryDdl implements PlatformHistoryDdl { + + private String systemPeriodStart; + private String systemPeriodEnd; + private PlatformDdl platformDdl; + private String historySuffix; + private final AtomicInteger counter = new AtomicInteger(0); + private Map createdHistoryTables = new ConcurrentHashMap<>(); + + @Override + public void configure(ServerConfig serverConfig, PlatformDdl platformDdl) { + this.systemPeriodStart = serverConfig.getAsOfSysPeriod() + "_start"; + this.systemPeriodEnd = serverConfig.getAsOfSysPeriod() + "_end"; + this.platformDdl = platformDdl; + this.historySuffix = serverConfig.getHistoryTableSuffix(); + } + + @Override + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + String tableName = table.getName(); + String historyTableName = tableName + historySuffix; + DdlBuffer apply = writer.applyHistoryView(); + if (apply.isEmpty()) { + createdHistoryTables.clear(); + } + + apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(historyTableName).append(" (").newLine(); + + // create history table + Collection cols = table.allColumns(); + for (MColumn column : cols) { + if (!column.isDraftOnly()) { + writeColumnDefinition(apply, column.getName(), column.getType(), column.getDefaultValue(), column.isNotnull(), + column.isIdentity() ? platformDdl.identitySuffix : null); + apply.append(",").newLine(); + } + } + writeColumnDefinition(apply, systemPeriodStart, "TIMESTAMP", null, false, null); + apply.append(",").newLine(); + writeColumnDefinition(apply, systemPeriodEnd, "TIMESTAMP", null, false, null); + apply.newLine().append(")").endOfStatement(); + + // enable system versioning + apply.append("alter table ").append(tableName).append(" add (").newLine(); + apply.append(" ").append(systemPeriodStart).append(" TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, ") + .newLine(); + apply.append(" ").append(systemPeriodEnd).append(" TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END").newLine(); + apply.append(")").endOfStatement(); + + apply.append("alter table ").append(tableName).append(" add period for system_time(").append(systemPeriodStart) + .append(",").append(systemPeriodEnd).append(")").endOfStatement(); + + enableSystemVersioning(apply, tableName, historyTableName, true, false); + + createdHistoryTables.put(tableName, historyTableName); + + dropHistoryTable(writer.dropAll(), tableName, historyTableName); + } + + @Override + public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + dropHistoryTable(writer.applyDropDependencies(), dropHistoryTable.getBaseTable(), + dropHistoryTable.getBaseTable() + historySuffix); + } + + protected void dropHistoryTable(DdlBuffer apply, String baseTable, String historyTable) throws IOException { + // disable system versioning + disableSystemVersioning(apply, baseTable); + + apply.append("alter table ").append(baseTable).append(" drop period for system_time").endOfStatement(); + + // drop the period columns + apply.append("alter table ").append(baseTable).append(" drop (").append(systemPeriodStart).append(",") + .append(systemPeriodEnd).append(")").endOfStatement(); + + // drop the history table + apply.append("drop table ").append(historyTable).append(" cascade").endOfStatement(); + } + + @Override + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + MTable table = writer.getTable(addHistoryTable.getBaseTable()); + if (table == null) { + throw new IllegalStateException( + "MTable " + addHistoryTable.getBaseTable() + " not found in writer? (required for history DDL)"); + } + createWithHistory(writer, table); + } + + @Override + public void updateTriggers(DdlWrite write, HistoryTableUpdate baseTable) throws IOException { + // nothing to do + } + + protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type, String defaultValue, + boolean isNotNull, String generated) throws IOException { + + String platformType = platformDdl.convert(type, false); + buffer.append(" ").append(platformDdl.lowerColumnName(columnName)); + buffer.append(" ").append(platformType); + if (defaultValue != null) { + buffer.append(" default ").append(defaultValue); + } + if (isNotNull) { + buffer.append(" not null"); + } + if (generated != null) { + buffer.append(" ").append(generated); + } + } + + public void disableSystemVersioning(DdlBuffer apply, String tableName) throws IOException { + disableSystemVersioning(apply, tableName, false); + } + + public void disableSystemVersioning(DdlBuffer apply, String tableName, boolean uniqueStatement) throws IOException { + apply.append("alter table ").append(tableName).append(" drop system versioning"); + if (uniqueStatement) { + // needed for the DB migration test to prevent the statement from being filtered + // out as a duplicate + apply.append(" /* ").append(String.valueOf(counter.getAndIncrement())).append(" */"); + } + apply.endOfStatement(); + } + + public void enableSystemVersioning(DdlBuffer apply, String tableName, String historyTableName, boolean validated, + boolean uniqueStatement) throws IOException { + apply.append("alter table ").append(tableName).append(" add system versioning history table ") + .append(historyTableName); + if (!validated) { + apply.append(" not validated"); + } + if (uniqueStatement) { + // needed for the DB migration test to prevent the statement from being filtered + // out as a duplicate + apply.append(" /* ").append(String.valueOf(counter.getAndIncrement())).append(" */"); + } + apply.endOfStatement(); + } + + public boolean isSystemVersioningEnabled(String tableName) { + return !createdHistoryTables.containsKey(tableName); + } +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaRowStoreDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaRowStoreDdl.java new file mode 100644 index 000000000..d662f1039 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaRowStoreDdl.java @@ -0,0 +1,11 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.config.dbplatform.DatabasePlatform; + +public class HanaRowStoreDdl extends AbstractHanaDdl { + + public HanaRowStoreDdl(DatabasePlatform platform) { + super(platform); + this.createTable = "create row table"; + } +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java new file mode 100644 index 000000000..1d0b47abc --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java @@ -0,0 +1,136 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import java.io.IOException; +import java.util.List; + +import io.ebean.config.PropertiesWrapper; +import io.ebean.config.ServerConfig; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.migration.AddColumn; +import io.ebeaninternal.dbmigration.migration.AlterColumn; +import io.ebeaninternal.dbmigration.migration.Column; +import io.ebeaninternal.dbmigration.migration.DropColumn; +import io.ebeaninternal.dbmigration.model.MTable; + +public class HanaTableDdl extends BaseTableDdl { + + private final HanaHistoryDdl historyDdl; + private final boolean generateUniqueDdl; + + public HanaTableDdl(ServerConfig serverConfig, PlatformDdl platformDdl) { + super(serverConfig, platformDdl); + this.historyDdl = (HanaHistoryDdl) platformDdl.historyDdl; + if (serverConfig.getProperties() != null) { + PropertiesWrapper wrapper = new PropertiesWrapper("ebean", "hana", serverConfig.getProperties(), + serverConfig.getClassLoadConfig()); + this.generateUniqueDdl = wrapper.getBoolean("generateUniqueDdl", false); + } else { + this.generateUniqueDdl = false; + } + } + + @Override + protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException { + String ddl = platformDdl.alterColumnBaseAttributes(alter); + if (hasValue(ddl)) { + writer.apply().append(ddl).endOfStatement(); + } + } + + @Override + public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { + String tableName = addColumn.getTableName(); + MTable table = writer.getTable(tableName); + if (table == null) { + super.generate(writer, addColumn); + return; + } + + boolean manageSystemVersioning = isTrue(table.isWithHistory()) && historyDdl.isSystemVersioningEnabled(tableName); + + if (manageSystemVersioning) { + historyDdl.disableSystemVersioning(writer.apply(), table.getName(), this.generateUniqueDdl); + } + + super.generate(writer, addColumn); + + if (manageSystemVersioning) { + // make same changes to the history table + String historyTable = historyTable(tableName); + List columns = addColumn.getColumn(); + for (Column column : columns) { + alterTableAddColumn(writer.apply(), historyTable, column, true, true); + } + + historyDdl.enableSystemVersioning(writer.apply(), table.getName(), historyTable, false, this.generateUniqueDdl); + } + } + + @Override + public void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException { + String tableName = alterColumn.getTableName(); + MTable table = writer.getTable(tableName); + if (table == null) { + super.generate(writer, alterColumn); + return; + } + + boolean manageSystemVersioning = isTrue(table.isWithHistory()) && historyDdl.isSystemVersioningEnabled(tableName); + + if (manageSystemVersioning) { + historyDdl.disableSystemVersioning(writer.apply(), tableName, this.generateUniqueDdl); + } + + super.generate(writer, alterColumn); + + if (manageSystemVersioning) { + // make same changes to the history table + String historyTable = historyTable(tableName); + if (hasValue(alterColumn.getType()) || hasValue(alterColumn.getDefaultValue()) + || alterColumn.isNotnull() != null) { + AlterColumn alterHistoryColumn = new AlterColumn(); + alterHistoryColumn.setTableName(historyTable); + alterHistoryColumn.setColumnName(alterColumn.getColumnName()); + alterHistoryColumn.setType(alterColumn.getType()); + alterHistoryColumn.setDefaultValue(alterColumn.getDefaultValue()); + alterHistoryColumn.setNotnull(alterColumn.isNotnull()); + alterHistoryColumn.setCurrentType(alterColumn.getCurrentType()); + alterHistoryColumn.setCurrentDefaultValue(alterColumn.getCurrentDefaultValue()); + alterHistoryColumn.setCurrentNotnull(alterColumn.isCurrentNotnull()); + String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn); + + // write the apply to history table + writer.apply().append(histColumnDdl).endOfStatement(); + } + + historyDdl.enableSystemVersioning(writer.apply(), tableName, historyTable, false, this.generateUniqueDdl); + } + } + + @Override + public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException { + String tableName = dropColumn.getTableName(); + MTable table = writer.getTable(tableName); + if (table == null) { + super.generate(writer, dropColumn); + return; + } + + boolean manageSystemVersioning = isTrue(table.isWithHistory()) && historyDdl.isSystemVersioningEnabled(tableName); + + if (manageSystemVersioning) { + historyDdl.disableSystemVersioning(writer.apply(), tableName, this.generateUniqueDdl); + } + + super.generate(writer, dropColumn); + + if (manageSystemVersioning) { + // also drop from the history table + String historyTable = historyTable(tableName); + alterTableDropColumn(writer.apply(), historyTable, dropColumn.getColumnName()); + + historyDdl.enableSystemVersioning(writer.apply(), tableName, historyTable, false, this.generateUniqueDdl); + } + } + +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java index 515004bd5..a07dcdfd6 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -78,12 +78,16 @@ public class PlatformDdl { protected String dropIndexIfExists = "drop index if exists "; protected String alterColumn = "alter column"; + + protected String alterColumnSuffix = ""; protected String dropUniqueConstraint = "drop constraint"; protected String addConstraint = "add constraint"; protected String addColumn = "add column"; + + protected String addColumnSuffix = ""; protected String columnSetType = ""; @@ -96,6 +100,12 @@ public class PlatformDdl { protected String columnSetNull = "set null"; protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null"; + + protected String createTable = "create table"; + + protected String dropColumn = "drop column"; + + protected String dropColumnSuffix = ""; /** * Set false for MsSqlServer to allow multiple nulls for OneToOne mapping. @@ -458,6 +468,7 @@ public class PlatformDdl { if (isTrue(column.isNotnull())) { buffer.append(" not null"); } + buffer.append(addColumnSuffix); buffer.endOfStatement(); // check constraints cannot be added in one statement for h2 @@ -467,14 +478,15 @@ public class PlatformDdl { buffer.append(ddl).endOfStatement(); } } else { + buffer.append(addColumnSuffix); buffer.endOfStatement(); } } public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException { - buffer.append("alter table ").append(tableName).append(" drop column ").append(columnName) - .endOfStatement(); + buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName) + .append(dropColumnSuffix).endOfStatement(); } /** @@ -494,19 +506,19 @@ public class PlatformDdl { */ public String alterColumnType(String tableName, String columnName, String type) { - return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type, false); + return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type, false) + alterColumnSuffix; } /** * Alter a column adding or removing the not null constraint. *

- * Note that that MySql and SQL Server instead use alterColumnBaseAttributes() + * Note that that MySql, SQL Server, and HANA instead use alterColumnBaseAttributes() *

*/ public String alterColumnNotnull(String tableName, String columnName, boolean notnull) { String suffix = notnull ? columnSetNotnull : columnSetNull; - return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix; + return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix; } /** @@ -522,17 +534,17 @@ public class PlatformDdl { */ public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) { String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue); - return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix; + return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix; } /** * Alter column setting both the type and not null constraint. *

- * Used by MySql and SQL Server as these require both column attributes to be set together. + * Used by MySql, SQL Server, and HANA as these require both column attributes to be set together. *

*/ public String alterColumnBaseAttributes(AlterColumn alter) { - // by default do nothing, only used by mysql and sql server as they can only + // by default do nothing, only used by mysql, sql server, and HANA as they can only // modify the column with the full column definition return null; } @@ -662,6 +674,17 @@ public class PlatformDdl { public void unlockTables(DdlBuffer buffer, Collection tables) throws IOException { } + + /** + * Returns the database-specific "create table" command prefix. For HANA this is + * either "create column table" or "create row table", for all other databases + * it is "create table". + * + * @return The "create table" command prefix + */ + public String getCreateTableCommandPrefix() { + return createTable; + } public boolean suppressPrimaryKeyOnPartition() { return false; diff --git a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java index efeb3d9e0..acf955620 100644 --- a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -5,6 +5,7 @@ import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.cockroach.CockroachPlatform; import io.ebean.config.dbplatform.db2.DB2Platform; import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.oracle.OraclePlatform; @@ -106,6 +107,9 @@ public class DatabasePlatformFactory { if (dbName.equals("sqlite")) { return new SQLitePlatform(); } + if (dbName.equals("hana")) { + return new HanaPlatform(); + } throw new RuntimeException("database platform " + dbName + " is not known?"); } @@ -156,6 +160,8 @@ public class DatabasePlatformFactory { return new DB2Platform(); } else if (dbProductName.contains("sql anywhere")) { return new SqlAnywherePlatform(); + }else if (dbProductName.contains("hdb")) { + return new HanaPlatform(); } // use the standard one diff --git a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java index bf901ed50..dbf657c49 100644 --- a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java +++ b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java @@ -4,6 +4,7 @@ import io.ebean.config.dbplatform.DatabasePlatform; 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.MySqlDdl; import io.ebeaninternal.dbmigration.ddlgeneration.platform.Oracle10Ddl; @@ -47,6 +48,8 @@ public class PlatformDdlBuilder { case SQLSERVER17: case SQLSERVER: return new SqlServerDdl(platform); + case HANA: + return new HanaColumnStoreDdl(platform); default: return new PlatformDdl(platform); } 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 e9696e5b6..1bebb5e96 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java @@ -25,6 +25,8 @@ public class DbExpressionHandlerFactory { case SQLSERVER17: case SQLSERVER: return new SqlServerDbExpression(); + case HANA: + return new HanaDbExpression(); default: return new BasicDbExpression(); } diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/HanaDbExpression.java b/src/main/java/io/ebeaninternal/server/expression/platform/HanaDbExpression.java new file mode 100644 index 000000000..0593bdb84 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/expression/platform/HanaDbExpression.java @@ -0,0 +1,60 @@ +package io.ebeaninternal.server.expression.platform; + +import io.ebeaninternal.api.SpiExpressionRequest; +import io.ebeaninternal.server.expression.BitwiseOp; +import io.ebeaninternal.server.expression.Op; + +/** + * HANA handling of platform specific expressions. + */ +public class HanaDbExpression extends BaseDbExpression { + + @Override + public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, + long match) { + bitwiseFunction(request, propName, operator, compare); + } + + @Override + public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) { + request.append("json_value(").append(propName).append(", '$.").append(path).append("')"); + request.append(operator.bind()); + } + + @Override + public void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty) { + request.append("cardinality(").append(propName).append(")"); + if (empty) { + request.append(" = 0"); + } else { + request.append(" <> 0"); + } + } + + @Override + public String concat(String property0, String separator, String property1, String suffix) { + StringBuilder sb = new StringBuilder(); + sb.append("concat(").append(property0).append(", '").append(separator).append("'||").append(property1); + if (suffix != null && !suffix.isEmpty()) { + sb.append("||'").append(suffix).append('\''); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values) { + for (int i = 0; i < values.length; i++) { + if (i > 0) { + request.append(" and "); + } + request.append("(?"); + if (!contains) { + request.append(" not "); + } + request.append(" member of ").append(propName).append(")"); + + } + } + +} diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index a58c7b6ec..eeacc87df 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -426,7 +426,7 @@ class CQueryBuilder { try { // For SqlServer we need either "selectMethod=cursor" in the connection string or fetch explicitly a cursorable // statement here by specifying ResultSet.CONCUR_UPDATABLE - PreparedStatement statement = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + PreparedStatement statement = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, dbPlatform.isSupportsResultSetConcurrencyModeUpdatable() ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY); predicates.bind(statement, connection); ResultSet resultSet = statement.executeQuery(); diff --git a/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml b/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml index 825171455..d50eef8b6 100644 --- a/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml +++ b/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml @@ -159,4 +159,39 @@ BEGIN END $$ + +-- Inital script to create stored procedures etc for the hana platform +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN +-- deletes all constraints and foreign keys referring to TABLE.COLUMN +-- +CREATE OR REPLACE PROCEDURE usp_ebean_drop_foreign_keys(IN table_name NVARCHAR(256), IN column_name NVARCHAR(256)) +AS +BEGIN + DECLARE foreign_key_names TABLE(CONSTRAINT_NAME NVARCHAR(256), TABLE_NAME NVARCHAR(256)); + DECLARE i INT; + + foreign_key_names = SELECT CONSTRAINT_NAME, TABLE_NAME FROM SYS.REFERENTIAL_CONSTRAINTS WHERE SCHEMA_NAME=CURRENT_SCHEMA AND TABLE_NAME=UPPER(:table_name) AND COLUMN_NAME=UPPER(:column_name); + + FOR I IN 1 .. RECORD_COUNT(:foreign_key_names) DO + EXEC 'ALTER TABLE "' || ESCAPE_DOUBLE_QUOTES(:foreign_key_names.TABLE_NAME[i]) || '" DROP CONSTRAINT "' || ESCAPE_DOUBLE_QUOTES(:foreign_key_names.CONSTRAINT_NAME[i]) || '"'; + END FOR; + +END; +$$ + +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN +-- deletes the column and ensures that all indices and constraints are dropped first +-- +CREATE OR REPLACE PROCEDURE usp_ebean_drop_column(IN table_name NVARCHAR(256), IN column_name NVARCHAR(256)) +AS +BEGIN + CALL usp_ebean_drop_foreign_keys(table_name, column_name); + EXEC 'ALTER TABLE "' || UPPER(ESCAPE_DOUBLE_QUOTES(table_name)) || '" DROP ("' || UPPER(ESCAPE_DOUBLE_QUOTES(column_name)) || '")'; +END; +$$ + diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index cee47bcaf..7fd97fa9e 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -1,5 +1,6 @@ package io.ebean; +import io.ebean.annotation.PersistBatch; import io.ebean.annotation.Platform; import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaTimedMetric; @@ -10,6 +11,9 @@ import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.core.HelpCreateQueryRequest; import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.expression.platform.DbExpressionHandler; +import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory; + import org.avaje.agentloader.AgentLoader; import org.junit.runner.RunWith; import org.slf4j.Logger; @@ -144,6 +148,10 @@ public abstract class BaseTestCase { public boolean isMySql() { return Platform.MYSQL == platform(); } + + public boolean isHana() { + return Platform.HANA == platform(); + } public boolean isPlatformBooleanNative() { return Types.BOOLEAN == spiEbeanServer().getDatabasePlatform().getBooleanDbType(); @@ -152,6 +160,10 @@ public abstract class BaseTestCase { public boolean isPlatformOrderNullsSupport() { return isH2() || isPostgres(); } + + public boolean isPersistBatchOnCascade() { + return spiEbeanServer().getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE; + } /** * Wait for the L2 cache to propagate changes post-commit. @@ -205,6 +217,18 @@ public abstract class BaseTestCase { assertThat(sql).contains(containsIn+" not in "); } } + + /** + * Platform specific CONCAT clause. + */ + protected String concat(String property0, String separator, String property1) { + return concat(property0, separator, property1, null); + } + + protected String concat(String property0, String separator, String property1, String suffix) { + DbExpressionHandler dbExpressionHandler = DbExpressionHandlerFactory.from(spiEbeanServer().getDatabasePlatform()); + return dbExpressionHandler.concat(property0, separator, property1, suffix); + } protected OrmQueryRequest createQueryRequest(SpiQuery.Type type, Query query, Transaction t) { return HelpCreateQueryRequest.create(server(), type, query, t); diff --git a/src/test/java/io/ebean/DtoQueryFromOrmTest.java b/src/test/java/io/ebean/DtoQueryFromOrmTest.java index 07e8b3122..3a1adf02c 100644 --- a/src/test/java/io/ebean/DtoQueryFromOrmTest.java +++ b/src/test/java/io/ebean/DtoQueryFromOrmTest.java @@ -3,6 +3,7 @@ package io.ebean; import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaQueryMetric; import io.ebean.meta.MetaTimedMetric; + import org.ebeantest.LoggedSqlCollector; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -43,17 +44,13 @@ public class DtoQueryFromOrmTest extends BaseTestCase { resetAllMetrics(); - String[] prefix = {"Bl", "B", "Red", "jim"}; + String[] prefix = { "Bl", "B", "Red", "jim" }; for (String val : prefix) { List list = Ebean.find(Contact.class) - .select("email, concat(lastName,', ',firstName) as fullName") - .where().istartsWith("concat(lastName,', ',firstName)", val) - .orderBy().asc("lastName") - .setMaxRows(10) - .asDto(ContactDto.class) - .setLabel("prefixLoop") - .findList(); + .select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where() + .istartsWith(concat("lastName", ", ", "firstName"), val).orderBy().asc("lastName").setMaxRows(10) + .asDto(ContactDto.class).setLabel("prefixLoop").findList(); System.out.println("List:" + list); } @@ -77,14 +74,10 @@ public class DtoQueryFromOrmTest extends BaseTestCase { LoggedSqlCollector.start(); - DtoQuery query = - Ebean.find(Contact.class) + DtoQuery query = Ebean.find(Contact.class) // we must explicitly add the id property for DTO query (if we want it) - .select("id, email, concat(lastName,', ',firstName) as fullName") - .where().isNotNull("email").isNotNull("lastName") - .orderBy().asc("lastName") - .asDto(ContactDto.class) - .setLabel("explicitId") + .select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email") + .isNotNull("lastName").orderBy().asc("lastName").asDto(ContactDto.class).setLabel("explicitId") .setRelaxedMode(); List dtos = query.findList(); @@ -98,7 +91,8 @@ public class DtoQueryFromOrmTest extends BaseTestCase { } List sql = LoggedSqlCollector.stop(); - assertThat(sql.get(0)).contains("select t0.id, t0.email, concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); + assertThat(sql.get(0)).contains("select t0.id, t0.email, " + concat("t0.last_name", ", ", "t0.first_name") + + " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); } @Test @@ -108,12 +102,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase { LoggedSqlCollector.start(); - DtoQuery query = - Ebean.find(Contact.class) - .select("email, concat(lastName,', ',firstName) as fullName") - .where().isNotNull("email").isNotNull("lastName") - .orderBy().asc("lastName") - .asDto(ContactDto.class); + DtoQuery query = Ebean.find(Contact.class) + .select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email") + .isNotNull("lastName").orderBy().asc("lastName").asDto(ContactDto.class); List dtos = query.findList(); @@ -126,10 +117,10 @@ public class DtoQueryFromOrmTest extends BaseTestCase { } List sql = LoggedSqlCollector.stop(); - assertThat(sql.get(0)).contains("select t0.email, concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); + assertThat(sql.get(0)).contains("select t0.email, " + concat("t0.last_name", ", ", "t0.first_name") + + " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); } - @Test public void example() { @@ -137,15 +128,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase { LoggedSqlCollector.start(); - List contactDtos - = Ebean.find(Contact.class) - .setLabel("emailFullName") - .select("email, concat(lastName,', ',firstName) as fullName") - .where().isNotNull("email").isNotNull("lastName") - .orderBy().asc("lastName") - .setMaxRows(10) - .asDto(ContactDto.class) - .findList(); + List contactDtos = Ebean.find(Contact.class).setLabel("emailFullName") + .select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email") + .isNotNull("lastName").orderBy().asc("lastName").setMaxRows(10).asDto(ContactDto.class).findList(); assertThat(contactDtos).isNotEmpty(); @@ -158,10 +143,12 @@ public class DtoQueryFromOrmTest extends BaseTestCase { List sql = LoggedSqlCollector.stop(); if (isSqlServer()) { - assertThat(sql.get(0)).contains("select top 10 t0.email, concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); + assertThat(sql.get(0)).contains("select top 10 t0.email, " + concat("t0.last_name", ", ", "t0.first_name") + + " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); } else { - assertThat(sql.get(0)).contains("select t0.email, concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); + assertThat(sql.get(0)).contains("select t0.email, " + concat("t0.last_name", ", ", "t0.first_name") + + " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); } } @@ -172,14 +159,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase { LoggedSqlCollector.start(); - List contactDtos - = Ebean.find(Contact.class) - .select("id, email, concat(lastName,', ',firstName) as fullName") - .where().isNotNull("email").isNotNull("lastName") - .orderBy().asc("lastName") - .setMaxRows(10) - .asDto(ContactDto.class) - .findList(); + List contactDtos = Ebean.find(Contact.class) + .select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("email") + .isNotNull("lastName").orderBy().asc("lastName").setMaxRows(10).asDto(ContactDto.class).findList(); assertThat(contactDtos).isNotEmpty(); @@ -192,9 +174,12 @@ public class DtoQueryFromOrmTest extends BaseTestCase { List sql = LoggedSqlCollector.stop(); if (isSqlServer()) { - assertThat(sql.get(0)).contains("select top 10 t0.id, t0.email, concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); + assertThat(sql.get(0)).contains("select top 10 t0.id, t0.email, " + + concat("t0.last_name", ", ", "t0.first_name") + + " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); } else { - assertThat(sql.get(0)).contains("select t0.id, t0.email, concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); + assertThat(sql.get(0)).contains("select t0.id, t0.email, " + concat("t0.last_name", ", ", "t0.first_name") + + " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name"); } } @@ -205,15 +190,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase { LoggedSqlCollector.start(); - List contactDtos - = Ebean.find(Contact.class) - .select("concat(lastName,', ',firstName) as fullName") - .where().isNotNull("lastName") - .orderBy().asc("lastName") - .asDto(ContactDto.class) - .setFirstRow(2) - .setMaxRows(5) - .findList(); + List contactDtos = Ebean.find(Contact.class) + .select(concat("lastName", ", ", "firstName") + " as fullName").where().isNotNull("lastName").orderBy() + .asc("lastName").asDto(ContactDto.class).setFirstRow(2).setMaxRows(5).findList(); assertThat(contactDtos).isNotEmpty(); @@ -225,10 +204,10 @@ public class DtoQueryFromOrmTest extends BaseTestCase { } List sql = LoggedSqlCollector.stop(); - assertThat(sql.get(0)).contains("select concat(t0.last_name,', ',t0.first_name) fullName from contact t0 where"); + assertThat(sql.get(0)) + .contains("select " + concat("t0.last_name", ", ", "t0.first_name") + " fullName from contact t0 where"); } - @Test public void example_aggregate() { @@ -236,14 +215,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase { LoggedSqlCollector.start(); - List contactDtos - = Ebean.find(Contact.class) - .select("lastName, count(*) as totalCount") - .where().isNotNull("lastName") - .having().gt("count(*)", 1) - .orderBy().desc("count(*)") - .asDto(ContactTotals.class) - .findList(); + List contactDtos = Ebean.find(Contact.class).select("lastName, count(*) as totalCount").where() + .isNotNull("lastName").having().gt("count(*)", 1).orderBy().desc("count(*)").asDto(ContactTotals.class) + .findList(); assertThat(contactDtos).isNotEmpty(); @@ -253,7 +227,8 @@ public class DtoQueryFromOrmTest extends BaseTestCase { } List sql = LoggedSqlCollector.stop(); - assertThat(sql.get(0)).contains("select t0.last_name, count(*) totalCount from contact t0 where t0.last_name is not null group by t0.last_name having count(*) > ?"); + assertThat(sql.get(0)).contains( + "select t0.last_name, count(*) totalCount from contact t0 where t0.last_name is not null group by t0.last_name having count(*) > ?"); } @Test @@ -261,12 +236,8 @@ public class DtoQueryFromOrmTest extends BaseTestCase { ResetBasicData.reset(); - List contactDtos - = Ebean.find(Contact.class) - .select("lastName, count(*) as totalCount") - .where().isNotNull("lastName") - .asDto(ContactTotals.class) - .findList(); + List contactDtos = Ebean.find(Contact.class).select("lastName, count(*) as totalCount").where() + .isNotNull("lastName").asDto(ContactTotals.class).findList(); assertThat(contactDtos).isNotEmpty(); } diff --git a/src/test/java/io/ebean/SqlRowBooleanTest.java b/src/test/java/io/ebean/SqlRowBooleanTest.java index 3496b02cd..e5aa7d148 100644 --- a/src/test/java/io/ebean/SqlRowBooleanTest.java +++ b/src/test/java/io/ebean/SqlRowBooleanTest.java @@ -16,6 +16,8 @@ public class SqlRowBooleanTest extends BaseTestCase { sqlQuery = Ebean.createSqlQuery("SELECT 1 AS ISNT_NULL from dual"); } else if (isDb2()) { sqlQuery = Ebean.createSqlQuery("SELECT 1 AS ISNT_NULL from SYSIBM.SYSDUMMY1"); + } else if (isHana()) { + sqlQuery = Ebean.createSqlQuery("SELECT 1 AS ISNT_NULL from sys.dummy"); } else { sqlQuery = Ebean.createSqlQuery("SELECT 1 IS NOT NULL AS ISNT_NULL"); } diff --git a/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java b/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java new file mode 100644 index 000000000..c40ceaf41 --- /dev/null +++ b/src/test/java/io/ebean/config/dbplatform/HanaHistorySupportTest.java @@ -0,0 +1,48 @@ +package io.ebean.config.dbplatform; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import io.ebean.config.dbplatform.hana.HanaHistorySupport; + +public class HanaHistorySupportTest { + + private HanaHistorySupport support = new HanaHistorySupport(); + + @Test + public void getAsOfPredicate() { + + String asOfPredicate = support.getAsOfPredicate("t0", "sys_period"); + assertNull(asOfPredicate); + } + + @Test + public void getAsOfViewSuffix() { + + String asOfViewSuffix = support.getAsOfViewSuffix("_with_history"); + assertEquals(asOfViewSuffix, " for system_time as of ?"); + } + + @Test + public void getVersionsBetweenSuffix() { + + String asOfViewSuffix = support.getVersionsBetweenSuffix("_with_history"); + assertEquals(asOfViewSuffix, " for system_time between ? and ?"); + } + + @Test + public void getLower() throws Exception { + + String lower = support.getSysPeriodLower("t0", "sys_period"); + assertEquals(lower, "t0.sys_period_start"); + } + + @Test + public void getUpper() throws Exception { + + String upper = support.getSysPeriodUpper("t0", "sys_period"); + assertEquals(upper, "t0.sys_period_end"); + } +} diff --git a/src/test/java/io/ebean/config/dbplatform/HanaPlatformTest.java b/src/test/java/io/ebean/config/dbplatform/HanaPlatformTest.java new file mode 100644 index 000000000..948513a05 --- /dev/null +++ b/src/test/java/io/ebean/config/dbplatform/HanaPlatformTest.java @@ -0,0 +1,67 @@ +package io.ebean.config.dbplatform; + +import io.ebean.config.PlatformConfig; +import io.ebean.config.dbplatform.hana.HanaPlatform; +import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl; +import io.ebeaninternal.server.core.PlatformDdlBuilder; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HanaPlatformTest { + + HanaPlatform platform = new HanaPlatform(); + + @Test + public void testTypeConversion() { + + PlatformDdl ddl = PlatformDdlBuilder.create(platform); + + assertThat(ddl.convert("clob", false)).isEqualTo("nclob"); + assertThat(ddl.convert("blob", false)).isEqualTo("blob"); + assertThat(ddl.convert("json", false)).isEqualTo("nclob"); + assertThat(ddl.convert("jsonb", false)).isEqualTo("nclob"); + assertThat(ddl.convert("jsonvarchar", false)).isEqualTo("nvarchar(255)"); + + assertThat(ddl.convert("double", false)).isEqualTo("double"); + assertThat(ddl.convert("varchar(20)", false)).isEqualTo("nvarchar(20)"); + assertThat(ddl.convert("decimal(10)", false)).isEqualTo("decimal(10)"); + assertThat(ddl.convert("decimal(8,4)", false)).isEqualTo("decimal(8,4)"); + assertThat(ddl.convert("boolean", false)).isEqualTo("boolean"); + assertThat(ddl.convert("bit", false)).isEqualTo("smallint"); + assertThat(ddl.convert("tinyint", false)).isEqualTo("smallint"); + assertThat(ddl.convert("binary", false)).isEqualTo("varbinary(255)"); + assertThat(ddl.convert("binary(16)", false)).isEqualTo("varbinary(16)"); + + assertThat(ddl.convert("point", false)).isEqualTo("st_point"); + + assertThat(ddl.convert("multilinestring", false)).isEqualTo("st_geometry"); + assertThat(ddl.convert("multipolygon", false)).isEqualTo("st_geometry"); + assertThat(ddl.convert("multipoint", false)).isEqualTo("st_geometry"); + assertThat(ddl.convert("linestring", false)).isEqualTo("st_geometry"); + assertThat(ddl.convert("polygon", false)).isEqualTo("st_geometry"); + } + + @Test + public void uuid_default() { + + HanaPlatform platform = new HanaPlatform(); + platform.configure(new PlatformConfig()); + DbPlatformType dbType = platform.getDbTypeMap().get(DbPlatformType.UUID); + + assertThat(dbType.renderType(0, 0)).isEqualTo("varchar(40)"); + } + + @Test + public void uuid_as_binary() { + + HanaPlatform platform = new HanaPlatform(); + PlatformConfig config = new PlatformConfig(); + config.setDbUuid(PlatformConfig.DbUuid.AUTO_BINARY); + + platform.configure(config); + + DbPlatformType dbType = platform.getDbTypeMap().get(DbPlatformType.UUID); + assertThat(dbType.renderType(0, 0)).isEqualTo("varbinary(16)"); + } +} diff --git a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java index 635a9b9ac..27fda6d25 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java @@ -53,12 +53,14 @@ public class DbMigrationGenerateTest { migration.addPlatform(Platform.ORACLE, "oracle"); migration.addPlatform(Platform.SQLITE, "sqlite"); migration.addPlatform(Platform.SQLSERVER17, "sqlserver17"); + migration.addPlatform(Platform.HANA, "hana"); ServerConfig config = new ServerConfig(); config.setName("migrationtest"); config.loadFromProperties(); config.setRegister(false); config.setDefaultServer(false); + config.getProperties().put("ebean.hana.generateUniqueDdl", "true"); // need to generate unique statements to prevent them from being filtered out as duplicates by the DdlRunner config.setPackages(Arrays.asList("misc.migration.v1_0")); diff --git a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java index 218b2ced8..00b99161f 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java @@ -7,7 +7,6 @@ import io.ebean.SqlUpdate; import io.ebean.Transaction; import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; -import io.ebean.migration.MigrationConfig; import io.ebean.migration.ddl.DdlRunner; import io.ebeaninternal.dbmigration.ddlgeneration.Helper; @@ -90,7 +89,7 @@ public class DbMigrationTest extends BaseTestCase { runScript(false, "1.0__initial.sql"); - if (isOracle()) { + if (isOracle() || isHana()) { SqlUpdate update = server().createSqlUpdate("insert into migtest_e_basic (id, old_boolean, user_id) values (1, :false, 1)"); update.setParameter("false", false); assertThat(server().execute(update)).isEqualTo(1); @@ -199,6 +198,7 @@ public class DbMigrationTest extends BaseTestCase { for (String table : tables) { // simple and stupid try to execute all commands on all dialects. sb.append("alter table ").append(table).append(" set ( system_versioning = OFF );\n"); + sb.append("alter table ").append(table).append(" drop system versioning;\n"); sb.append("drop table ").append(table).append(";\n"); sb.append("drop table ").append(table).append(" cascade;\n"); sb.append("drop table ").append(table).append("_history;\n"); diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java index e185e8df7..bfb7b0b64 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -5,6 +5,7 @@ import io.ebean.Ebean; import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.postgres.PostgresPlatform; import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform; import io.ebeaninternal.api.SpiEbeanServer; @@ -16,7 +17,6 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; - public class BaseDdlHandlerTest extends BaseTestCase { private ServerConfig serverConfig = new ServerConfig(); @@ -37,6 +37,10 @@ public class BaseDdlHandlerTest extends BaseTestCase { return handler(new SqlServer17Platform()); } + private DdlHandler hanaHandler() { + return handler(new HanaPlatform()); + } + @Test public void addColumn_nullable_noConstraint() throws Exception { @@ -47,6 +51,10 @@ public class BaseDdlHandlerTest extends BaseTestCase { write = new DdlWrite(); sqlserverHandler().generate(write, Helper.getAddColumn()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add added_to_foo nvarchar(20);\n\n"); + + write = new DdlWrite(); + hanaHandler().generate(write, Helper.getAddColumn()); + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( added_to_foo nvarchar(20));\n\n"); } @Test @@ -56,10 +64,16 @@ public class BaseDdlHandlerTest extends BaseTestCase { h2Handler().generate(write, Helper.getAlterTableAddColumnWithCheckConstraint()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column status integer;\n" + "alter table foo add constraint ck_ordering_status check ( status in (0,1));\n\n"); + + write = new DdlWrite(); + hanaHandler().generate(write, Helper.getAlterTableAddColumnWithCheckConstraint()); + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( status integer);\n" + + "alter table foo add constraint ck_ordering_status check ( status in (0,1));\n\n"); } /** - * Test the functionality of the Ebean {@literal @}DbArray extension during DDL generation. + * Test the functionality of the Ebean {@literal @}DbArray extension during DDL + * generation. */ @Test public void addColumn_dbarray() throws Exception { @@ -76,6 +90,12 @@ public class BaseDdlHandlerTest extends BaseTestCase { DdlHandler sqlserverHandler = sqlserverHandler(); sqlserverHandler.generate(write, Helper.getAlterTableAddDbArrayColumn()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_added_to_foo varchar(1000);\n\n"); + + write = new DdlWrite(); + + DdlHandler hanaHandler = hanaHandler(); + hanaHandler.generate(write, Helper.getAlterTableAddDbArrayColumn()); + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_added_to_foo nvarchar(255) array);\n\n"); } @Test @@ -93,6 +113,10 @@ public class BaseDdlHandlerTest extends BaseTestCase { write = new DdlWrite(); sqlserverHandler().generate(write, Helper.getAlterTableAddDbArrayColumnWithLength()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_ninety varchar(90);\n\n"); + + write = new DdlWrite(); + hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnWithLength()); + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_ninety nvarchar(255) array(90));\n\n"); } @Test @@ -113,6 +137,14 @@ public class BaseDdlHandlerTest extends BaseTestCase { write = new DdlWrite(); sqlserverHandler().generate(write, Helper.getAlterTableAddDbArrayColumnInteger()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_integer varchar(1000);\n\n"); + + write = new DdlWrite(); + hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array(90));\n\n"); + + write = new DdlWrite(); + hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnInteger()); + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array);\n\n"); } @Test @@ -127,7 +159,8 @@ public class BaseDdlHandlerTest extends BaseTestCase { assertThat(buffer).contains("alter table foo add column some_id integer;"); String fkBuffer = write.applyForeignKeys().getBuffer(); - assertThat(fkBuffer).contains("alter table foo add constraint fk_foo_some_id foreign key (some_id) references bar (id) on delete restrict on update restrict;"); + assertThat(fkBuffer).contains( + "alter table foo add constraint fk_foo_some_id foreign key (some_id) references bar (id) on delete restrict on update restrict;"); assertThat(fkBuffer).contains("create index idx_foo_some_id on foo (some_id);"); assertThat(write.dropAll().getBuffer()).isEqualTo(""); } @@ -142,8 +175,15 @@ public class BaseDdlHandlerTest extends BaseTestCase { assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); assertThat(write.dropAll().getBuffer()).isEqualTo(""); - } + write = new DdlWrite(); + DdlHandler hanaHandler = hanaHandler(); + + hanaHandler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("CALL usp_ebean_drop_column('foo', 'col2');\n\n"); + assertThat(write.dropAll().getBuffer()).isEqualTo(""); + } @Test public void createTable() throws Exception { @@ -157,6 +197,16 @@ public class BaseDdlHandlerTest extends BaseTestCase { assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); assertThat(write.dropAll().getBuffer().trim()).isEqualTo("drop table if exists foo;"); + + write = new DdlWrite(); + DdlHandler hanaHandler = hanaHandler(); + + hanaHandler.generate(write, Helper.getCreateTable()); + + String createColumnTableDDL = Helper.asText(this, "/assert/create-column-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createColumnTableDDL); + assertThat(write.dropAll().getBuffer().trim()).isEqualTo("drop table foo cascade;"); } @Test @@ -174,7 +224,6 @@ public class BaseDdlHandlerTest extends BaseTestCase { assertThat(write.dropAll().getBuffer()).isEqualTo(rollbackLast); } - @Ignore @Test public void generateChangeSetFromModel() throws Exception { diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java new file mode 100644 index 000000000..911a28964 --- /dev/null +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java @@ -0,0 +1,42 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.junit.Test; + +import io.ebean.config.dbplatform.hana.HanaPlatform; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.migration.Column; + +public class HanaDdlTest { + + @Test + public void alterTableDropColumn() throws IOException { + HanaColumnStoreDdl ddl = new HanaColumnStoreDdl(new HanaPlatform()); + DdlWrite write = new DdlWrite(); + ddl.alterTableDropColumn(write.apply(), "my_table", "my_column"); + assertEquals("CALL usp_ebean_drop_column('my_table', 'my_column');\n", write.apply().getBuffer()); + } + + @Test + public void alterTableAddColumn() throws IOException { + HanaColumnStoreDdl ddl = new HanaColumnStoreDdl(new HanaPlatform()); + DdlWrite write = new DdlWrite(); + Column column = new Column(); + column.setName("my_column"); + column.setComment("comment"); + column.setDefaultValue("1"); + column.setNotnull(Boolean.TRUE); + column.setType("int"); + column.setUnique("unique"); + column.setPrimaryKey(Boolean.TRUE); + column.setCheckConstraint("CHECK(my_column > 0)"); + column.setCheckConstraintName("check_constraint"); + column.setHistoryExclude(Boolean.TRUE); + column.setIdentity(Boolean.TRUE); + ddl.alterTableAddColumn(write.apply(), "my_table", column, false, "1"); + assertEquals("alter table my_table add ( my_column int default 1 not null);\nalter table my_table add constraint check_constraint CHECK(my_column > 0);\n", write.apply().getBuffer()); + } +} diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java index d19d4cc28..7d5279990 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java @@ -4,6 +4,7 @@ import io.ebean.Ebean; import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.IdType; import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.oracle.OraclePlatform; import io.ebean.config.dbplatform.postgres.PostgresPlatform; @@ -16,6 +17,8 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class PlatformDdl_AlterColumnTest { @@ -24,6 +27,7 @@ public class PlatformDdl_AlterColumnTest { private PlatformDdl mysqlDdl = PlatformDdlBuilder.create(new MySqlPlatform()); private PlatformDdl oraDdl = PlatformDdlBuilder.create(new OraclePlatform()); private PlatformDdl sqlServerDdl = PlatformDdlBuilder.create(new SqlServer17Platform()); + private PlatformDdl hanaDdl = PlatformDdlBuilder.create(new HanaPlatform()); { ServerConfig serverConfig = Ebean.getDefaultServer().getPluginApi().getServerConfig(); @@ -63,6 +67,14 @@ public class PlatformDdl_AlterColumnTest { assertThat(pgDdl.convertArrayType("varchar[]")).isEqualTo("varchar[]"); assertThat(pgDdl.convertArrayType("integer[]")).isEqualTo("integer[]"); } + + @Test + public void convertArrayType_hana() { + assertThat(hanaDdl.convertArrayType("varchar[](90)")).isEqualTo("nvarchar(255) array(90)"); + assertThat(hanaDdl.convertArrayType("integer[](60)")).isEqualTo("integer array(60)"); + assertThat(hanaDdl.convertArrayType("varchar[]")).isEqualTo("nvarchar(255) array"); + assertThat(hanaDdl.convertArrayType("integer[]")).isEqualTo("integer array"); + } @Test public void testAlterColumnBaseAttributes() throws Exception { @@ -77,20 +89,32 @@ public class PlatformDdl_AlterColumnTest { sql = sqlServerDdl.alterColumnBaseAttributes(alterColumn); assertEquals("alter table mytab alter column acol nvarchar(5) not null", sql); + + sql = hanaDdl.alterColumnBaseAttributes(alterColumn); + assertEquals("alter table mytab alter ( acol nvarchar(5) not null)", sql); alterColumn.setNotnull(Boolean.FALSE); sql = mysqlDdl.alterColumnBaseAttributes(alterColumn); assertEquals("alter table mytab modify acol varchar(5)", sql); + + sql = hanaDdl.alterColumnBaseAttributes(alterColumn); + assertEquals("alter table mytab alter ( acol nvarchar(5))", sql); alterColumn.setNotnull(null); alterColumn.setType("varchar(100)"); sql = mysqlDdl.alterColumnBaseAttributes(alterColumn); assertEquals("alter table mytab modify acol varchar(100)", sql); + + sql = hanaDdl.alterColumnBaseAttributes(alterColumn); + assertEquals("alter table mytab alter ( acol nvarchar(100))", sql); alterColumn.setCurrentNotnull(Boolean.TRUE); sql = mysqlDdl.alterColumnBaseAttributes(alterColumn); assertEquals("alter table mytab modify acol varchar(100) not null", sql); + + sql = hanaDdl.alterColumnBaseAttributes(alterColumn); + assertEquals("alter table mytab alter ( acol nvarchar(100) not null)", sql); } @Test @@ -110,6 +134,9 @@ public class PlatformDdl_AlterColumnTest { sql = sqlServerDdl.alterColumnType("mytab", "acol", "varchar(20)"); assertNull(sql); + + sql = hanaDdl.alterColumnType("mytab", "acol", "varchar(20)"); + assertNull(sql); } @Test @@ -129,6 +156,9 @@ public class PlatformDdl_AlterColumnTest { sql = sqlServerDdl.alterColumnNotnull("mytab", "acol", true); assertNull(sql); + + sql = hanaDdl.alterColumnNotnull("mytab", "acol", true); + assertNull(sql); } @Test @@ -148,6 +178,9 @@ public class PlatformDdl_AlterColumnTest { sql = sqlServerDdl.alterColumnNotnull("mytab", "acol", false); assertNull(sql); + + sql = hanaDdl.alterColumnNotnull("mytab", "acol", false); + assertNull(sql); } @Test @@ -167,6 +200,15 @@ public class PlatformDdl_AlterColumnTest { sql = sqlServerDdl.alterColumnDefaultValue("mytab", "acol", "'hi'"); assertEquals("alter table mytab add default 'hi' for acol", sql); + + boolean exceptionCaught = false; + try { + hanaDdl.alterColumnDefaultValue("mytab", "acol", "'hi'"); + } + catch (UnsupportedOperationException e) { + exceptionCaught = true; + } + assertTrue(exceptionCaught); } @Test @@ -186,6 +228,15 @@ public class PlatformDdl_AlterColumnTest { sql = sqlServerDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT"); assertEquals("EXEC usp_ebean_drop_default_constraint mytab, acol", sql); + + boolean exceptionCaught = false; + try { + hanaDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT"); + } + catch (UnsupportedOperationException e) { + exceptionCaught = true; + } + assertTrue(exceptionCaught); } @Test @@ -226,4 +277,14 @@ public class PlatformDdl_AlterColumnTest { assertEquals(oraDdl.useIdentityType(IdentityType.GENERATOR), IdType.GENERATOR); assertEquals(oraDdl.useIdentityType(IdentityType.EXTERNAL), IdType.EXTERNAL); } + + @Test + public void useIdentityType_hana() { + + assertEquals(hanaDdl.useIdentityType(null), IdType.IDENTITY); + assertEquals(hanaDdl.useIdentityType(IdentityType.SEQUENCE), IdType.IDENTITY); + assertEquals(hanaDdl.useIdentityType(IdentityType.IDENTITY), IdType.IDENTITY); + assertEquals(hanaDdl.useIdentityType(IdentityType.GENERATOR), IdType.GENERATOR); + assertEquals(hanaDdl.useIdentityType(IdentityType.EXTERNAL), IdType.EXTERNAL); + } } diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java index e68ece07c..50bd331b6 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java @@ -1,6 +1,8 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; +import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.oracle.OraclePlatform; import io.ebean.config.dbplatform.postgres.PostgresPlatform; @@ -12,12 +14,12 @@ import static org.junit.Assert.assertEquals; public class PlatformDdl_dropUniqueConstraintTest { - private PlatformDdl h2Ddl = PlatformDdlBuilder.create(new H2Platform()); private PlatformDdl pgDdl = PlatformDdlBuilder.create(new PostgresPlatform()); private PlatformDdl mysqlDdl = PlatformDdlBuilder.create(new MySqlPlatform()); private PlatformDdl oraDdl = PlatformDdlBuilder.create(new OraclePlatform()); private PlatformDdl sqlServerDdl = PlatformDdlBuilder.create(new SqlServer17Platform()); + private PlatformDdl hanaDdl = PlatformDdlBuilder.create(new HanaPlatform()); @Test public void test() throws Exception { @@ -30,11 +32,22 @@ public class PlatformDdl_dropUniqueConstraintTest { assertEquals("alter table mytab drop constraint uq_name", sql); sql = sqlServerDdl.alterTableDropUniqueConstraint("mytab", "uq_name"); assertEquals("IF (OBJECT_ID('uq_name', 'UQ') IS NOT NULL) alter table mytab drop constraint uq_name;\n" - + "IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('mytab','U') AND name = 'uq_name') drop index uq_name ON mytab", sql); - + + "IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('mytab','U') AND name = 'uq_name') drop index uq_name ON mytab", + sql); sql = mysqlDdl.alterTableDropUniqueConstraint("mytab", "uq_name"); assertEquals("alter table mytab drop index uq_name", sql); + + ServerConfig serverConfig = new ServerConfig(); + hanaDdl.configure(serverConfig); + sql = hanaDdl.alterTableDropUniqueConstraint("mytab", "uq_name"); + assertEquals("delimiter $$\n" + + "do\n" + + "begin\n" + + "declare exit handler for sql_error_code 397 begin end;\n" + + "exec 'alter table mytab drop constraint uq_name';\n" + + "end;\n" + + "$$", sql); } } diff --git a/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java b/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java new file mode 100644 index 000000000..c67fe6de5 --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/expression/platform/HanaDbExpressionTest.java @@ -0,0 +1,70 @@ +package io.ebeaninternal.server.expression.platform; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import io.ebeaninternal.api.SpiExpressionRequest; +import io.ebeaninternal.server.expression.DefaultExpressionRequest; +import io.ebeaninternal.server.expression.Op; + +public class HanaDbExpressionTest { + private HanaDbExpression expression = new HanaDbExpression(); + + @Test + public void testArrayContains() { + SpiExpressionRequest request = new DefaultExpressionRequest(null); + expression.arrayContains(request, "arrayproperty", true, "v1", "v2", "v3"); + assertEquals("(? member of arrayproperty) and (? member of arrayproperty) and (? member of arrayproperty)", + request.getSql()); + } + + @Test + public void testArrayNotContains() { + SpiExpressionRequest request = new DefaultExpressionRequest(null); + expression.arrayContains(request, "arrayproperty", false, "v1", "v2", "v3"); + assertEquals( + "(? not member of arrayproperty) and (? not member of arrayproperty) and (? not member of arrayproperty)", + request.getSql()); + } + + @Test + public void testArrayContainsEmpty() { + SpiExpressionRequest request = new DefaultExpressionRequest(null); + expression.arrayContains(request, "arrayproperty", true); + assertEquals("", request.getSql()); + } + + @Test + public void testArrayIsEmpty() { + SpiExpressionRequest request = new DefaultExpressionRequest(null); + expression.arrayIsEmpty(request, "arrayproperty", true); + assertEquals("cardinality(arrayproperty) = 0", request.getSql()); + } + + @Test + public void testArrayIsNotEmpty() { + SpiExpressionRequest request = new DefaultExpressionRequest(null); + expression.arrayIsEmpty(request, "arrayproperty", false); + assertEquals("cardinality(arrayproperty) <> 0", request.getSql()); + } + + @Test + public void testConcat() { + String concat = expression.concat("property0", "separator", "property1", "suffix"); + assertEquals("concat(property0, 'separator'||property1||'suffix')", concat); + } + + @Test + public void testConcatNullSuffix() { + String concat = expression.concat("property0", "separator", "property1", null); + assertEquals("concat(property0, 'separator'||property1)", concat); + } + + @Test + public void testJson() { + SpiExpressionRequest request = new DefaultExpressionRequest(null); + expression.json(request, "jsonproperty", "path", Op.EQ, "val"); + assertEquals("json_value(jsonproperty, '$.path') = ? ", request.getSql()); + } +} diff --git a/src/test/java/io/ebeaninternal/server/grammer/EqlParserTest.java b/src/test/java/io/ebeaninternal/server/grammer/EqlParserTest.java index 0f09a79a1..ccf3aab1f 100644 --- a/src/test/java/io/ebeaninternal/server/grammer/EqlParserTest.java +++ b/src/test/java/io/ebeaninternal/server/grammer/EqlParserTest.java @@ -3,6 +3,9 @@ package io.ebeaninternal.server.grammer; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; +import io.ebean.annotation.ForPlatform; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; import io.ebeaninternal.api.SpiQuery; import org.junit.Test; import org.tests.model.basic.Customer; @@ -11,6 +14,8 @@ import org.tests.model.basic.ResetBasicData; import java.util.Arrays; import java.util.List; +import javax.xml.ws.RequestWrapper; + import static org.assertj.core.api.Assertions.assertThat; public class EqlParserTest extends BaseTestCase { @@ -127,6 +132,7 @@ public class EqlParserTest extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) // The HANA JDBC driver checks the field length on binding and rejects 'NEW' public void where_or1() { Query query = parse("where name = 'Rob' or (status = 'NEW' and smallnote is null)"); @@ -134,8 +140,19 @@ public class EqlParserTest extends BaseTestCase { assertThat(query.getGeneratedSql()).contains("where (t0.name = ? or (t0.status = ? and t0.smallnote is null ) )"); } + + @Test + @ForPlatform(Platform.HANA) + public void where_or1_hana() { + + Query query = parse("where name = 'Rob' or (status = 'N' and smallnote is null)"); + query.findList(); + + assertThat(query.getGeneratedSql()).contains("where (t0.name = ? or (t0.status = ? and t0.smallnote is null ) )"); + } @Test + @IgnorePlatform(Platform.HANA) // The HANA JDBC driver checks the field length on binding and rejects 'NEW' public void where_or2() { Query query = parse("where (name = 'Rob' or status = 'NEW') and smallnote is null"); @@ -143,8 +160,19 @@ public class EqlParserTest extends BaseTestCase { assertThat(query.getGeneratedSql()).contains("where ((t0.name = ? or t0.status = ? ) and t0.smallnote is null )"); } + + @Test + @ForPlatform(Platform.HANA) + public void where_or2_hana() { + + Query query = parse("where (name = 'Rob' or status = 'N') and smallnote is null"); + query.findList(); + + assertThat(query.getGeneratedSql()).contains("where ((t0.name = ? or t0.status = ? ) and t0.smallnote is null )"); + } @Test + @IgnorePlatform(Platform.HANA) // The HANA JDBC driver checks the field length on binding and rejects 'NEW' public void test_simplifyExpressions() { Query query = parse("where not (name = 'Rob' and status = 'NEW')"); @@ -159,6 +187,23 @@ public class EqlParserTest extends BaseTestCase { query.findList(); assertThat(query.getGeneratedSql()).contains("where not (t0.name = ? and t0.status = ? )"); } + + @Test + @ForPlatform(Platform.HANA) + public void test_simplifyExpressions_hana() { + + Query query = parse("where not (name = 'Rob' and status = 'N')"); + query.findList(); + assertThat(query.getGeneratedSql()).contains("where not (t0.name = ? and t0.status = ? )"); + + query = parse("where not ((name = 'Rob' and status = 'N'))"); + query.findList(); + assertThat(query.getGeneratedSql()).contains("where not (t0.name = ? and t0.status = ? )"); + + query = parse("where not (((name = 'Rob') and (status = 'N')))"); + query.findList(); + assertThat(query.getGeneratedSql()).contains("where not (t0.name = ? and t0.status = ? )"); + } @Test diff --git a/src/test/java/org/tests/basic/TestErrorBindLog.java b/src/test/java/org/tests/basic/TestErrorBindLog.java index 0b90f485a..75b1bd7c5 100644 --- a/src/test/java/org/tests/basic/TestErrorBindLog.java +++ b/src/test/java/org/tests/basic/TestErrorBindLog.java @@ -18,7 +18,12 @@ public class TestErrorBindLog extends BaseTestCase { } catch (PersistenceException e) { String msg = e.getMessage(); - Assert.assertTrue(msg.contains("Bind values:")); + if (isHana()) { + Assert.assertTrue(msg.contains("Error with property[1] dt[12]data[JUNK]")); + } + else { + Assert.assertTrue(msg.contains("Bind values:")); + } } } } diff --git a/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java b/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java index 8bc76c951..c818a3287 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java +++ b/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java @@ -4,7 +4,9 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; import io.ebean.annotation.Transactional; import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaTimedMetric; @@ -119,6 +121,7 @@ public class TestBatchInsertFlush extends BaseTestCase { @Test @Transactional(batch = PersistBatch.ALL) + @IgnorePlatform(Platform.HANA) public void transactional_flushOnGetId() { EbeanServer server = Ebean.getDefaultServer(); @@ -141,6 +144,7 @@ public class TestBatchInsertFlush extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) public void testFlushOnGetId() { EbeanServer server = Ebean.getDefaultServer(); diff --git a/src/test/java/org/tests/batchinsert/TestBatchInsertSimple.java b/src/test/java/org/tests/batchinsert/TestBatchInsertSimple.java index 8245f31ea..497b9d137 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchInsertSimple.java +++ b/src/test/java/org/tests/batchinsert/TestBatchInsertSimple.java @@ -3,7 +3,9 @@ package org.tests.batchinsert; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; import io.ebean.annotation.Transactional; import org.junit.Test; import org.tests.model.basic.UTDetail; @@ -46,6 +48,7 @@ public class TestBatchInsertSimple extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) public void testTransactional() { saveWithFullBatchMode(); @@ -131,7 +134,7 @@ public class TestBatchInsertSimple extends BaseTestCase { Transaction transaction = Ebean.beginTransaction(); try { transaction.setBatch(PersistBatch.NONE); - transaction.setBatchOnCascade(PersistBatch.ALL); + transaction.setBatchOnCascade(spiEbeanServer().getDatabasePlatform().getPersistBatchOnCascade()); transaction.setBatchSize(20); // escalate based on batchOnCascade value diff --git a/src/test/java/org/tests/batchinsert/TestBatchOnCascadeExceptionHandling.java b/src/test/java/org/tests/batchinsert/TestBatchOnCascadeExceptionHandling.java index 568dd0e8b..d28dcc974 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchOnCascadeExceptionHandling.java +++ b/src/test/java/org/tests/batchinsert/TestBatchOnCascadeExceptionHandling.java @@ -3,7 +3,9 @@ package org.tests.batchinsert; import io.ebean.BaseTestCase; import io.ebean.EbeanServer; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; import io.ebeaninternal.api.SpiTransaction; import io.ebeaninternal.server.persist.BatchControl; import org.tests.model.basic.EBasicWithUniqueCon; @@ -20,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class TestBatchOnCascadeExceptionHandling extends BaseTestCase { + @IgnorePlatform(Platform.HANA) @Test public void testBatchScenarioWithSavepoint() throws SQLException { server().save(createEntityWithName("conflict", "before")); @@ -84,7 +87,7 @@ public class TestBatchOnCascadeExceptionHandling extends BaseTestCase { Transaction txn = server.beginTransaction(); try { assertThat(txn.getBatch()).isSameAs(PersistBatch.NONE); - assertThat(txn.getBatchOnCascade()).isSameAs(PersistBatch.ALL); + assertThat(txn.getBatchOnCascade()).isSameAs(spiEbeanServer().getDatabasePlatform().getPersistBatchOnCascade()); failingOperation.run(); Assertions.fail("PersistenceException expected"); diff --git a/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java b/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java index 2fab37ff9..3b3230581 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java +++ b/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java @@ -3,6 +3,8 @@ package org.tests.batchinsert; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.EbeanServer; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; import io.ebean.annotation.Transactional; import org.tests.model.basic.Customer; import org.junit.Test; @@ -18,6 +20,7 @@ public class TestBatchSaveWithGetBeanId extends BaseTestCase { */ @Transactional(batchSize = 10) @Test + @IgnorePlatform(Platform.HANA) // HANA doesn't support insert batching public void test() { EbeanServer server = Ebean.getDefaultServer(); diff --git a/src/test/java/org/tests/compositekeys/db/Type.java b/src/test/java/org/tests/compositekeys/db/Type.java index 0b5784b8a..298d6bc4b 100644 --- a/src/test/java/org/tests/compositekeys/db/Type.java +++ b/src/test/java/org/tests/compositekeys/db/Type.java @@ -6,10 +6,12 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.Table; import javax.persistence.Version; import java.util.List; @Entity +@Table(name="`type`") // needs to be quoted because it's a keyword on HANA public class Type { @Id private TypeKey key; diff --git a/src/test/java/org/tests/merge/TestMergeBasic.java b/src/test/java/org/tests/merge/TestMergeBasic.java index 03938fa71..d02861c5e 100644 --- a/src/test/java/org/tests/merge/TestMergeBasic.java +++ b/src/test/java/org/tests/merge/TestMergeBasic.java @@ -59,12 +59,23 @@ public class TestMergeBasic extends BaseTestCase { assertThat(sql.get(2)).contains("update uuone set name=?, description=?, version=? where id=? and version=?"); // persist children ... - assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); - assertThat(sql.get(4)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + assertThat(sql.get(4)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); - assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); - assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); - assertThat(sql.get(7)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(7)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + } + else { + assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + + assertThat(sql.get(4)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + + assertThat(sql.get(7)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + } } @Test @@ -97,12 +108,23 @@ public class TestMergeBasic extends BaseTestCase { assertThat(sql.get(2)).contains("update uuone set name=?, description=?, version=? where id=? and version=?"); // persist children ... - assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); - assertThat(sql.get(4)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + assertThat(sql.get(4)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + + assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(7)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + } + else { + assertThat(sql.get(3)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); - assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); - assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); - assertThat(sql.get(7)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(4)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(5)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + assertThat(sql.get(6)).contains("update uutwo set name=?, notes=?, version=?, master_id=? where id=? and version=?"); + + assertThat(sql.get(7)).contains("insert into uutwo (id, name, notes, version, master_id) values (?,?,?,?,?);"); + } } diff --git a/src/test/java/org/tests/merge/TestMergeCustomer.java b/src/test/java/org/tests/merge/TestMergeCustomer.java index c796d702b..6068d034d 100644 --- a/src/test/java/org/tests/merge/TestMergeCustomer.java +++ b/src/test/java/org/tests/merge/TestMergeCustomer.java @@ -277,10 +277,18 @@ public class TestMergeCustomer extends BaseTestCase { assertThat(sql.get(2)).contains("delete from mcontact where id=?"); assertThat(sql.get(5)).contains("update mcustomer set name=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?"); - assertThat(sql.get(6)).contains("insert into mcontact"); - assertThat(sql.get(7)).contains("insert into mcontact"); - assertThat(sql.get(8)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); - assertThat(sql.get(11)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(6)).contains("insert into mcontact"); + assertThat(sql.get(7)).contains("insert into mcontact"); + assertThat(sql.get(8)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + assertThat(sql.get(11)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + } + else { + assertThat(sql.get(6)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + assertThat(sql.get(7)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + assertThat(sql.get(10)).contains("insert into mcontact"); + assertThat(sql.get(11)).contains("insert into mcontact"); + } } @Test @@ -305,7 +313,7 @@ public class TestMergeCustomer extends BaseTestCase { List sql = LoggedSqlCollector.stop(); assertThat(sql.get(0)).contains("select t0.id, t3.id, t1.id, t2.id from mcustomer t0 left join maddress t3 on t3.id = t0.shipping_address_id left join maddress t1 on t1.id = t0.billing_address_id left join mcontact t2 on t2.customer_id = t0.id where t0.id = ?"); - if (isH2()) { + if (isH2() || isHana()) { // with nested OneToMany .. we need a second query to read the contact message ids assertThat(sql.get(1)).contains("select t0.contact_id, t0.id from mcontact_message t0 where (t0.contact_id) in (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"); } @@ -317,10 +325,18 @@ public class TestMergeCustomer extends BaseTestCase { assertThat(sql.get(6)).contains("update maddress set street=?, city=?, version=? where id=? and version=?"); assertThat(sql.get(7)).contains("update mcustomer set name=?, notes=?, version=?, shipping_address_id=?, billing_address_id=? where id=? and version=?"); - assertThat(sql.get(8)).contains("insert into mcontact"); - assertThat(sql.get(9)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); - - assertThat(sql.get(13)).contains("update mcontact_message set title=?, subject=?, notes=?, version=?, contact_id=? where id=? and version=?"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(8)).contains("insert into mcontact"); + assertThat(sql.get(9)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + + assertThat(sql.get(13)).contains("update mcontact_message set title=?, subject=?, notes=?, version=?, contact_id=? where id=? and version=?"); + } + else { + assertThat(sql.get(8)).contains("update mcontact set email=?, first_name=?, last_name=?, version=?, customer_id=? where id=? and version=?"); + assertThat(sql.get(9)).contains("update mcontact_message set title=?, subject=?, notes=?, version=?, contact_id=? where id=? and version=?"); + + assertThat(sql.get(sql.size()-1)).contains("insert into mcontact"); + } } diff --git a/src/test/java/org/tests/merge/TestMergeM2M.java b/src/test/java/org/tests/merge/TestMergeM2M.java index 930d2def1..784e53025 100644 --- a/src/test/java/org/tests/merge/TestMergeM2M.java +++ b/src/test/java/org/tests/merge/TestMergeM2M.java @@ -40,10 +40,20 @@ public class TestMergeM2M extends BaseTestCase { Ebean.merge(machine, options); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); - assertThat(sql.get(0)).contains("select"); - assertThat(sql.get(1)).contains("insert into mmachine"); - assertThat(sql.get(2)).contains("insert into mmachine_mgroup"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("select"); + assertThat(sql.get(1)).contains("insert into mmachine"); + assertThat(sql.get(2)).contains("insert into mmachine_mgroup"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("select"); + assertThat(sql.get(1)).contains("insert into mmachine"); + assertThat(sql.get(2)).contains("insert into mmachine_mgroup"); + assertThat(sql.get(3)).contains("insert into mmachine_mgroup"); + assertThat(sql.get(4)).contains("insert into mmachine_mgroup"); + } machine.setName("mac1-mod"); machine.getGroups().remove(group2); diff --git a/src/test/java/org/tests/model/array/TestDbArray_asSet.java b/src/test/java/org/tests/model/array/TestDbArray_asSet.java index ac970b2e4..b87726c53 100644 --- a/src/test/java/org/tests/model/array/TestDbArray_asSet.java +++ b/src/test/java/org/tests/model/array/TestDbArray_asSet.java @@ -3,6 +3,9 @@ package org.tests.model.array; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -22,6 +25,7 @@ public class TestDbArray_asSet extends BaseTestCase { private EArraySetBean found; @Test + @IgnorePlatform(Platform.HANA) public void insert() { bean.setName("some stuff"); @@ -122,6 +126,7 @@ public class TestDbArray_asSet extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) public void insertNulls() { EArraySetBean bean = new EArraySetBean(); @@ -135,6 +140,7 @@ public class TestDbArray_asSet extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) public void insertAll_when_hasNulls() { EArraySetBean bean = new EArraySetBean(); diff --git a/src/test/java/org/tests/model/array/TestDbArray_basic.java b/src/test/java/org/tests/model/array/TestDbArray_basic.java index 6a3e938c5..4da3ada4a 100644 --- a/src/test/java/org/tests/model/array/TestDbArray_basic.java +++ b/src/test/java/org/tests/model/array/TestDbArray_basic.java @@ -17,6 +17,8 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; import io.ebean.SqlRow; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; public class TestDbArray_basic extends BaseTestCase { @@ -25,6 +27,7 @@ public class TestDbArray_basic extends BaseTestCase { private EArrayBean found; @Test + @IgnorePlatform(Platform.HANA) public void insert() throws SQLException { bean.setName("some stuff"); @@ -159,6 +162,7 @@ public class TestDbArray_basic extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) public void insertNulls() { EArrayBean bean = new EArrayBean(); @@ -172,6 +176,7 @@ public class TestDbArray_basic extends BaseTestCase { } @Test + @IgnorePlatform(Platform.HANA) public void insertAll_when_hasNulls() { EArrayBean bean = new EArrayBean(); 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 9a42ea601..34a484ce9 100644 --- a/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java +++ b/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java @@ -280,6 +280,8 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase { assertThat(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()) { assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||'-'||t0.code)"); + } else if (isHana()) { + assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku, '-'||t0.code)"); } else { assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku,'-',t0.code)"); } @@ -319,6 +321,8 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase { assertThat(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()){ assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||':'||t0.code||'-foo')"); + } else if (isHana()){ + assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and concat(t0.sku, ':'||t0.code||'-foo')"); } else { assertThat(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/basic/xtra/TestInsertBatchThenFlushThenUpdate.java b/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenFlushThenUpdate.java index 59b29a499..b10abc2df 100644 --- a/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenFlushThenUpdate.java +++ b/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenFlushThenUpdate.java @@ -3,7 +3,10 @@ package org.tests.model.basic.xtra; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -16,6 +19,7 @@ import static org.junit.Assert.assertTrue; public class TestInsertBatchThenFlushThenUpdate extends BaseTestCase { @Test + @IgnorePlatform(Platform.HANA) public void test() { LoggedSqlCollector.start(); diff --git a/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenUpdate.java b/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenUpdate.java index 08d86c0de..cc2dd8185 100644 --- a/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenUpdate.java +++ b/src/test/java/org/tests/model/basic/xtra/TestInsertBatchThenUpdate.java @@ -3,7 +3,10 @@ package org.tests.model.basic.xtra; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -16,6 +19,7 @@ import static org.junit.Assert.assertEquals; public class TestInsertBatchThenUpdate extends BaseTestCase { @Test + @IgnorePlatform(Platform.HANA) public void test() { LoggedSqlCollector.start(); @@ -60,6 +64,7 @@ public class TestInsertBatchThenUpdate extends BaseTestCase { @Test + @IgnorePlatform(Platform.HANA) public void test_noFlushOn_getterOfNonGeneratedProperty() { LoggedSqlCollector.start(); diff --git a/src/test/java/org/tests/model/basic/xtra/TestInsertBatchWithDifferentRootTypes.java b/src/test/java/org/tests/model/basic/xtra/TestInsertBatchWithDifferentRootTypes.java index 487e98a09..da54417e1 100644 --- a/src/test/java/org/tests/model/basic/xtra/TestInsertBatchWithDifferentRootTypes.java +++ b/src/test/java/org/tests/model/basic/xtra/TestInsertBatchWithDifferentRootTypes.java @@ -3,7 +3,10 @@ package org.tests.model.basic.xtra; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -15,6 +18,7 @@ import static org.junit.Assert.assertEquals; public class TestInsertBatchWithDifferentRootTypes extends BaseTestCase { @Test + @IgnorePlatform(Platform.HANA) public void testDifferRootTypes() { LoggedSqlCollector.start(); diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasic.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasic.java index 0dea97952..bcbb642f8 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasic.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasic.java @@ -29,9 +29,17 @@ public class TestElementCollectionBasic extends BaseTestCase { assertThat(eventLog()).containsExactly("preInsert", "postInsert"); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("insert into ec_person"); - assertThat(sql.get(1)).contains("insert into ec_person_phone"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into ec_person"); + assertThat(sql.get(1)).contains("insert into ec_person_phone"); + } + else { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("insert into ec_person"); + assertThat(sql.get(1)).contains("insert into ec_person_phone"); + assertThat(sql.get(2)).contains("insert into ec_person_phone"); + } EcPerson person1 = new EcPerson("Fiona09"); person1.getPhoneNumbers().add("09 1234"); @@ -121,10 +129,20 @@ public class TestElementCollectionBasic extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); - assertThat(sql.get(0)).contains("update ec_person set name=?, version=? where id=? and version=?"); - assertThat(sql.get(1)).contains("delete from ec_person_phone where owner_id=?"); - assertThat(sql.get(2)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("update ec_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ec_person_phone where owner_id=?"); + assertThat(sql.get(2)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("update ec_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ec_person_phone where owner_id=?"); + assertThat(sql.get(2)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(3)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(4)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + } assertThat(eventLog()).containsExactly("preUpdate", "postUpdate"); @@ -189,9 +207,21 @@ public class TestElementCollectionBasic extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("delete from ec_person_phone where owner_id=?"); - assertThat(sql.get(1)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("delete from ec_person_phone where owner_id=?"); + assertThat(sql.get(1)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + } + else { + assertThat(sql).hasSize(7); + assertThat(sql.get(0)).contains("delete from ec_person_phone where owner_id=?"); + assertThat(sql.get(1)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(2)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(3)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(4)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(5)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + assertThat(sql.get(6)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)"); + } assertThat(eventLog()).containsExactly("preUpdate", "postUpdate"); diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java index a73220ce1..13a41e66d 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java @@ -1,6 +1,9 @@ package org.tests.model.elementcollection; import io.ebean.Ebean; +import io.ebean.annotation.PersistBatch; +import io.ebeaninternal.api.SpiEbeanServer; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -39,7 +42,12 @@ public class TestElementCollectionBasicCache { Ebean.save(two); sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + } + else { + assertThat(sql).hasSize(5); + } Ebean.save(two); @@ -60,7 +68,12 @@ public class TestElementCollectionBasicCache { Ebean.save(three); sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); // cache hit + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); // cache hit + } + else { + assertThat(sql).hasSize(5); // cache hit + } EcPerson four = Ebean.find(EcPerson.class) .setId(person.getId()) @@ -77,4 +90,8 @@ public class TestElementCollectionBasicCache { LoggedSqlCollector.stop(); } + + public boolean isPersistBatchOnCascade() { + return ((SpiEbeanServer) Ebean.getDefaultServer()).getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE; + } } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java index a2e181892..3ae0ddb19 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMap.java @@ -2,6 +2,8 @@ package org.tests.model.elementcollection; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import io.ebean.annotation.PersistBatch; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -24,9 +26,17 @@ public class TestElementCollectionBasicMap extends BaseTestCase { Ebean.save(person); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("insert into ecm_person"); - assertThat(sql.get(1)).contains("insert into ecm_person_phone"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into ecm_person"); + assertThat(sql.get(1)).contains("insert into ecm_person_phone"); + } + else { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("insert into ecm_person"); + assertThat(sql.get(1)).contains("insert into ecm_person_phone"); + assertThat(sql.get(2)).contains("insert into ecm_person_phone"); + } EcmPerson person1 = new EcmPerson("Fiona09"); person1.getPhoneNumbers().put("home", "09 1234"); @@ -96,10 +106,20 @@ public class TestElementCollectionBasicMap extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); - assertThat(sql.get(0)).contains("update ecm_person set name=?, version=? where id=? and version=?"); - assertThat(sql.get(1)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?"); - assertThat(sql.get(2)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("update ecm_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?"); + assertThat(sql.get(2)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("update ecm_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?"); + assertThat(sql.get(2)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + assertThat(sql.get(3)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + assertThat(sql.get(4)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + } updateNothing(bean); } @@ -120,9 +140,19 @@ public class TestElementCollectionBasicMap extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?"); - assertThat(sql.get(1)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?"); + assertThat(sql.get(1)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?"); + assertThat(sql.get(1)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + assertThat(sql.get(2)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + assertThat(sql.get(3)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + assertThat(sql.get(4)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)"); + } delete(bean); } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java index 137e8e495..e92ba9324 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java @@ -1,6 +1,9 @@ package org.tests.model.elementcollection; import io.ebean.Ebean; +import io.ebean.annotation.PersistBatch; +import io.ebeaninternal.api.SpiEbeanServer; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -41,7 +44,12 @@ public class TestElementCollectionBasicMapCache { Ebean.save(two); sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + } + else { + assertThat(sql).hasSize(5); + } Ebean.save(two); @@ -64,7 +72,12 @@ public class TestElementCollectionBasicMapCache { Ebean.save(three); sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); // cache hit + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); // cache hit + } + else { + assertThat(sql).hasSize(3); // cache hit + } EcmPerson four = Ebean.find(EcmPerson.class) .setId(person.getId()) @@ -82,4 +95,8 @@ public class TestElementCollectionBasicMapCache { LoggedSqlCollector.stop(); } + + public boolean isPersistBatchOnCascade() { + return ((SpiEbeanServer) Ebean.getDefaultServer()).getDatabasePlatform().getPersistBatchOnCascade() != PersistBatch.NONE; + } } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicSet.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicSet.java index 2d3124351..2d240671a 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicSet.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicSet.java @@ -23,9 +23,17 @@ public class TestElementCollectionBasicSet extends BaseTestCase { Ebean.save(person); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("insert into ecs_person"); - assertThat(sql.get(1)).contains("insert into ecs_person_phone"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into ecs_person"); + assertThat(sql.get(1)).contains("insert into ecs_person_phone"); + } + else { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("insert into ecs_person"); + assertThat(sql.get(1)).contains("insert into ecs_person_phone"); + assertThat(sql.get(2)).contains("insert into ecs_person_phone"); + } EcsPerson person1 = new EcsPerson("Fiona09"); person1.getPhoneNumbers().add("09 1234"); @@ -94,10 +102,20 @@ public class TestElementCollectionBasicSet extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); - assertThat(sql.get(0)).contains("update ecs_person set name=?, version=? where id=? and version=?"); - assertThat(sql.get(1)).contains("delete from ecs_person_phone where ecs_person_id=?"); - assertThat(sql.get(2)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("update ecs_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecs_person_phone where ecs_person_id=?"); + assertThat(sql.get(2)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("update ecs_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecs_person_phone where ecs_person_id=?"); + assertThat(sql.get(2)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + assertThat(sql.get(3)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + assertThat(sql.get(4)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + } updateNothing(bean); } @@ -118,9 +136,19 @@ public class TestElementCollectionBasicSet extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("delete from ecs_person_phone where ecs_person_id=?"); - assertThat(sql.get(1)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("delete from ecs_person_phone where ecs_person_id=?"); + assertThat(sql.get(1)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("delete from ecs_person_phone where ecs_person_id=?"); + assertThat(sql.get(1)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + assertThat(sql.get(2)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + assertThat(sql.get(3)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + assertThat(sql.get(4)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)"); + } delete(bean); } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedList.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedList.java index 8b86e6477..8d4cfbd0c 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedList.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedList.java @@ -22,9 +22,17 @@ public class TestElementCollectionEmbeddedList extends BaseTestCase { Ebean.save(person); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("insert into ecbl_person"); - assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into ecbl_person"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers"); + } + else { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("insert into ecbl_person"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers"); + assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers"); + } EcblPerson person1 = new EcblPerson("Fiona6409"); person1.getPhoneNumbers().add(new EcPhone("61","09","1234")); @@ -94,10 +102,20 @@ public class TestElementCollectionEmbeddedList extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); - assertThat(sql.get(0)).contains("update ecbl_person set name=?, version=? where id=? and version=?"); - assertThat(sql.get(1)).contains("delete from ecbl_person_phone_numbers where person_id=?"); - assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("update ecbl_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("update ecbl_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + assertThat(sql.get(3)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + assertThat(sql.get(4)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + } updateNothing(bean); } @@ -118,9 +136,19 @@ public class TestElementCollectionEmbeddedList extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); - assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + assertThat(sql.get(3)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + assertThat(sql.get(4)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + } delete(bean); } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java index c9cbaf4d4..cd236e4eb 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java @@ -2,6 +2,8 @@ package org.tests.model.elementcollection; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import io.ebean.annotation.PersistBatch; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -47,9 +49,17 @@ public class TestElementCollectionEmbeddedListCache extends BaseTestCase { Ebean.save(two); sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); // update of collection only - assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); - assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); // update of collection only + assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + } + else { + assertThat(sql).hasSize(3); // update of collection only + assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + } EcblPerson three = Ebean.find(EcblPerson.class) .setId(person.getId()) diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java index 50b37480c..4a6419c0e 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java @@ -23,9 +23,17 @@ public class TestElementCollectionEmbeddedMap extends BaseTestCase { Ebean.save(person); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("insert into ecbm_person"); - assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into ecbm_person"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers"); + } + else { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("insert into ecbm_person"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers"); + assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers"); + } EcbmPerson person1 = new EcbmPerson("Fiona6409"); person1.getPhoneNumbers().put("home",new EcPhone("64","09","1234")); @@ -87,10 +95,20 @@ public class TestElementCollectionEmbeddedMap extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(3); - assertThat(sql.get(0)).contains("update ecbm_person set name=?, version=? where id=? and version=?"); - assertThat(sql.get(1)).contains("delete from ecbm_person_phone_numbers where person_id=?"); - assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("update ecbm_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("update ecbm_person set name=?, version=? where id=? and version=?"); + assertThat(sql.get(1)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number)"); + assertThat(sql.get(3)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number)"); + assertThat(sql.get(4)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number)"); + } updateNothing(bean); } @@ -111,9 +129,19 @@ public class TestElementCollectionEmbeddedMap extends BaseTestCase { Ebean.save(bean); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); - assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + } + else { + assertThat(sql).hasSize(5); + assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + assertThat(sql.get(3)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + assertThat(sql.get(4)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + } delete(bean); } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java index 1c29bfc55..4aa75ed22 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java @@ -48,9 +48,17 @@ public class TestElementCollectionEmbeddedMapCache extends BaseTestCase { Ebean.save(two); sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(2); // update of collection only - assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); - assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(2); // update of collection only + assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + } + else { + assertThat(sql).hasSize(3); // update of collection only + assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + } EcbmPerson three = Ebean.find(EcbmPerson.class) .setId(person.getId()) diff --git a/src/test/java/org/tests/model/m2o/TestManyToOneAsOne.java b/src/test/java/org/tests/model/m2o/TestManyToOneAsOne.java index b7d1cc7b6..eb993da93 100644 --- a/src/test/java/org/tests/model/m2o/TestManyToOneAsOne.java +++ b/src/test/java/org/tests/model/m2o/TestManyToOneAsOne.java @@ -18,7 +18,7 @@ public class TestManyToOneAsOne extends BaseTestCase { @Transactional(batchSize = 20) @Test - @IgnorePlatform({Platform.SQLSERVER, Platform.ORACLE}) // probably due the use of sequences - Empl has already an ID and Addr refers to it. + @IgnorePlatform({Platform.SQLSERVER, Platform.ORACLE, Platform.HANA}) // probably due the use of sequences - Empl has already an ID and Addr refers to it. public void test_when_jdbcBatch() { runInserts(); } diff --git a/src/test/java/org/tests/model/onetoone/album/DeleteById_SoftDelete_Tests.java b/src/test/java/org/tests/model/onetoone/album/DeleteById_SoftDelete_Tests.java index bb27d3c5a..5beea394f 100644 --- a/src/test/java/org/tests/model/onetoone/album/DeleteById_SoftDelete_Tests.java +++ b/src/test/java/org/tests/model/onetoone/album/DeleteById_SoftDelete_Tests.java @@ -216,7 +216,12 @@ public class DeleteById_SoftDelete_Tests extends BaseTestCase { List sql = LoggedSqlCollector.stop(); assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?"); + } + else { + assertThat(sql.get(0)).contains("update cover set deleted=? where id=?"); + } } @Test @@ -232,7 +237,12 @@ public class DeleteById_SoftDelete_Tests extends BaseTestCase { List sql = LoggedSqlCollector.stop(); assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?"); + } + else { + assertThat(sql.get(0)).contains("update cover set deleted=? where id=?"); + } } @Test @@ -254,7 +264,12 @@ public class DeleteById_SoftDelete_Tests extends BaseTestCase { List sql = LoggedSqlCollector.stop(); assertThat(sql).hasSize(2); - assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?"); + if (isPersistBatchOnCascade()) { + assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?"); + } + else { + assertThat(sql.get(0)).contains("update cover set deleted=? where id=?"); + } } @Test diff --git a/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java b/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java index dd7f6b6c1..1a377b7e5 100644 --- a/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java +++ b/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTable.java @@ -2,10 +2,13 @@ package org.tests.o2m.jointable; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import io.ebean.annotation.PersistBatch; + import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -37,8 +40,15 @@ public class TestOneToManyJoinTable extends BaseTestCase { Ebean.save(troop); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(1); - assertThat(sql.get(0)).contains("insert into troop_monkey (troop_pid, monkey_mid) values (?, ?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(1); + assertThat(sql.get(0)).contains("insert into troop_monkey (troop_pid, monkey_mid) values (?, ?)"); + } + else { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into troop_monkey (troop_pid, monkey_mid) values (?, ?)"); + assertThat(sql.get(1)).contains("insert into troop_monkey (troop_pid, monkey_mid) values (?, ?)"); + } int intersectionRows = Ebean.createSqlQuery("select count(*) as total from troop_monkey where troop_pid = ?") .setParameter(1, troop.getPid()) @@ -87,12 +97,23 @@ public class TestOneToManyJoinTable extends BaseTestCase { Ebean.save(trainer); List sql = LoggedSqlCollector.current(); + //Collections.sort(sql); - assertThat(sql).hasSize(6); - assertThat(sql.get(0)).contains("insert into trainer "); - assertThat(sql.get(1)).contains("insert into monkey "); - assertThat(sql.get(4)).contains("update monkey set name=?, food_preference=?, version=? where mid=? and version=?"); - assertThat(sql.get(5)).contains("insert into trainer_monkey "); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(6); + assertThat(sql.get(0)).contains("insert into trainer "); + assertThat(sql.get(1)).contains("insert into monkey "); + assertThat(sql.get(4)).contains("update monkey set name=?, food_preference=?, version=? where mid=? and version=?"); + assertThat(sql.get(5)).contains("insert into trainer_monkey "); + } + else { + assertThat(sql).hasSize(10); + assertThat(sql.get(0)).contains("insert into trainer "); + assertThat(sql.get(1)).contains("update monkey set food_preference=?, version=? where mid=? and version=?"); + assertThat(sql.get(2)).contains("insert into monkey "); + assertThat(sql.get(5)).contains("insert into trainer_monkey "); + assertThat(sql.get(9)).contains("insert into trainer_monkey "); + } int intersectionRows = Ebean.createSqlQuery("select count(*) as total from trainer_monkey where trainer_tid = ?") diff --git a/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTableNoTableName.java b/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTableNoTableName.java index dc8b93738..3ba5331e2 100644 --- a/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTableNoTableName.java +++ b/src/test/java/org/tests/o2m/jointable/TestOneToManyJoinTableNoTableName.java @@ -37,8 +37,15 @@ public class TestOneToManyJoinTableNoTableName extends BaseTestCase { Ebean.save(troop); List sql = LoggedSqlCollector.current(); - assertThat(sql).hasSize(1); - assertThat(sql.get(0)).contains("insert into mkeygroup_monkey (mkeygroup_pid, monkey_mid) values (?, ?)"); + if (isPersistBatchOnCascade()) { + assertThat(sql).hasSize(1); + assertThat(sql.get(0)).contains("insert into mkeygroup_monkey (mkeygroup_pid, monkey_mid) values (?, ?)"); + } + else { + assertThat(sql).hasSize(2); + assertThat(sql.get(0)).contains("insert into mkeygroup_monkey (mkeygroup_pid, monkey_mid) values (?, ?)"); + assertThat(sql.get(1)).contains("insert into mkeygroup_monkey (mkeygroup_pid, monkey_mid) values (?, ?)"); + } int intersectionRows = Ebean.createSqlQuery("select count(*) as total from mkeygroup_monkey where mkeygroup_pid = ?") .setParameter(1, troop.getPid()) diff --git a/src/test/java/org/tests/query/aggregation/TestAggregationCount.java b/src/test/java/org/tests/query/aggregation/TestAggregationCount.java index 3c027b662..cbd55510e 100644 --- a/src/test/java/org/tests/query/aggregation/TestAggregationCount.java +++ b/src/test/java/org/tests/query/aggregation/TestAggregationCount.java @@ -1,8 +1,12 @@ package org.tests.query.aggregation; -import io.ebean.BaseTestCase; -import io.ebean.Ebean; -import io.ebean.Query; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.StrictAssertions.assertThat; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.List; + import org.ebeantest.LoggedSqlCollector; import org.junit.BeforeClass; import org.junit.Test; @@ -13,11 +17,9 @@ import org.tests.model.basic.ResetBasicData; import org.tests.model.tevent.TEventMany; import org.tests.model.tevent.TEventOne; -import java.sql.Timestamp; -import java.time.Instant; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import io.ebean.Query; public class TestAggregationCount extends BaseTestCase { @@ -383,7 +385,7 @@ public class TestAggregationCount extends BaseTestCase { List names = Ebean.find(Contact.class) - .select("concat(lastName,', ',firstName)") + .select(concat("lastName",", ","firstName")) .where().isNull("phone") .orderBy().asc("lastName") .findSingleAttributeList(); @@ -391,7 +393,7 @@ public class TestAggregationCount extends BaseTestCase { assertThat(names).isNotEmpty(); List sql = LoggedSqlCollector.stop(); - assertThat(trimSql(sql.get(0))).contains("select concat(t0.last_name,', ',t0.first_name) from contact t0 where t0.phone is null order by t0.last_name"); + assertThat(trimSql(sql.get(0))).contains("select " + concat("t0.last_name",", ","t0.first_name") + " from contact t0 where t0.phone is null order by t0.last_name"); } @Test @@ -404,7 +406,7 @@ public class TestAggregationCount extends BaseTestCase { List names = Ebean.find(Contact.class) - .select("concat(updtime,', ',firstName)") + .select(concat("updtime",", ","firstName")) .where().isNull("phone") .orderBy().asc("lastName") .findSingleAttributeList(); @@ -412,7 +414,7 @@ public class TestAggregationCount extends BaseTestCase { assertThat(names).isNotEmpty(); List sql = LoggedSqlCollector.stop(); - assertThat(trimSql(sql.get(0))).contains("select concat(t0.updtime,', ',t0.first_name) from contact t0"); + assertThat(trimSql(sql.get(0))).contains("select " + concat("t0.updtime",", ","t0.first_name") + " from contact t0"); } @Test @@ -446,7 +448,7 @@ public class TestAggregationCount extends BaseTestCase { List contacts = Ebean.find(Contact.class) - .select("email, concat(lastName,', ',firstName) as lastName") + .select("email, " + concat("lastName",", ","firstName") + " as lastName") .where().isNull("phone") .orderBy().asc("lastName") .findList(); @@ -459,7 +461,7 @@ public class TestAggregationCount extends BaseTestCase { } List sql = LoggedSqlCollector.stop(); - assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.email, concat(t0.last_name,', ',t0.first_name) lastName from contact t0 where t0.phone is null order by t0.last_name; --bind()"); + assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.email, " + concat("t0.last_name",", ","t0.first_name") + " lastName from contact t0 where t0.phone is null order by t0.last_name; --bind()"); } } diff --git a/src/test/java/org/tests/transaction/TestBatchPersistCascade.java b/src/test/java/org/tests/transaction/TestBatchPersistCascade.java index 5c35a381d..982a127fb 100644 --- a/src/test/java/org/tests/transaction/TestBatchPersistCascade.java +++ b/src/test/java/org/tests/transaction/TestBatchPersistCascade.java @@ -4,7 +4,10 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.Platform; + import org.tests.model.basic.UTDetail; import org.tests.model.basic.UTMaster; import org.ebeantest.LoggedSqlCollector; @@ -21,6 +24,7 @@ public class TestBatchPersistCascade extends BaseTestCase { Logger logger = LoggerFactory.getLogger(TestBatchPersistCascade.class); @Test + @IgnorePlatform(Platform.HANA) public void test() { EbeanServer ebeanServer = Ebean.getServer(null); diff --git a/src/test/java/org/tests/transaction/TestNestedSubTransaction.java b/src/test/java/org/tests/transaction/TestNestedSubTransaction.java index dc9e3d147..a30d5a104 100644 --- a/src/test/java/org/tests/transaction/TestNestedSubTransaction.java +++ b/src/test/java/org/tests/transaction/TestNestedSubTransaction.java @@ -20,7 +20,7 @@ public class TestNestedSubTransaction extends BaseTestCase { /** * MySql only supports named savepoints - review. */ - @IgnorePlatform({Platform.MYSQL, Platform.SQLSERVER}) + @IgnorePlatform({Platform.MYSQL, Platform.SQLSERVER, Platform.HANA}) @Test public void ebeanServer_commitTransaction_expect_sameAsTransactionCommit() { @@ -65,7 +65,7 @@ public class TestNestedSubTransaction extends BaseTestCase { } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) @Test public void nestedUseSavepoint_doubleNested_rollbackCommit() { @@ -102,7 +102,7 @@ public class TestNestedSubTransaction extends BaseTestCase { } } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) @Test public void nestedUseSavepoint_doubleNested_commitRollback() { @@ -139,7 +139,7 @@ public class TestNestedSubTransaction extends BaseTestCase { } } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) @Test public void nestedUseSavepoint_nested_RequiresNew() { @@ -175,7 +175,7 @@ public class TestNestedSubTransaction extends BaseTestCase { assertNull(after); } - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.HANA}) @Test public void nestedUseSavepoint() { diff --git a/src/test/resources/assert/create-column-table.txt b/src/test/resources/assert/create-column-table.txt new file mode 100644 index 000000000..879972680 --- /dev/null +++ b/src/test/resources/assert/create-column-table.txt @@ -0,0 +1,8 @@ +create column table foo ( + col1 nvarchar(4) generated by default as identity not null, + col2 nvarchar(30) not null, + col3 nvarchar(30) not null, + constraint pk_foo primary key (col1) +); +comment on table foo is 'comment'; + diff --git a/src/test/resources/dbmigration/migrationtest/hana/1.0__initial.sql b/src/test/resources/dbmigration/migrationtest/hana/1.0__initial.sql new file mode 100644 index 000000000..cb6d8e6d4 --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/1.0__initial.sql @@ -0,0 +1,235 @@ +-- Migrationscripts for ebean unittest +-- apply changes +create column table migtest_ckey_assoc ( + id integer generated by default as identity not null, + assoc_one nvarchar(255), + constraint pk_migtest_ckey_assoc primary key (id) +); + +create column table migtest_ckey_detail ( + id integer generated by default as identity not null, + something nvarchar(255), + constraint pk_migtest_ckey_detail primary key (id) +); + +create column table migtest_ckey_parent ( + one_key integer not null, + two_key nvarchar(127) not null, + name nvarchar(255), + version integer not null, + constraint pk_migtest_ckey_parent primary key (one_key,two_key) +); + +create column table migtest_fk_cascade ( + id bigint generated by default as identity not null, + one_id bigint, + constraint pk_migtest_fk_cascade primary key (id) +); + +create column table migtest_fk_cascade_one ( + id bigint generated by default as identity not null, + constraint pk_migtest_fk_cascade_one primary key (id) +); + +create column table migtest_fk_none ( + id bigint generated by default as identity not null, + one_id bigint, + constraint pk_migtest_fk_none primary key (id) +); + +create column table migtest_fk_none_via_join ( + id bigint generated by default as identity not null, + one_id bigint, + constraint pk_migtest_fk_none_via_join primary key (id) +); + +create column table migtest_fk_one ( + id bigint generated by default as identity not null, + constraint pk_migtest_fk_one primary key (id) +); + +create column table migtest_fk_set_null ( + id bigint generated by default as identity not null, + one_id bigint, + constraint pk_migtest_fk_set_null primary key (id) +); + +create column table migtest_e_basic ( + id integer generated by default as identity not null, + status nvarchar(1), + name nvarchar(127), + description nvarchar(127), + some_date timestamp, + old_boolean boolean default false not null, + old_boolean2 boolean, + eref_id integer, + indextest1 nvarchar(127), + indextest2 nvarchar(127), + indextest3 nvarchar(127), + indextest4 nvarchar(127), + indextest5 nvarchar(127), + indextest6 nvarchar(127), + user_id integer not null, + constraint ck_migtest_e_basic_status check ( status in ('N','A','I')), + constraint uq_migtest_e_basic_indextest2 unique (indextest2), + constraint uq_migtest_e_basic_indextest6 unique (indextest6), + constraint pk_migtest_e_basic primary key (id) +); + +create column table migtest_e_enum ( + id integer generated by default as identity not null, + test_status nvarchar(1), + constraint ck_migtest_e_enum_test_status check ( test_status in ('N','A','I')), + constraint pk_migtest_e_enum primary key (id) +); + +create column table migtest_e_history ( + id integer generated by default as identity not null, + test_string nvarchar(255), + constraint pk_migtest_e_history primary key (id) +); + +create column table migtest_e_history2 ( + id integer generated by default as identity not null, + test_string nvarchar(255), + obsolete_string1 nvarchar(255), + obsolete_string2 nvarchar(255), + constraint pk_migtest_e_history2 primary key (id) +); + +create column table migtest_e_history3 ( + id integer generated by default as identity not null, + test_string nvarchar(255), + constraint pk_migtest_e_history3 primary key (id) +); + +create column table migtest_e_history4 ( + id integer generated by default as identity not null, + test_number integer, + constraint pk_migtest_e_history4 primary key (id) +); + +create column table migtest_e_history5 ( + id integer generated by default as identity not null, + test_number integer, + constraint pk_migtest_e_history5 primary key (id) +); + +create column table migtest_e_history6 ( + id integer generated by default as identity not null, + test_number1 integer, + test_number2 integer not null, + constraint pk_migtest_e_history6 primary key (id) +); + +create column table migtest_e_ref ( + id integer generated by default as identity not null, + name nvarchar(127) not null, + constraint uq_migtest_e_ref_name unique (name), + constraint pk_migtest_e_ref primary key (id) +); + +create column table migtest_e_softdelete ( + id integer generated by default as identity not null, + test_string nvarchar(255), + constraint pk_migtest_e_softdelete primary key (id) +); + +create column table migtest_mtm_c ( + id integer generated by default as identity not null, + name nvarchar(255), + constraint pk_migtest_mtm_c primary key (id) +); + +create column table migtest_mtm_m ( + id bigint generated by default as identity not null, + name nvarchar(255), + constraint pk_migtest_mtm_m primary key (id) +); + +create column table migtest_oto_child ( + id integer generated by default as identity not null, + name nvarchar(255), + constraint pk_migtest_oto_child primary key (id) +); + +create column table migtest_oto_master ( + id bigint generated by default as identity not null, + name nvarchar(255), + constraint pk_migtest_oto_master primary key (id) +); + +-- explicit index "ix_migtest_e_basic_indextest1" for single column "indextest1" of table "migtest_e_basic" is not necessary; +-- explicit index "ix_migtest_e_basic_indextest5" for single column "indextest5" of table "migtest_e_basic" is not necessary; +-- explicit index "ix_migtest_fk_cascade_one_id" for single column "one_id" of table "migtest_fk_cascade" is not necessary; +alter table migtest_fk_cascade add constraint fk_migtest_fk_cascade_one_id foreign key (one_id) references migtest_fk_cascade_one (id) on delete cascade on update cascade; + +-- explicit index "ix_migtest_fk_set_null_one_id" for single column "one_id" of table "migtest_fk_set_null" is not necessary; +alter table migtest_fk_set_null add constraint fk_migtest_fk_set_null_one_id foreign key (one_id) references migtest_fk_one (id) on delete set null on update set null; + +-- explicit index "ix_migtest_e_basic_eref_id" for single column "eref_id" of table "migtest_e_basic" is not necessary; +alter table migtest_e_basic add constraint fk_migtest_e_basic_eref_id foreign key (eref_id) references migtest_e_ref (id) on delete restrict on update restrict; + +create column table migtest_e_history2_history ( + id integer, + test_string nvarchar(255), + obsolete_string1 nvarchar(255), + obsolete_string2 nvarchar(255), + sys_period_start timestamp, + sys_period_end timestamp +); +alter table migtest_e_history2 add ( + sys_period_start TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, + sys_period_end TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END +); +alter table migtest_e_history2 add period for system_time(sys_period_start,sys_period_end); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history; +create column table migtest_e_history3_history ( + id integer, + test_string nvarchar(255), + sys_period_start timestamp, + sys_period_end timestamp +); +alter table migtest_e_history3 add ( + sys_period_start TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, + sys_period_end TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END +); +alter table migtest_e_history3 add period for system_time(sys_period_start,sys_period_end); +alter table migtest_e_history3 add system versioning history table migtest_e_history3_history; +create column table migtest_e_history4_history ( + id integer, + test_number integer, + sys_period_start timestamp, + sys_period_end timestamp +); +alter table migtest_e_history4 add ( + sys_period_start TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, + sys_period_end TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END +); +alter table migtest_e_history4 add period for system_time(sys_period_start,sys_period_end); +alter table migtest_e_history4 add system versioning history table migtest_e_history4_history; +create column table migtest_e_history5_history ( + id integer, + test_number integer, + sys_period_start timestamp, + sys_period_end timestamp +); +alter table migtest_e_history5 add ( + sys_period_start TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, + sys_period_end TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END +); +alter table migtest_e_history5 add period for system_time(sys_period_start,sys_period_end); +alter table migtest_e_history5 add system versioning history table migtest_e_history5_history; +create column table migtest_e_history6_history ( + id integer, + test_number1 integer, + test_number2 integer not null, + sys_period_start timestamp, + sys_period_end timestamp +); +alter table migtest_e_history6 add ( + sys_period_start TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, + sys_period_end TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END +); +alter table migtest_e_history6 add period for system_time(sys_period_start,sys_period_end); +alter table migtest_e_history6 add system versioning history table migtest_e_history6_history; diff --git a/src/test/resources/dbmigration/migrationtest/hana/1.1.sql b/src/test/resources/dbmigration/migrationtest/hana/1.1.sql new file mode 100644 index 000000000..efa06b208 --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/1.1.sql @@ -0,0 +1,176 @@ +-- Migrationscripts for ebean unittest +-- apply changes +create column table migtest_e_user ( + id integer generated by default as identity not null, + constraint pk_migtest_e_user primary key (id) +); + +create column table migtest_mtm_c_migtest_mtm_m ( + migtest_mtm_c_id integer not null, + migtest_mtm_m_id bigint not null, + constraint pk_migtest_mtm_c_migtest_mtm_m primary key (migtest_mtm_c_id,migtest_mtm_m_id) +); + +create column table migtest_mtm_m_migtest_mtm_c ( + migtest_mtm_m_id bigint not null, + migtest_mtm_c_id integer not null, + constraint pk_migtest_mtm_m_migtest_mtm_c primary key (migtest_mtm_m_id,migtest_mtm_c_id) +); + +alter table migtest_ckey_detail add ( one_key integer); +alter table migtest_ckey_detail add ( two_key nvarchar(127)); + +alter table migtest_ckey_detail add constraint fk_migtest_ckey_detail_parent foreign key (one_key,two_key) references migtest_ckey_parent (one_key,two_key) on delete restrict on update restrict; +alter table migtest_ckey_parent add ( assoc_id integer); + +alter table migtest_fk_cascade drop constraint fk_migtest_fk_cascade_one_id; +alter table migtest_fk_cascade add constraint fk_migtest_fk_cascade_one_id foreign key (one_id) references migtest_fk_cascade_one (id) on delete restrict on update restrict; +alter table migtest_fk_none add constraint fk_migtest_fk_none_one_id foreign key (one_id) references migtest_fk_one (id) on delete restrict on update restrict; +alter table migtest_fk_none_via_join add constraint fk_migtest_fk_none_via_join_one_id foreign key (one_id) references migtest_fk_one (id) on delete restrict on update restrict; +alter table migtest_fk_set_null drop constraint fk_migtest_fk_set_null_one_id; +alter table migtest_fk_set_null add constraint fk_migtest_fk_set_null_one_id foreign key (one_id) references migtest_fk_one (id) on delete restrict on update restrict; + +update migtest_e_basic set status = 'A' where status is null; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint ck_migtest_e_basic_status'; +end; +$$; +alter table migtest_e_basic alter ( status nvarchar(1) default 'A' not null); +alter table migtest_e_basic alter ( status nvarchar(1) default 'A' not null); +alter table migtest_e_basic add constraint ck_migtest_e_basic_status check ( status in ('N','A','I','?')); + +-- rename all collisions; +-- cannot create unique index "uq_migtest_e_basic_description" on table "migtest_e_basic" with nullable columns; + +insert into migtest_e_user (id) select distinct user_id from migtest_e_basic; +alter table migtest_e_basic add constraint fk_migtest_e_basic_user_id foreign key (user_id) references migtest_e_user (id) on delete restrict on update restrict; +alter table migtest_e_basic alter ( user_id integer); +alter table migtest_e_basic add ( new_string_field nvarchar(255) default 'foo''bar' not null); +alter table migtest_e_basic add ( new_boolean_field boolean default true not null); +update migtest_e_basic set new_boolean_field = old_boolean; + +alter table migtest_e_basic add ( new_boolean_field2 boolean default true not null); +alter table migtest_e_basic add ( progress integer default 0 not null); +alter table migtest_e_basic add constraint ck_migtest_e_basic_progress check ( progress in (0,1,2)); +alter table migtest_e_basic add ( new_integer integer default 42 not null); + +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_indextest2'; +end; +$$; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_indextest6'; +end; +$$; +-- cannot create unique index "uq_migtest_e_basic_status_indextest1" on table "migtest_e_basic" with nullable columns; +-- cannot create unique index "uq_migtest_e_basic_name" on table "migtest_e_basic" with nullable columns; +-- cannot create unique index "uq_migtest_e_basic_indextest4" on table "migtest_e_basic" with nullable columns; +-- cannot create unique index "uq_migtest_e_basic_indextest5" on table "migtest_e_basic" with nullable columns; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_enum drop constraint ck_migtest_e_enum_test_status'; +end; +$$; +comment on column migtest_e_history.test_string is 'Column altered to long now'; +alter table migtest_e_history alter ( test_string bigint); +comment on table migtest_e_history is 'We have history now'; +alter table migtest_e_history2 drop system versioning /* 0 */; + +-- NOTE: table has @History - special migration may be necessary +update migtest_e_history2 set test_string = 'unknown' where test_string is null; +alter table migtest_e_history2 alter ( test_string nvarchar(255) default 'unknown' not null); +alter table migtest_e_history2 alter ( test_string nvarchar(255) default 'unknown' not null); +alter table migtest_e_history2_history alter ( test_string nvarchar(255) default 'unknown' not null); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 1 */; +alter table migtest_e_history2 drop system versioning /* 2 */; +alter table migtest_e_history2 add ( test_string2 nvarchar(255)); +alter table migtest_e_history2 add ( test_string3 nvarchar(255) default 'unknown' not null); +alter table migtest_e_history2 add ( new_column nvarchar(20)); + +alter table migtest_e_history2_history add ( test_string2 nvarchar(255)); +alter table migtest_e_history2_history add ( test_string3 nvarchar(255) default 'unknown'); +alter table migtest_e_history2_history add ( new_column nvarchar(20)); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 3 */; +alter table migtest_e_history3 drop system versioning /* 4 */; +alter table migtest_e_history3 add system versioning history table migtest_e_history3_history not validated /* 5 */; +alter table migtest_e_history4 drop system versioning /* 6 */; +alter table migtest_e_history4 alter ( test_number bigint); +alter table migtest_e_history4_history alter ( test_number bigint); +alter table migtest_e_history4 add system versioning history table migtest_e_history4_history not validated /* 7 */; +alter table migtest_e_history5 drop system versioning /* 8 */; +alter table migtest_e_history5 add ( test_boolean boolean default false not null); + +alter table migtest_e_history5_history add ( test_boolean boolean default false); +alter table migtest_e_history5 add system versioning history table migtest_e_history5_history not validated /* 9 */; +alter table migtest_e_history6 drop system versioning /* 10 */; + +-- NOTE: table has @History - special migration may be necessary +update migtest_e_history6 set test_number1 = 42 where test_number1 is null; +alter table migtest_e_history6 alter ( test_number1 integer default 42 not null); +alter table migtest_e_history6 alter ( test_number1 integer default 42 not null); +alter table migtest_e_history6_history alter ( test_number1 integer default 42 not null); +alter table migtest_e_history6 add system versioning history table migtest_e_history6_history not validated /* 11 */; +alter table migtest_e_history6 drop system versioning /* 12 */; +alter table migtest_e_history6 alter ( test_number2 integer); +alter table migtest_e_history6_history alter ( test_number2 integer); +alter table migtest_e_history6 add system versioning history table migtest_e_history6_history not validated /* 13 */; +alter table migtest_e_softdelete add ( deleted boolean default false not null); + +alter table migtest_oto_child add ( master_id bigint); + +-- explicit index "ix_migtest_e_basic_indextest3" for single column "indextest3" of table "migtest_e_basic" is not necessary; +-- explicit index "ix_migtest_e_basic_indextest6" for single column "indextest6" of table "migtest_e_basic" is not necessary; +delimiter $$ +do +begin +declare exit handler for sql_error_code 261 begin end; +exec 'drop index ix_migtest_e_basic_indextest1'; +end; +$$; +delimiter $$ +do +begin +declare exit handler for sql_error_code 261 begin end; +exec 'drop index ix_migtest_e_basic_indextest5'; +end; +$$; +-- explicit index "ix_migtest_mtm_c_migtest_mtm_m_migtest_mtm_c" for single column "migtest_mtm_c_id" of table "migtest_mtm_c_migtest_mtm_m" is not necessary; +alter table migtest_mtm_c_migtest_mtm_m add constraint fk_migtest_mtm_c_migtest_mtm_m_migtest_mtm_c foreign key (migtest_mtm_c_id) references migtest_mtm_c (id) on delete restrict on update restrict; + +-- explicit index "ix_migtest_mtm_c_migtest_mtm_m_migtest_mtm_m" for single column "migtest_mtm_m_id" of table "migtest_mtm_c_migtest_mtm_m" is not necessary; +alter table migtest_mtm_c_migtest_mtm_m add constraint fk_migtest_mtm_c_migtest_mtm_m_migtest_mtm_m foreign key (migtest_mtm_m_id) references migtest_mtm_m (id) on delete restrict on update restrict; + +-- explicit index "ix_migtest_mtm_m_migtest_mtm_c_migtest_mtm_m" for single column "migtest_mtm_m_id" of table "migtest_mtm_m_migtest_mtm_c" is not necessary; +alter table migtest_mtm_m_migtest_mtm_c add constraint fk_migtest_mtm_m_migtest_mtm_c_migtest_mtm_m foreign key (migtest_mtm_m_id) references migtest_mtm_m (id) on delete restrict on update restrict; + +-- explicit index "ix_migtest_mtm_m_migtest_mtm_c_migtest_mtm_c" for single column "migtest_mtm_c_id" of table "migtest_mtm_m_migtest_mtm_c" is not necessary; +alter table migtest_mtm_m_migtest_mtm_c add constraint fk_migtest_mtm_m_migtest_mtm_c_migtest_mtm_c foreign key (migtest_mtm_c_id) references migtest_mtm_c (id) on delete restrict on update restrict; + +-- explicit index "ix_migtest_ckey_parent_assoc_id" for single column "assoc_id" of table "migtest_ckey_parent" is not necessary; +alter table migtest_ckey_parent add constraint fk_migtest_ckey_parent_assoc_id foreign key (assoc_id) references migtest_ckey_assoc (id) on delete restrict on update restrict; + +alter table migtest_oto_child add constraint fk_migtest_oto_child_master_id foreign key (master_id) references migtest_oto_master (id) on delete restrict on update restrict; + +create column table migtest_e_history_history ( + id integer, + test_string bigint, + sys_period_start timestamp, + sys_period_end timestamp +); +alter table migtest_e_history add ( + sys_period_start TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, + sys_period_end TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END +); +alter table migtest_e_history add period for system_time(sys_period_start,sys_period_end); +alter table migtest_e_history add system versioning history table migtest_e_history_history; diff --git a/src/test/resources/dbmigration/migrationtest/hana/1.2__dropsFor_1.1.sql b/src/test/resources/dbmigration/migrationtest/hana/1.2__dropsFor_1.1.sql new file mode 100644 index 000000000..a69417f3e --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/1.2__dropsFor_1.1.sql @@ -0,0 +1,19 @@ +-- Migrationscripts for ebean unittest +-- apply changes +CALL usp_ebean_drop_column('migtest_e_basic', 'old_boolean'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'old_boolean2'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'eref_id'); + +alter table migtest_e_history2 drop system versioning /* 0 */; +CALL usp_ebean_drop_column('migtest_e_history2', 'obsolete_string1'); + +CALL usp_ebean_drop_column('migtest_e_history2_history', 'obsolete_string1'); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 1 */; +alter table migtest_e_history2 drop system versioning /* 2 */; +CALL usp_ebean_drop_column('migtest_e_history2', 'obsolete_string2'); + +CALL usp_ebean_drop_column('migtest_e_history2_history', 'obsolete_string2'); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 3 */; +drop table migtest_e_ref cascade; diff --git a/src/test/resources/dbmigration/migrationtest/hana/1.3.sql b/src/test/resources/dbmigration/migrationtest/hana/1.3.sql new file mode 100644 index 000000000..11221535d --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/1.3.sql @@ -0,0 +1,134 @@ +-- Migrationscripts for ebean unittest +-- apply changes +create column table migtest_e_ref ( + id integer generated by default as identity not null, + name nvarchar(127) not null, + constraint uq_migtest_e_ref_name unique (name), + constraint pk_migtest_e_ref primary key (id) +); + +alter table migtest_ckey_detail drop constraint fk_migtest_ckey_detail_parent; +alter table migtest_fk_cascade drop constraint fk_migtest_fk_cascade_one_id; +alter table migtest_fk_cascade add constraint fk_migtest_fk_cascade_one_id foreign key (one_id) references migtest_fk_cascade_one (id) on delete cascade on update cascade; +alter table migtest_fk_none drop constraint fk_migtest_fk_none_one_id; +alter table migtest_fk_none_via_join drop constraint fk_migtest_fk_none_via_join_one_id; +alter table migtest_fk_set_null drop constraint fk_migtest_fk_set_null_one_id; +alter table migtest_fk_set_null add constraint fk_migtest_fk_set_null_one_id foreign key (one_id) references migtest_fk_one (id) on delete set null on update set null; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint ck_migtest_e_basic_status'; +end; +$$; +alter table migtest_e_basic alter ( status nvarchar(1) default null); +alter table migtest_e_basic alter ( status nvarchar(1) default null); +alter table migtest_e_basic add constraint ck_migtest_e_basic_status check ( status in ('N','A','I')); +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_description'; +end; +$$; + +update migtest_e_basic set user_id = 23 where user_id is null; +alter table migtest_e_basic drop constraint fk_migtest_e_basic_user_id; +alter table migtest_e_basic alter ( user_id integer default 23 not null); +alter table migtest_e_basic alter ( user_id integer default 23 not null); +alter table migtest_e_basic add ( old_boolean boolean default false not null); +alter table migtest_e_basic add ( old_boolean2 boolean); +alter table migtest_e_basic add ( eref_id integer); + +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_status_indextest1'; +end; +$$; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_name'; +end; +$$; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_indextest4'; +end; +$$; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_basic drop constraint uq_migtest_e_basic_indextest5'; +end; +$$; +-- cannot create unique index "uq_migtest_e_basic_indextest2" on table "migtest_e_basic" with nullable columns; +-- cannot create unique index "uq_migtest_e_basic_indextest6" on table "migtest_e_basic" with nullable columns; +delimiter $$ +do +begin +declare exit handler for sql_error_code 397 begin end; +exec 'alter table migtest_e_enum drop constraint ck_migtest_e_enum_test_status'; +end; +$$; +alter table migtest_e_enum add constraint ck_migtest_e_enum_test_status check ( test_status in ('N','A','I')); +comment on column migtest_e_history.test_string is ''; +comment on table migtest_e_history is ''; +alter table migtest_e_history2 drop system versioning /* 0 */; +alter table migtest_e_history2 alter ( test_string nvarchar(255) default null); +alter table migtest_e_history2 alter ( test_string nvarchar(255) default null); +alter table migtest_e_history2_history alter ( test_string nvarchar(255) default null); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 1 */; +alter table migtest_e_history2 drop system versioning /* 2 */; +alter table migtest_e_history2 add ( obsolete_string1 nvarchar(255)); +alter table migtest_e_history2 add ( obsolete_string2 nvarchar(255)); + +alter table migtest_e_history2_history add ( obsolete_string1 nvarchar(255)); +alter table migtest_e_history2_history add ( obsolete_string2 nvarchar(255)); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 3 */; +alter table migtest_e_history3 drop system versioning /* 4 */; +alter table migtest_e_history3 add system versioning history table migtest_e_history3_history not validated /* 5 */; +alter table migtest_e_history4 drop system versioning /* 6 */; +alter table migtest_e_history4 alter ( test_number decimal ); +alter table migtest_e_history4 alter ( test_number integer); +alter table migtest_e_history4_history alter ( test_number decimal ); +alter table migtest_e_history4_history alter ( test_number integer); +alter table migtest_e_history4 add system versioning history table migtest_e_history4_history not validated /* 7 */; +alter table migtest_e_history6 drop system versioning /* 8 */; +alter table migtest_e_history6 alter ( test_number1 integer default null); +alter table migtest_e_history6 alter ( test_number1 integer default null); +alter table migtest_e_history6_history alter ( test_number1 integer default null); +alter table migtest_e_history6 add system versioning history table migtest_e_history6_history not validated /* 9 */; +alter table migtest_e_history6 drop system versioning /* 10 */; + +-- NOTE: table has @History - special migration may be necessary +update migtest_e_history6 set test_number2 = 7 where test_number2 is null; +alter table migtest_e_history6 alter ( test_number2 integer default 7 not null); +alter table migtest_e_history6 alter ( test_number2 integer default 7 not null); +alter table migtest_e_history6_history alter ( test_number2 integer default 7 not null); +alter table migtest_e_history6 add system versioning history table migtest_e_history6_history not validated /* 11 */; +-- explicit index "ix_migtest_e_basic_indextest1" for single column "indextest1" of table "migtest_e_basic" is not necessary; +-- explicit index "ix_migtest_e_basic_indextest5" for single column "indextest5" of table "migtest_e_basic" is not necessary; +delimiter $$ +do +begin +declare exit handler for sql_error_code 261 begin end; +exec 'drop index ix_migtest_e_basic_indextest3'; +end; +$$; +delimiter $$ +do +begin +declare exit handler for sql_error_code 261 begin end; +exec 'drop index ix_migtest_e_basic_indextest6'; +end; +$$; +-- explicit index "ix_migtest_e_basic_eref_id" for single column "eref_id" of table "migtest_e_basic" is not necessary; +alter table migtest_e_basic add constraint fk_migtest_e_basic_eref_id foreign key (eref_id) references migtest_e_ref (id) on delete restrict on update restrict; + diff --git a/src/test/resources/dbmigration/migrationtest/hana/1.4__dropsFor_1.3.sql b/src/test/resources/dbmigration/migrationtest/hana/1.4__dropsFor_1.3.sql new file mode 100644 index 000000000..fe5d6371f --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/1.4__dropsFor_1.3.sql @@ -0,0 +1,51 @@ +-- Migrationscripts for ebean unittest +-- drop dependencies +alter table migtest_e_history drop system versioning; +alter table migtest_e_history drop period for system_time; +alter table migtest_e_history drop (sys_period_start,sys_period_end); +drop table migtest_e_history_history cascade; + +-- apply changes +CALL usp_ebean_drop_column('migtest_ckey_detail', 'one_key'); + +CALL usp_ebean_drop_column('migtest_ckey_detail', 'two_key'); + +CALL usp_ebean_drop_column('migtest_ckey_parent', 'assoc_id'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'new_string_field'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'new_boolean_field'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'new_boolean_field2'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'progress'); + +CALL usp_ebean_drop_column('migtest_e_basic', 'new_integer'); + +alter table migtest_e_history2 drop system versioning /* 0 */; +CALL usp_ebean_drop_column('migtest_e_history2', 'test_string2'); + +CALL usp_ebean_drop_column('migtest_e_history2_history', 'test_string2'); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 1 */; +alter table migtest_e_history2 drop system versioning /* 2 */; +CALL usp_ebean_drop_column('migtest_e_history2', 'test_string3'); + +CALL usp_ebean_drop_column('migtest_e_history2_history', 'test_string3'); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 3 */; +alter table migtest_e_history2 drop system versioning /* 4 */; +CALL usp_ebean_drop_column('migtest_e_history2', 'new_column'); + +CALL usp_ebean_drop_column('migtest_e_history2_history', 'new_column'); +alter table migtest_e_history2 add system versioning history table migtest_e_history2_history not validated /* 5 */; +alter table migtest_e_history5 drop system versioning /* 6 */; +CALL usp_ebean_drop_column('migtest_e_history5', 'test_boolean'); + +CALL usp_ebean_drop_column('migtest_e_history5_history', 'test_boolean'); +alter table migtest_e_history5 add system versioning history table migtest_e_history5_history not validated /* 7 */; +CALL usp_ebean_drop_column('migtest_e_softdelete', 'deleted'); + +CALL usp_ebean_drop_column('migtest_oto_child', 'master_id'); + +drop table migtest_e_user cascade; +drop table migtest_mtm_c_migtest_mtm_m cascade; +drop table migtest_mtm_m_migtest_mtm_c cascade; diff --git a/src/test/resources/dbmigration/migrationtest/hana/I__create_procs.sql b/src/test/resources/dbmigration/migrationtest/hana/I__create_procs.sql new file mode 100644 index 000000000..6abbe7a96 --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/I__create_procs.sql @@ -0,0 +1,33 @@ +-- Inital script to create stored procedures etc for the hana platform +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN +-- deletes all constraints and foreign keys referring to TABLE.COLUMN +-- +CREATE OR REPLACE PROCEDURE usp_ebean_drop_foreign_keys(IN table_name NVARCHAR(256), IN column_name NVARCHAR(256)) +AS +BEGIN + DECLARE foreign_key_names TABLE(CONSTRAINT_NAME NVARCHAR(256), TABLE_NAME NVARCHAR(256)); + DECLARE i INT; + + foreign_key_names = SELECT CONSTRAINT_NAME, TABLE_NAME FROM SYS.REFERENTIAL_CONSTRAINTS WHERE SCHEMA_NAME=CURRENT_SCHEMA AND TABLE_NAME=UPPER(:table_name) AND COLUMN_NAME=UPPER(:column_name); + + FOR I IN 1 .. RECORD_COUNT(:foreign_key_names) DO + EXEC 'ALTER TABLE "' || ESCAPE_DOUBLE_QUOTES(:foreign_key_names.TABLE_NAME[i]) || '" DROP CONSTRAINT "' || ESCAPE_DOUBLE_QUOTES(:foreign_key_names.CONSTRAINT_NAME[i]) || '"'; + END FOR; + +END; +$$ + +delimiter $$ +-- +-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN +-- deletes the column and ensures that all indices and constraints are dropped first +-- +CREATE OR REPLACE PROCEDURE usp_ebean_drop_column(IN table_name NVARCHAR(256), IN column_name NVARCHAR(256)) +AS +BEGIN + CALL usp_ebean_drop_foreign_keys(table_name, column_name); + EXEC 'ALTER TABLE "' || UPPER(ESCAPE_DOUBLE_QUOTES(table_name)) || '" DROP ("' || UPPER(ESCAPE_DOUBLE_QUOTES(column_name)) || '")'; +END; +$$ diff --git a/src/test/resources/dbmigration/migrationtest/hana/R__order_views_hana.sql b/src/test/resources/dbmigration/migrationtest/hana/R__order_views_hana.sql new file mode 100644 index 000000000..85b1dce9f --- /dev/null +++ b/src/test/resources/dbmigration/migrationtest/hana/R__order_views_hana.sql @@ -0,0 +1,7 @@ + + create view order_agg_vw as + select d.order_id, sum(d.order_qty * d.unit_price) as order_total, + sum(d.ship_qty * d.unit_price) as ship_total + from o_order_detail d + group by d.order_id; + \ No newline at end of file diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 5080db655..0e9fc7c8a 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -158,6 +158,11 @@ datasource.db2.password=veryverysecret#1234 datasource.db2.databaseUrl=jdbc:db2://127.0.0.1:50000/SAMPLE datasource.db2.databaseDriver=com.ibm.db2.jcc.DB2Driver +datasource.hana.username=EBEAN_TEST +datasource.hana.password=Eb3an_test +datasource.hana.databaseUrl=jdbc:sap://hxehost:39013/?databaseName=HXE +datasource.hana.databaseDriver=com.sap.db.jdbc.Driver + # parameters for migration test datasource.migrationtest.username=SA datasource.migrationtest.password=SA diff --git a/src/test/resources/extra-ddl.xml b/src/test/resources/extra-ddl.xml index bb786221b..aaf69b523 100644 --- a/src/test/resources/extra-ddl.xml +++ b/src/test/resources/extra-ddl.xml @@ -21,6 +21,14 @@ from o_order_detail d group by d.order_id; + + + create view order_agg_vw as + select d.order_id, sum(d.order_qty * d.unit_price) as order_total, + sum(d.ship_qty * d.unit_price) as ship_total + from o_order_detail d + group by d.order_id; + drop view if exists order_agg_vw;