diff --git a/ebean-test/src/test/java/io/ebean/BaseTestCase.java b/ebean-test/src/test/java/io/ebean/BaseTestCase.java index c44ce2092..43ba79427 100644 --- a/ebean-test/src/test/java/io/ebean/BaseTestCase.java +++ b/ebean-test/src/test/java/io/ebean/BaseTestCase.java @@ -195,6 +195,14 @@ public abstract class BaseTestCase { return Platform.DB2 == platform(); } + public boolean isSqLite() { + return Platform.SQLITE == platform(); + } + + public boolean isClickHouse() { + return Platform.CLICKHOUSE == platform(); + } + public boolean platformDistinctOn() { return isPostgresCompatible(); } diff --git a/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java b/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java index eaccbab44..f7a20e0d8 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java @@ -1,11 +1,18 @@ package io.ebeaninternal.dbmigration; import io.ebean.BaseTestCase; +import io.ebean.Database; +import io.ebean.DatabaseFactory; import io.ebean.SqlRow; import io.ebean.SqlUpdate; +import io.ebean.Version; import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; +import io.ebean.config.DatabaseConfig; +import io.ebean.config.dbplatform.DbHistorySupport; import io.ebean.datasource.pool.ConnectionPool; +import misc.migration.v1_1.EHistory; +import misc.migration.v1_1.EHistory2; import org.junit.jupiter.api.Test; @@ -13,12 +20,20 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * This testcase tries to apply the migrationtests that are genearated by {@link DbMigrationGenerateTest}. + * + * It does also some basic checks, if the migration is applied correctly. + * + * Please note, that this test requires the scripts generated by DbMigrationGenerateTest. So you may have to execute this test + * first. + * * @author Roland Praml, FOCONIS AG * */ @@ -29,7 +44,7 @@ public class DbMigrationTest extends BaseTestCase { assert url != null : scriptName + " not found"; server().script().run(url); } - + @Test public void lastVersion() { File d = new File("src/test/resources/migrationtest/dbmigration/h2"); @@ -38,20 +53,6 @@ public class DbMigrationTest extends BaseTestCase { assertThat(LastMigration.nextVersion(d, null, true)).isEqualTo("1.4"); } - @IgnorePlatform({ - Platform.DB2, // currently does not work due missing reorg feature - //Platform.CLICKHOUSE, // test(s) do not start at all - //Platform.COCKROACH, // does not see applied DDL? commit missing? - // Platform.NUODB nuodb container does not start - - // Yugabyte does not see column updates on table alters: - // update table T set C = 'value'; alter table T alter column C set not null -> error column C has null values - // do we need a commit after update? - Platform.YUGABYTE, - }) - //Platform.ORACLE, Platform.NUODB, Platform.POSTGRES, Platform.YUGABYTE}) - // Note: Postgres locks up on build server - // Note: YUGABYTE complains on "alter table migtest_e_basic alter column status set not null;" @Test public void testRunMigration() throws IOException, SQLException { // Shutdown and reconnect - this prevents postgres from lock up @@ -87,13 +88,17 @@ public class DbMigrationTest extends BaseTestCase { ((ConnectionPool)server().dataSource()).offline(); ((ConnectionPool)server().dataSource()).online(); - if (isSqlServer() || isMariaDB() || isMySql()) { + if (isSqlServer() || isMariaDB() || isMySql() || isHana()) { runScript("I__create_procs.sql"); } runScript("1.0__initial.sql"); - if (isOracle() || isHana()) { + if (isClickHouse()) { + // ClickHouse does not support transactions, so we cannot do update statements + // Add column is also not implemented. So exit here + return; + } else if (isOracle() || isHana()) { SqlUpdate update = server().sqlUpdate("insert into migtest_e_basic (id, old_boolean, user_id) values (1, :false, 1)"); update.setParameter("false", false); assertThat(server().execute(update)).isEqualTo(1); @@ -110,6 +115,15 @@ public class DbMigrationTest extends BaseTestCase { } createHistoryEntities(); + if (isOracle()) { + // Oracle does not like to convert varchar to integer + // ORA-01439. "column to be modified must be empty to change datatype". + // If the current table is not empty, you may have to create a temp-table + // with correct data types or do it with DBMS_REDEFINITION - to get the test + // working, we clear all data in the table + server().sqlUpdate("delete from migtest_e_history").execute(); + server().sqlUpdate("delete from migtest_e_history4").execute(); + } // Run migration runScript("1.1.sql"); @@ -136,6 +150,11 @@ public class DbMigrationTest extends BaseTestCase { assertThat(row.getBoolean("new_boolean_field2")).isTrue(); //assertThat(row.getTimestamp("some_date")).isCloseTo(new Date(), 60_000); // allow 1 minute delta + testVersioning(); + if (isSqLite()) { + // SqLite does not support drops on columns with foreign keys, so we end with the test here. + return; + } runScript("1.2__dropsFor_1.1.sql"); // Oracle caches the statement and does not detect schema change. It fails with @@ -145,16 +164,98 @@ public class DbMigrationTest extends BaseTestCase { row = result.get(0); assertThat(row.keySet()).doesNotContain("old_boolean", "old_boolean2"); + if (isYugabyte()) { + // there are some unsupported alter commands in 1.3 - so we exit here + return; + } runScript("1.3.sql"); runScript("1.4__dropsFor_1.3.sql"); - // now DB structure shoud be the same as v1_0 - perform a diffent query. + // now DB structure should be the same as v1_0 - perform a diffent query. result = server().sqlQuery("select * from migtest_e_basic order by id,name").findList(); assertThat(result).hasSize(2); row = result.get(0); assertThat(row.keySet()).contains("old_boolean", "old_boolean2"); } + // do some history tests with V1.1 models + private void testVersioning() { + if (isOracle()) { + System.err.println("FIXME: Oracle history support seems to be broken"); + return; + } + DbHistorySupport history = server().pluginApi().databasePlatform().getHistorySupport(); + if (history == null) { + return; + } + DatabaseConfig config = new DatabaseConfig(); + config.setName(server().name()); + config.loadFromProperties(server().pluginApi().config().getProperties()); + config.setDataSource(server().dataSource()); + config.setReadOnlyDataSource(server().dataSource()); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setRegister(false); + config.setPackages(Arrays.asList("misc.migration.v1_1")); + + Database tmpServer = DatabaseFactory.create(config); + try { + EHistory hist = new misc.migration.v1_1.EHistory(); + hist.setId(2); + hist.setTestString(42L); + tmpServer.save(hist); + + hist = tmpServer.find(EHistory.class).where().eq("testString", 42L).findOne(); + hist.setTestString(45L); + tmpServer.save(hist); + + List> versions = tmpServer.find(EHistory.class).setId(hist.getId()) + .findVersionsBetween(Timestamp.valueOf("1970-01-01 00:00:00"), Timestamp.valueOf("2100-01-01 00:00:00")); + assertThat(versions).hasSize(2); + assertThat(versions.get(0).getDiff().toString()).isEqualTo("{testString=45,42}"); + + EHistory2 hist2 = new misc.migration.v1_1.EHistory2(); + hist2.setId(2); + hist2.setTestString("foo1"); + hist2.setTestString2("bar1"); + hist2.setTestString3("baz1"); + tmpServer.save(hist2); + hist2.setTestString("foo2"); + hist2.setTestString2("bar2"); + tmpServer.save(hist2); + + List> versions2 = tmpServer.find(EHistory2.class).setId(hist.getId()) + .findVersionsBetween(Timestamp.valueOf("1970-01-01 00:00:00"), Timestamp.valueOf("2100-01-01 00:00:00")); + assertThat(versions2).hasSize(2); + + // not all platforms will support history exclusions + switch (server().platform()) { + case H2: // Trigger ignores HistoryExclude + + case SQLSERVER17: // these DBs are 'standard based' so they also do not support HistoryExclude + case MARIADB: + case HANA: + assertThat(versions2.get(0).getDiff().toString()) + .contains("testString=foo2,foo1") + .contains("testString2=bar2,bar1"); + break; + + case MYSQL: + case POSTGRES: + case YUGABYTE: + assertThat(versions2.get(0).getDiff().toString()) + .contains("testString=foo2,foo1") + .contains("testString2=bar2,null"); + break; + default: + throw new IllegalArgumentException(server().platform() + " not expected"); + } + + } finally { + tmpServer.shutdown(false, false); + } + } + /** * */ diff --git a/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java b/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java index d949d4838..f67b4c9f2 100644 --- a/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java +++ b/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java @@ -23,4 +23,20 @@ public class EHistory { @DbMigration(platforms = Platform.POSTGRES, preAlter = "alter table ${table} alter column ${column} TYPE bigint USING (${column}::integer)") Long testString; + + public Long getTestString() { + return testString; + } + + public void setTestString(Long testString) { + this.testString = testString; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } } diff --git a/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java b/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java index 750ad4e62..0c58ab84b 100644 --- a/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java +++ b/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java @@ -41,4 +41,45 @@ public class EHistory2 { @Size(max = 20) String newColumn; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTestString() { + return testString; + } + + public void setTestString(String testString) { + this.testString = testString; + } + + public String getTestString2() { + return testString2; + } + + public void setTestString2(String testString2) { + this.testString2 = testString2; + } + + public String getTestString3() { + return testString3; + } + + public void setTestString3(String testString3) { + this.testString3 = testString3; + } + + public String getNewColumn() { + return newColumn; + } + + public void setNewColumn(String newColumn) { + this.newColumn = newColumn; + } + }