diff --git a/.gitignore b/.gitignore index 7758d48..8b61f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build/ /integration-test-db/src/main/resources/adamd/target_version gradle-plugin-test/.gradle/ gradle.properties +local.properties diff --git a/build.gradle b/build.gradle index 9c4b614..a87f10f 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ ext.getLocalProperty = { key, file = "local.properties" -> def properties = new Properties() def localProperties = new File(rootProject.rootDir, file) if (localProperties.isFile()) { - new InputStreamReader(new FileInputStream(localProperties), UTF_8).with {properties.load} + new InputStreamReader(new FileInputStream(localProperties), UTF_8).with {properties.load(it)} return properties.getProperty(key, "") } else { return null diff --git a/integration-test/build.gradle b/integration-test/build.gradle index d17a971..ac64e1d 100644 --- a/integration-test/build.gradle +++ b/integration-test/build.gradle @@ -7,12 +7,27 @@ group 'ch.ergon.adam' sourceCompatibility = 21 +ext.jooqProUser = hasProperty("jooqProUser") ? property("jooqProUser").toString() : rootProject.getLocalProperty("JOOQ_PRO_USER") +ext.jooqProPassword = hasProperty("jooqProPassword") ? property("jooqProPassword").toString() : rootProject.getLocalProperty("JOOQ_PRO_PASSWORD") + +repositories { + mavenCentral() + maven { + url = uri("https://repo.jooq.org/repo") + credentials { + username = jooqProUser + password = jooqProPassword + } + } +} + dependencies { testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.14.1' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.1' testImplementation project(':core') testImplementation project(':yml') testImplementation project(':postgresql') + testImplementation project(':oracle') testImplementation project(':sqlite') testImplementation project(':integration-test-db') testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.1' diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/AbstractDbTestBase.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/AbstractDbTestBase.java index aba8f70..723b9ff 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/AbstractDbTestBase.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/AbstractDbTestBase.java @@ -1,11 +1,11 @@ package ch.ergon.adam.integrationtest; -import ch.ergon.adam.yml.YmlSink; import ch.ergon.adam.core.db.SchemaMigrator; import ch.ergon.adam.core.db.SourceAndSinkFactory; import ch.ergon.adam.core.db.interfaces.SchemaSink; import ch.ergon.adam.core.db.interfaces.SchemaSource; import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.yml.YmlSink; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSchemaTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSchemaTest.java index d5c2620..6715279 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSchemaTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSchemaTest.java @@ -11,9 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static ch.ergon.adam.core.Adam.DEFAULT_ADAM_PACKAGE; -import static ch.ergon.adam.core.Adam.DEFAULT_MAIN_RESOURCE_PATH; -import static ch.ergon.adam.core.Adam.TARGET_VERSION_FILE_NAME; +import static ch.ergon.adam.core.Adam.*; import static ch.ergon.adam.core.prepost.db_schema_version.DbSchemaVersionSource.SCHEMA_VERSION_TABLE_NAME; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSqliteInMemoryTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSqliteInMemoryTest.java index d75d3af..bdde821 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSqliteInMemoryTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithSqliteInMemoryTest.java @@ -12,9 +12,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static ch.ergon.adam.core.Adam.DEFAULT_ADAM_PACKAGE; -import static ch.ergon.adam.core.Adam.DEFAULT_MAIN_RESOURCE_PATH; -import static ch.ergon.adam.core.Adam.TARGET_VERSION_FILE_NAME; +import static ch.ergon.adam.core.Adam.*; import static ch.ergon.adam.core.prepost.db_schema_version.DbSchemaVersionSource.SCHEMA_VERSION_TABLE_NAME; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithoutSchemaTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithoutSchemaTest.java index 946d3fb..d403848 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithoutSchemaTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/DbMigrationWithoutSchemaTest.java @@ -12,9 +12,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static ch.ergon.adam.core.Adam.DEFAULT_ADAM_PACKAGE; -import static ch.ergon.adam.core.Adam.DEFAULT_MAIN_RESOURCE_PATH; -import static ch.ergon.adam.core.Adam.TARGET_VERSION_FILE_NAME; +import static ch.ergon.adam.core.Adam.*; import static ch.ergon.adam.core.prepost.db_schema_version.DbSchemaVersionSource.SCHEMA_VERSION_TABLE_NAME; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/AbstractOracleSchemaCoverageTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/AbstractOracleSchemaCoverageTest.java new file mode 100644 index 0000000..762c4d1 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/AbstractOracleSchemaCoverageTest.java @@ -0,0 +1,92 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.core.db.SchemaDiffExtractor; +import ch.ergon.adam.core.db.interfaces.SchemaSource; +import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.core.db.schema.View; +import ch.ergon.adam.core.helper.FileHelper; +import ch.ergon.adam.integrationtest.AssertAnyChangeStrategy; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static ch.ergon.adam.core.helper.CollectorsHelper.createSchemaItemNameArray; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +public abstract class AbstractOracleSchemaCoverageTest extends AbstractOracleTestBase { + + private static Path tempFolder; + + @BeforeAll + public static void setupTempFolder() throws IOException { + tempFolder = Files.createTempDirectory("ADAMTableTest"); + tempFolder.toFile().deleteOnExit(); + } + + @AfterAll + public static void cleanupTempFolder() throws IOException { + FileHelper.deleteFolderRecursively(tempFolder); + } + + private static final String CREATE_TABLE_SQL = + "CREATE TABLE test_table (" + + "id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY, " + + "col1 INTEGER NOT NULL, " + + "col2 NUMBER(10,2) DEFAULT 10 NULL, " + + "col3 CLOB NULL, " + + "col4 VARCHAR2(10) NOT NULL, " + + "CONSTRAINT test_table_pkey PRIMARY KEY (id), " + + "CONSTRAINT test_table_col1_key UNIQUE (col1)" + + ")"; + + private static final String CREATE_TABLE_WITH_FK_SQL = + "CREATE TABLE test_fktable (" + + "id INTEGER REFERENCES test_table(id), " + + "CONSTRAINT test_fktable_pkey PRIMARY KEY (id) " + + ")"; + + private static final String CREATE_VIEW = + "CREATE VIEW test_view AS (" + + "SELECT * FROM test_table " + + ")"; + + private static final String CREATE_SEQUENCE = + "CREATE SEQUENCE test_sequence"; + + private static final String CREATE_VIEW2 = + "CREATE VIEW depending_view AS (" + + "SELECT * FROM test_view " + + ")"; + + + @Test + public void testSchemaCoverage() throws Exception { + getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); + getSourceDbConnection().createStatement().execute(CREATE_TABLE_WITH_FK_SQL); + getSourceDbConnection().createStatement().execute(CREATE_VIEW); + getSourceDbConnection().createStatement().execute(CREATE_VIEW2); + getSourceDbConnection().createStatement().execute(CREATE_SEQUENCE); + SchemaSource source = getSourceDbSource(); + Schema finalSchema = executeTransformation(source); + + assertSchemaEquals(source.getSchema(), finalSchema); + } + + protected abstract Schema executeTransformation(SchemaSource source) throws Exception; + + private void assertSchemaEquals(Schema sourceSchema, Schema targetSchema) { + SchemaDiffExtractor diffExtractor = new SchemaDiffExtractor(sourceSchema, targetSchema); + diffExtractor.process(new AssertAnyChangeStrategy()); + + sourceSchema.getViews().forEach(sourceView -> { + View targetView = targetSchema.getView(sourceView.getName()); + String[] sourceDependencies = createSchemaItemNameArray(sourceView.getBaseRelations()); + String[] targetDependencies = createSchemaItemNameArray(targetView.getBaseRelations()); + assertArrayEquals(targetDependencies, sourceDependencies); + }); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/AbstractOracleTestBase.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/AbstractOracleTestBase.java new file mode 100644 index 0000000..b08da35 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/AbstractOracleTestBase.java @@ -0,0 +1,11 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.AbstractDbTestBase; + +public class AbstractOracleTestBase extends AbstractDbTestBase { + + + public AbstractOracleTestBase() { + super(new OracleTestDbUrlProvider()); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleAddFieldTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleAddFieldTests.java new file mode 100644 index 0000000..88c98e3 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleAddFieldTests.java @@ -0,0 +1,9 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.AddFieldTests; + +public class OracleAddFieldTests extends AddFieldTests { + public OracleAddFieldTests() { + super(new OracleTestDbUrlProvider()); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleCastFieldTypeTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleCastFieldTypeTests.java new file mode 100644 index 0000000..1569f7b --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleCastFieldTypeTests.java @@ -0,0 +1,58 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.core.db.schema.Field; +import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.testcases.CastFieldTypeTest; +import org.junit.jupiter.api.Test; + +import java.sql.ResultSet; + +import static ch.ergon.adam.core.db.schema.DataType.INTEGER; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OracleCastFieldTypeTests extends CastFieldTypeTest { + public OracleCastFieldTypeTests() { + super(new OracleTestDbUrlProvider()); + } + + @Test + public void testCastVarcharToSerial() throws Exception { + + // Setup db + getTargetDbConnection().createStatement().execute(getCreateTableStatement()); + getTargetDbConnection().createStatement().execute(INSERT_DATA_SQL); + DummySink dummySink = targetToDummy(); + Schema schema = dummySink.getTargetSchema(); + + // Apply change + Table table = schema.getTable("test_table"); + Field field = table.getField("col1"); + field.setDataType(INTEGER); + field.setSequence(true); + migrateTargetWithSchema(schema); + dummySink = targetToDummy(); + schema = dummySink.getTargetSchema(); + + // Verify + table = schema.getTable("test_table"); + assertNotNull(table); + + // Data still present? + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"col1\") from \"test_table\""); + assertTrue(result.next()); + assertThat(result.getInt(1), is(2)); + } + + @Override + protected String getCreateTableStatement() { + return "create table \"test_table\" (" + + "\"col1\" varchar2(100), " + + "\"col2\" int " + + ")"; + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleChangeFieldTypeTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleChangeFieldTypeTest.java new file mode 100644 index 0000000..11cfbca --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleChangeFieldTypeTest.java @@ -0,0 +1,34 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.ChangeFieldTypeTest; +import org.junit.jupiter.api.Disabled; + +public class OracleChangeFieldTypeTest extends ChangeFieldTypeTest { + public OracleChangeFieldTypeTest() { + super(new OracleTestDbUrlProvider()); + } + + protected String getCreateTableNotNullSql() { + return "create table \"test_table\" (" + + "\"test_field\" varchar(10) not null " + + ")"; + } + + protected String getCreateTableNullSql() { + return "create table \"test_table\" (" + + "\"test_field\" numeric(10,2) null " + + ")"; + } + + protected String getCreateTableTwoFieldsSql() { + return "create table \"test_table\" (" + + "\"col1\" clob null, " + + "\"col2\" clob null " + + ")"; + } + + @Override + @Disabled + public void changeFromSerialToClob() { + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleConstraintTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleConstraintTests.java new file mode 100644 index 0000000..70f55cb --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleConstraintTests.java @@ -0,0 +1,50 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.integrationtest.DummySink; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class OracleConstraintTests extends AbstractOracleTestBase { + + private static final String CREATE_TABLE_SQL = + "create table test_table (" + + "id integer null CHECK (id > 0) " + + ")"; + + @Test + public void testCreateConstraint() throws Exception { + + // Setup db + getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); + sourceToTarget(); + DummySink dummySink = targetToDummy(); + + // Verify + Schema schema = dummySink.getTargetSchema(); + assertThat(schema.getTable("TEST_TABLE").getConstraints().size(), is(1)); + } + + @Test + public void testRecreateConstraintAfterTableChange() throws Exception { + + // Setup db + getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); + sourceToTarget(); + DummySink dummySink = targetToDummy(); + Schema schema = dummySink.getTargetSchema(); + + // Apply change + schema.getTable("TEST_TABLE").getField("ID").setNullable(false); + migrateTargetWithSchema(schema); + dummySink = targetToDummy(); + schema = dummySink.getTargetSchema(); + + // Verify + assertFalse(schema.getTable("TEST_TABLE").getField("ID").isNullable()); + assertThat(schema.getTable("TEST_TABLE").getConstraints().size(), is(1)); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleCreateTableTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleCreateTableTest.java new file mode 100644 index 0000000..de9e84d --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleCreateTableTest.java @@ -0,0 +1,173 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.core.db.schema.*; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; +import org.junit.jupiter.api.Test; + +import static ch.ergon.adam.core.db.schema.DataType.DECIMAL_INTEGER; +import static ch.ergon.adam.core.db.schema.DataType.NUMERIC; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class OracleCreateTableTest extends AbstractDbTestBase { + + public OracleCreateTableTest() { + super(new OracleTestDbUrlProvider()); + } + + private static final String YML = "---\n" + + "name: \"TEST_TABLE\"\n" + + "fields:\n" + + "- name: \"ID\"\n" + + " dataType: \"DECIMAL_INTEGER\"\n" + + " precision: 38\n" + + " sequence: true\n" + + "- name: \"COL1\"\n" + + " dataType: \"DECIMAL_INTEGER\"\n" + + " precision: 38\n" + + "- name: \"COL2\"\n" + + " dataType: \"NUMERIC\"\n" + + " defaultValue: \"10\"\n" + + " nullable: true\n" + + " precision: 10\n" + + " scale: 2\n" + + "- name: \"COL3\"\n" + + " dataType: \"CLOB\"\n" + + " nullable: true\n" + + "- name: \"COL4\"\n" + + " dataType: \"VARCHAR\"\n" + + " length: 10\n" + + "indexes:\n" + + "- name: \"TEST_TABLE_COL1_KEY\"\n" + + " fields:\n" + + " - \"COL1\"\n" + + " unique: true\n" + + "- name: \"TEST_TABLE_PKEY\"\n" + + " fields:\n" + + " - \"ID\"\n" + + " primary: true\n" + + " unique: true\n" + + "--- []\n" + + "--- []\n"; + + private static final String CREATE_TABLE_SQL = + "create table test_table (" + + "id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY, " + + "col1 INTEGER not null, " + + "col2 NUMBER(10,2) DEFAULT 10 null, " + + "col3 CLOB null, " + + "col4 VARCHAR2(10) not null, " + + "CONSTRAINT test_table_pkey PRIMARY KEY (id), " + + "CONSTRAINT test_table_col1_key UNIQUE (col1)" + + ")"; + + private void verifySchema(Schema schema) { + assertNotNull(schema); + assertThat(schema.getTables().size(), is(1)); + assertTrue(schema.getViews().isEmpty()); + assertTrue(schema.getEnums().isEmpty()); + + Table table = schema.getTable("TEST_TABLE"); + assertNotNull(table); + assertThat(table.getFields().size(), is(5)); + + Field field = table.getField("ID"); + assertNotNull(field); + assertThat(field.getIndex(), is(0)); + assertFalse(field.isNullable()); + assertFalse(field.isArray()); + assertNull(field.getLength()); + assertThat(field.getPrecision(), is(38)); + assertNull(field.getScale()); + assertTrue(field.isSequence()); + assertNull(field.getDefaultValue()); + assertNull(field.getDbEnum()); + assertThat(field.getDataType(), is(DECIMAL_INTEGER)); + + field = table.getField("COL1"); + assertNotNull(field); + assertThat(field.getIndex(), is(1)); + assertFalse(field.isNullable()); + assertFalse(field.isArray()); + assertNull(field.getLength()); + assertThat(field.getPrecision(), is(38)); + assertNull(field.getScale()); + assertFalse(field.isSequence()); + assertNull(field.getDefaultValue()); + assertNull(field.getDbEnum()); + assertThat(field.getDataType(), is(DECIMAL_INTEGER)); + + field = table.getField("COL2"); + assertNotNull(field); + assertThat(field.getIndex(), is(2)); + assertTrue(field.isNullable()); + assertFalse(field.isArray()); + assertNull(field.getLength()); + assertThat(field.getPrecision(), is(10)); + assertThat(field.getScale(), is(2)); + assertFalse(field.isSequence()); + assertThat(field.getDefaultValue(), is("10")); + assertNull(field.getDbEnum()); + assertThat(field.getDataType(), is(NUMERIC)); + + field = table.getField("COL3"); + assertNotNull(field); + assertThat(field.getIndex(), is(3)); + assertTrue(field.isNullable()); + assertFalse(field.isArray()); + assertNull(field.getLength()); + assertNull(field.getPrecision()); + assertNull(field.getScale()); + assertFalse(field.isSequence()); + assertNull(field.getDefaultValue()); + assertNull(field.getDbEnum()); + assertThat(field.getDataType(), is(DataType.CLOB)); + + field = table.getField("COL4"); + assertNotNull(field); + assertThat(field.getIndex(), is(4)); + assertFalse(field.isNullable()); + assertFalse(field.isArray()); + assertThat(field.getLength(), is(10)); + assertNull(field.getPrecision()); + assertNull(field.getScale()); + assertFalse(field.isSequence()); + assertNull(field.getDefaultValue()); + assertNull(field.getDbEnum()); + assertThat(field.getDataType(), is(DataType.VARCHAR)); + + Index index = table.getIndex("TEST_TABLE_PKEY"); + assertNotNull(index); + assertTrue(index.isPrimary()); + assertTrue(index.isUnique()); + assertThat(index.getFields().size(), is(1)); + assertThat(index.getFields().get(0).getName(), is("ID")); + + index = table.getIndex("TEST_TABLE_COL1_KEY"); + assertNotNull(index); + assertFalse(index.isPrimary()); + assertTrue(index.isUnique()); + assertThat(index.getFields().size(), is(1)); + assertThat(index.getFields().get(0).getName(), is("COL1")); + } + + @Test + public void testCreateTable() throws Exception { + getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); + sourceToTarget(); + DummySink dummySink = targetToDummy(); + Schema schema = dummySink.getTargetSchema(); + verifySchema(schema); + } + + @Test + public void testCreateTableToYml() throws Exception { + getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); + sourceToTarget(); + String yml = targetToYml(); + assertEquals(YML, yml); + } + +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleDefaultTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleDefaultTest.java new file mode 100644 index 0000000..be1bc3c --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleDefaultTest.java @@ -0,0 +1,34 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.DefaultTest; +import org.junit.jupiter.api.Test; + +public class OracleDefaultTest extends DefaultTest { + public OracleDefaultTest() { + super(new OracleTestDbUrlProvider()); + } + + protected String getCreateTableIntDefaultSql() { + return "create table \"test_table\" (" + + "\"id\" number default 1 " + + ")"; + } + + protected String getCreateTableStringDefaultSql() { + return "create table \"test_table\" (" + + "\"id\" VARCHAR(100) default 'defaultValue' " + + ")"; + } + + protected String getCreateTableFunctionDefaultSql() { + return "create table \"test_table\" (" + + "\"id\" varchar(100) default SYS_GUID() " + + ")"; + } + + @Test + @Override + public void testFunctionDefault() throws Exception { + doTestDefault(getCreateTableFunctionDefaultSql(), "SYS_GUID()"); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleEmptyDatabaseTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleEmptyDatabaseTest.java new file mode 100644 index 0000000..9464e98 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleEmptyDatabaseTest.java @@ -0,0 +1,10 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.EmptyDatabaseTest; + +public class OracleEmptyDatabaseTest extends EmptyDatabaseTest { + + public OracleEmptyDatabaseTest() { + super(new OracleTestDbUrlProvider()); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleIndexTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleIndexTests.java new file mode 100644 index 0000000..a5983a0 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleIndexTests.java @@ -0,0 +1,23 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.IndexTests; +import org.junit.jupiter.api.Disabled; + +public class OracleIndexTests extends IndexTests { + public OracleIndexTests() { + super(new OracleTestDbUrlProvider()); + } + + protected String getCreateTableSql() { + return "create table \"test_table\" (" + + "\"id\" integer null unique, " + + "\"col1\" varchar(100)" + + ")"; + } + + @Override + @Disabled + public void testRecreatePartialIndexAfterTableChange() throws Exception { + super.testRecreatePartialIndexAfterTableChange(); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleRemoveFieldTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleRemoveFieldTests.java new file mode 100644 index 0000000..4dc99ef --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleRemoveFieldTests.java @@ -0,0 +1,9 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.RemoveFieldTests; + +public class OracleRemoveFieldTests extends RemoveFieldTests { + public OracleRemoveFieldTests() { + super(new OracleTestDbUrlProvider()); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleRenameFieldTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleRenameFieldTests.java new file mode 100644 index 0000000..9f87c69 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleRenameFieldTests.java @@ -0,0 +1,9 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.testcases.RenameFieldTests; + +public class OracleRenameFieldTests extends RenameFieldTests { + public OracleRenameFieldTests() { + super(new OracleTestDbUrlProvider()); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleSchemaCoverageTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleSchemaCoverageTest.java new file mode 100644 index 0000000..f30725a --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleSchemaCoverageTest.java @@ -0,0 +1,17 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.core.db.SchemaMigrator; +import ch.ergon.adam.core.db.interfaces.SchemaSink; +import ch.ergon.adam.core.db.interfaces.SchemaSource; +import ch.ergon.adam.core.db.schema.Schema; + +public class OracleSchemaCoverageTest extends AbstractOracleSchemaCoverageTest { + + @Override + protected Schema executeTransformation(SchemaSource source) throws Exception { + try (SchemaSink targetSink = getTargetDbSink()) { + new SchemaMigrator(getTargetDbSource(), source, targetSink).migrate(); + } + return getTargetDbSource().getSchema(); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleTestDbUrlProvider.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleTestDbUrlProvider.java new file mode 100644 index 0000000..51bc680 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleTestDbUrlProvider.java @@ -0,0 +1,91 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.TestDbUrlProvider; +import org.jooq.DSLContext; +import org.jooq.impl.DSL; +import org.testcontainers.oracle.OracleContainer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class OracleTestDbUrlProvider extends TestDbUrlProvider { + + private static final OracleContainer sourceContainer = new OracleContainer("gvenzl/oracle-free"); + private static final OracleContainer targetContainer = new OracleContainer("gvenzl/oracle-free"); + + @Override + protected void initDbForTest() throws SQLException { + try (Connection conn = DriverManager.getConnection(getDbUrl(sourceContainer))) { + cleanSchema(conn); + } + try (Connection conn = DriverManager.getConnection(getDbUrl(targetContainer))) { + cleanSchema(conn); + } + } + + private void cleanSchema(Connection conn) throws SQLException { + DSLContext dslContext = DSL.using(conn); + dslContext.execute(""" + BEGIN + FOR cur_rec IN (SELECT object_name, object_type + FROM user_objects + WHERE object_type IN + ('TABLE', + 'VIEW', + 'PACKAGE', + 'PROCEDURE', + 'FUNCTION', + 'SEQUENCE', + 'TYPE', + 'SYNONYM', + 'MATERIALIZED VIEW' + )) + LOOP + BEGIN + IF cur_rec.object_type = 'TABLE' + THEN + EXECUTE IMMEDIATE 'DROP ' + || cur_rec.object_type + || ' "' + || cur_rec.object_name + || '" CASCADE CONSTRAINTS'; + ELSE + EXECUTE IMMEDIATE 'DROP ' + || cur_rec.object_type + || ' "' + || cur_rec.object_name + || '"'; + END IF; + EXCEPTION + WHEN OTHERS + THEN + DBMS_OUTPUT.put_line ( 'FAILED: DROP ' + || cur_rec.object_type + || ' "' + || cur_rec.object_name + || '"' + ); + END; + END LOOP; + END; + """); + } + + @Override + protected String getSourceDbUrl() { + return getDbUrl(sourceContainer); + } + + @Override + protected String getTargetDbUrl() { + return getDbUrl(targetContainer); + } + + protected String getDbUrl(OracleContainer container) { + if (!container.isRunning()) { + container.start(); + } + return container.getJdbcUrl().replace("@", container.getUsername() +"/" + container.getPassword() + "@") + "?currentSchema=" + container.getUsername().toUpperCase(); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleViewTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleViewTests.java new file mode 100644 index 0000000..7d2d6d8 --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/oracle/OracleViewTests.java @@ -0,0 +1,10 @@ +package ch.ergon.adam.integrationtest.oracle; + +import ch.ergon.adam.integrationtest.postgresql.PostgreSqlTestDbUrlProvider; +import ch.ergon.adam.integrationtest.testcases.ViewTests; + +public class OracleViewTests extends ViewTests { + public OracleViewTests() { + super(new OracleTestDbUrlProvider()); + } +} diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/AbstractSchemaCoverageTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/AbstractPostgreSQLSchemaCoverageTest.java similarity index 97% rename from integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/AbstractSchemaCoverageTest.java rename to integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/AbstractPostgreSQLSchemaCoverageTest.java index a96b652..9be959e 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/AbstractSchemaCoverageTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/AbstractPostgreSQLSchemaCoverageTest.java @@ -1,11 +1,11 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.AssertAnyChangeStrategy; import ch.ergon.adam.core.db.SchemaDiffExtractor; import ch.ergon.adam.core.db.interfaces.SchemaSource; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.View; import ch.ergon.adam.core.helper.FileHelper; +import ch.ergon.adam.integrationtest.AssertAnyChangeStrategy; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -17,7 +17,7 @@ import static ch.ergon.adam.core.helper.CollectorsHelper.createSchemaItemNameArray; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -public abstract class AbstractSchemaCoverageTest extends AbstractPostgresqlTestBase { +public abstract class AbstractPostgreSQLSchemaCoverageTest extends AbstractPostgresqlTestBase { private static Path tempFolder; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/CastFieldTypeWithEnumTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/CastFieldTypeWithEnumTest.java index 0f66e05..bf89493 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/CastFieldTypeWithEnumTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/CastFieldTypeWithEnumTest.java @@ -1,10 +1,10 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.core.db.schema.DbEnum; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.DummySink; import org.junit.jupiter.api.Test; import java.sql.ResultSet; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/ConstraintTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/ConstraintTests.java index e89b170..3e7f973 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/ConstraintTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/ConstraintTests.java @@ -1,7 +1,7 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.integrationtest.DummySink; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumArrayFieldTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumArrayFieldTest.java index d2437f1..e21544d 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumArrayFieldTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumArrayFieldTest.java @@ -1,10 +1,10 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.AbstractDbTestBase; -import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.core.db.schema.DataType; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumTests.java index 2c2ed36..55170a1 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/EnumTests.java @@ -1,7 +1,7 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.integrationtest.DummySink; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/MigrationStepExecutorTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/MigrationStepExecutorTest.java index 51c49ff..71708cf 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/MigrationStepExecutorTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/MigrationStepExecutorTest.java @@ -15,9 +15,7 @@ import java.sql.ResultSet; import java.util.List; -import static ch.ergon.adam.core.Adam.DEFAULT_ADAM_PACKAGE; -import static ch.ergon.adam.core.Adam.DEFAULT_MAIN_RESOURCE_PATH; -import static ch.ergon.adam.core.Adam.HISTORY_FILE_NAME; +import static ch.ergon.adam.core.Adam.*; import static ch.ergon.adam.core.prepost.MigrationStep.PREMIGRATION_ONCE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlArrayFieldTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlArrayFieldTest.java index 936fa3a..a310a4c 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlArrayFieldTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlArrayFieldTest.java @@ -1,12 +1,10 @@ package ch.ergon.adam.integrationtest.postgresql; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; public class PostgreSqlArrayFieldTest extends AbstractDbTestBase { diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCastFieldTypeTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCastFieldTypeTests.java index 0b375ef..533444a 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCastFieldTypeTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCastFieldTypeTests.java @@ -1,10 +1,10 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.integrationtest.testcases.CastFieldTypeTest; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.testcases.CastFieldTypeTest; import org.junit.jupiter.api.Test; import java.sql.ResultSet; @@ -24,7 +24,7 @@ public PostgreSqlCastFieldTypeTests() { public void testCastVarcharToSerial() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableStatement()); getTargetDbConnection().createStatement().execute(INSERT_DATA_SQL); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCreateTableTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCreateTableTest.java index b0b8a3a..55214e6 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCreateTableTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlCreateTableTest.java @@ -5,9 +5,7 @@ import ch.ergon.adam.integrationtest.DummySink; import org.junit.jupiter.api.Test; -import static ch.ergon.adam.core.db.schema.DataType.BIGINT; -import static ch.ergon.adam.core.db.schema.DataType.DECIMAL_INTEGER; -import static ch.ergon.adam.core.db.schema.DataType.NUMERIC; +import static ch.ergon.adam.core.db.schema.DataType.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlIntervalFieldTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlIntervalFieldTest.java index 01e145c..dfdb710 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlIntervalFieldTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreSqlIntervalFieldTest.java @@ -1,8 +1,8 @@ package ch.ergon.adam.integrationtest.postgresql; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreTimestampSizeTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreTimestampSizeTest.java index a6c753c..8105576 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreTimestampSizeTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgreTimestampSizeTest.java @@ -1,10 +1,10 @@ package ch.ergon.adam.integrationtest.postgresql; +import ch.ergon.adam.core.db.SchemaDiffExtractor; +import ch.ergon.adam.core.db.interfaces.SchemaSource; import ch.ergon.adam.core.db.schema.*; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.AssertAnyChangeStrategy; -import ch.ergon.adam.core.db.SchemaDiffExtractor; -import ch.ergon.adam.core.db.interfaces.SchemaSource; import org.junit.jupiter.api.Test; import java.io.IOException; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgresSchemaCoverageTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgresSchemaCoverageTest.java index b237216..3121393 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgresSchemaCoverageTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/PostgresSchemaCoverageTest.java @@ -5,7 +5,7 @@ import ch.ergon.adam.core.db.interfaces.SchemaSource; import ch.ergon.adam.core.db.schema.Schema; -public class PostgresSchemaCoverageTest extends AbstractSchemaCoverageTest { +public class PostgresSchemaCoverageTest extends AbstractPostgreSQLSchemaCoverageTest { @Override protected Schema executeTransformation(SchemaSource source) throws Exception { diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/RollbackOnInsertErrorTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/RollbackOnInsertErrorTest.java index c03a203..7de841e 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/RollbackOnInsertErrorTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/RollbackOnInsertErrorTest.java @@ -15,9 +15,7 @@ import java.util.ArrayList; import java.util.List; -import static ch.ergon.adam.core.Adam.DEFAULT_ADAM_PACKAGE; -import static ch.ergon.adam.core.Adam.DEFAULT_MAIN_RESOURCE_PATH; -import static ch.ergon.adam.core.Adam.TARGET_VERSION_FILE_NAME; +import static ch.ergon.adam.core.Adam.*; import static ch.ergon.adam.core.db.schema.DataType.INTEGER; import static ch.ergon.adam.core.prepost.db_schema_version.DbSchemaVersionSource.SCHEMA_VERSION_TABLE_NAME; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/SequenceTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/SequenceTests.java index 52512ef..7c5a601 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/SequenceTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/SequenceTests.java @@ -1,8 +1,8 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Sequence; +import ch.ergon.adam.integrationtest.DummySink; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/StableSequenceNameTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/StableSequenceNameTest.java index 013922a..66cc208 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/StableSequenceNameTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/StableSequenceNameTest.java @@ -1,9 +1,9 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.DummySink; import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/YmlSchemaCoverageTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/YmlSchemaCoverageTest.java index c1f0417..bb07621 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/YmlSchemaCoverageTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/postgresql/YmlSchemaCoverageTest.java @@ -1,13 +1,13 @@ package ch.ergon.adam.integrationtest.postgresql; -import ch.ergon.adam.integrationtest.EmptySource; -import ch.ergon.adam.yml.YmlSink; -import ch.ergon.adam.yml.YmlSource; import ch.ergon.adam.core.db.SchemaMigrator; import ch.ergon.adam.core.db.interfaces.SchemaSource; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.filetree.DirectoryTraverser; import ch.ergon.adam.core.helper.FileHelper; +import ch.ergon.adam.integrationtest.EmptySource; +import ch.ergon.adam.yml.YmlSink; +import ch.ergon.adam.yml.YmlSource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -16,7 +16,7 @@ import java.nio.file.Files; import java.nio.file.Path; -public class YmlSchemaCoverageTest extends AbstractSchemaCoverageTest { +public class YmlSchemaCoverageTest extends AbstractPostgreSQLSchemaCoverageTest { private static Path tempFolder; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteCreateTableTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteCreateTableTest.java index a7de5d4..fcc5a9b 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteCreateTableTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteCreateTableTest.java @@ -10,10 +10,7 @@ import static ch.ergon.adam.core.db.schema.DataType.NUMERIC; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public abstract class SqliteCreateTableTest extends AbstractDbTestBase { diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteIndexTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteIndexTests.java index 62cdc83..94d16c6 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteIndexTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteIndexTests.java @@ -1,6 +1,7 @@ package ch.ergon.adam.integrationtest.sqlite; import ch.ergon.adam.integrationtest.testcases.IndexTests; +import org.junit.jupiter.api.Disabled; import java.io.IOException; @@ -8,4 +9,10 @@ public class SqliteIndexTests extends IndexTests { public SqliteIndexTests() throws IOException { super(new SqliteTestFileDbUrlProvider()); } + + @Override + @Disabled + public void testRecreatePartialIndexAfterTableChange() throws Exception { + // https://github.com/jOOQ/jOOQ/issues/16683 + } } diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteTestInMemoryDbUrlProvider.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteTestInMemoryDbUrlProvider.java index 4fde040..d947d84 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteTestInMemoryDbUrlProvider.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/sqlite/SqliteTestInMemoryDbUrlProvider.java @@ -1,8 +1,8 @@ package ch.ergon.adam.integrationtest.sqlite; +import ch.ergon.adam.core.helper.Pair; import ch.ergon.adam.integrationtest.TestDbUrlProvider; import ch.ergon.adam.sqlite.SqliteInMemoryFactory; -import ch.ergon.adam.core.helper.Pair; import java.io.IOException; import java.sql.Connection; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/AddFieldTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/AddFieldTests.java index 5a75b36..7f05d8a 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/AddFieldTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/AddFieldTests.java @@ -1,36 +1,31 @@ package ch.ergon.adam.integrationtest.testcases; -import ch.ergon.adam.integrationtest.AbstractDbTestBase; -import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.integrationtest.TestDbUrlProvider; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.TestDbUrlProvider; import org.junit.jupiter.api.Test; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; -import static ch.ergon.adam.core.db.schema.DataType.CLOB; -import static ch.ergon.adam.core.db.schema.DataType.INTEGER; -import static ch.ergon.adam.core.db.schema.DataType.VARCHAR; +import static ch.ergon.adam.core.db.schema.DataType.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public abstract class AddFieldTests extends AbstractDbTestBase { private static final String CREATE_TABLE_SQL = - "create table test_table (" + - "id int " + + "create table \"test_table\" (" + + "\"id\" int " + ")"; private static final String INSERT_DATA_SQL = - "insert into test_table values (1)"; + "insert into \"test_table\" values (1)"; public AddFieldTests(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -70,7 +65,7 @@ public void testAddFieldAtEndOfTable() throws Exception { assertThat(field.getDefaultValue(), is("'testDefault'")); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select count(*) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select count(*) from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(1)); @@ -113,7 +108,7 @@ public void testAddFieldAtBeginOfTable() throws Exception { assertThat(field.getDefaultValue(), is("'abcd'")); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select count(*) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select count(*) from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(1)); } @@ -133,13 +128,13 @@ public void testAddFieldAtEndOfTableWithSimpleMigrationNotNullWithDefault() thro Field newField = new Field("new_field"); newField.setDataType(INTEGER); newField.setDefaultValue("1"); - newField.setSqlForNew("id"); + newField.setSqlForNew("\"id\""); fields.add(newField); table.setFields(fields); migrateTargetWithSchema(schema); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(new_field) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"new_field\") from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(1)); @@ -159,13 +154,13 @@ public void testAddFieldAtEndOfTableWithComplexMigrationNotNullWithoutDefault() List fields = new ArrayList<>(table.getFields()); Field newField = new Field("new_field"); newField.setDataType(INTEGER); - newField.setSqlForNew("id + 1"); + newField.setSqlForNew("\"id\" + 1"); fields.add(newField); table.setFields(fields); migrateTargetWithSchema(schema); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(new_field) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"new_field\") from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(2)); diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/CastFieldTypeTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/CastFieldTypeTest.java index da4ff26..27146ba 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/CastFieldTypeTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/CastFieldTypeTest.java @@ -1,11 +1,11 @@ package ch.ergon.adam.integrationtest.testcases; -import ch.ergon.adam.integrationtest.AbstractDbTestBase; -import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.integrationtest.TestDbUrlProvider; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.TestDbUrlProvider; import org.junit.jupiter.api.Test; import java.sql.ResultSet; @@ -19,15 +19,8 @@ public abstract class CastFieldTypeTest extends AbstractDbTestBase { - - protected static final String CREATE_TABLE_SQL = - "create table test_table (" + - "col1 varchar, " + - "col2 int " + - ")"; - protected static final String INSERT_DATA_SQL = - "insert into test_table values ('2', 2)"; + "insert into \"test_table\" values ('2', 2)"; public CastFieldTypeTest(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -37,7 +30,7 @@ public CastFieldTypeTest(TestDbUrlProvider testDbUrlProvider) { public void testCastVarcharToInt() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableStatement()); getTargetDbConnection().createStatement().execute(INSERT_DATA_SQL); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -55,7 +48,7 @@ public void testCastVarcharToInt() throws Exception { assertNotNull(table); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(col1) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"col1\") from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(2)); } @@ -64,7 +57,7 @@ public void testCastVarcharToInt() throws Exception { public void testCastIntToVarchar() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableStatement()); getTargetDbConnection().createStatement().execute(INSERT_DATA_SQL); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -82,10 +75,16 @@ public void testCastIntToVarchar() throws Exception { assertNotNull(table); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select count(col2) from test_table where col2= '2'"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select count(\"col2\") from \"test_table\" where \"col2\"= '2'"); assertTrue(result.next()); assertThat(result.getInt(1), is(1)); } - + protected String getCreateTableStatement() { + return + "create table \"test_table\" (" + + "\"col1\" varchar, " + + "\"col2\" int " + + ")"; + } } diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeArrayFieldTypeTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeArrayFieldTypeTest.java index 69de27e..c0cfb3a 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeArrayFieldTypeTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeArrayFieldTypeTest.java @@ -1,9 +1,9 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; import static ch.ergon.adam.core.db.schema.DataType.DECIMAL_INTEGER; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeFieldTypeTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeFieldTypeTest.java index 9b210ec..d956088 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeFieldTypeTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ChangeFieldTypeTest.java @@ -1,11 +1,11 @@ package ch.ergon.adam.integrationtest.testcases; -import ch.ergon.adam.integrationtest.AbstractDbTestBase; -import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.integrationtest.TestDbUrlProvider; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.TestDbUrlProvider; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -14,33 +14,34 @@ import static ch.ergon.adam.core.db.schema.DataType.CLOB; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public abstract class ChangeFieldTypeTest extends AbstractDbTestBase { - private static final String CREATE_TABLE_SERIAL_SQL = - "create table test_table (" + + protected String getCreateTableSerialSql() { + return "create table test_table (" + "id bigserial " + ")"; + } - private static final String CREATE_TABLE_NOT_NULL_SQL = - "create table test_table (" + + protected String getCreateTableNotNullSql() { + return "create table test_table (" + "test_field varchar not null " + ")"; + } - private static final String CREATE_TABLE_NULL_SQL = - "create table test_table (" + + protected String getCreateTableNullSql() { + return "create table test_table (" + "test_field numeric(10,2) null " + ")"; + } - private static final String CREATE_TABLE_TWO_FIELDS_SQL = - "create table test_table (" + + protected String getCreateTableTwoFieldsSql() { + return "create table test_table (" + "col1 text null, " + "col2 text null " + ")"; + } public ChangeFieldTypeTest(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -50,7 +51,7 @@ public ChangeFieldTypeTest(TestDbUrlProvider testDbUrlProvider) { public void changeFromSerialToClob() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_SERIAL_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableSerialSql()); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -84,7 +85,7 @@ public void changeFromSerialToClob() throws Exception { public void changeNotNullToNull() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_NOT_NULL_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableNotNullSql()); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -102,7 +103,7 @@ public void changeNotNullToNull() throws Exception { public void changeNullToNotNull() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_NULL_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableNullSql()); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -120,7 +121,7 @@ public void changeNullToNotNull() throws Exception { public void changePrecision() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_NULL_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableNullSql()); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -138,7 +139,7 @@ public void changePrecision() throws Exception { public void changeScale() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_NULL_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableNullSql()); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -156,7 +157,7 @@ public void changeScale() throws Exception { public void changeFieldOrder() throws Exception { // Setup db - getTargetDbConnection().createStatement().execute(CREATE_TABLE_TWO_FIELDS_SQL); + getTargetDbConnection().createStatement().execute(getCreateTableTwoFieldsSql()); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/DefaultTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/DefaultTest.java index 92100be..3feeb64 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/DefaultTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/DefaultTest.java @@ -1,9 +1,9 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -12,20 +12,23 @@ public abstract class DefaultTest extends AbstractDbTestBase { - private static final String CREATE_TABLE_INT_DEFAULT_SQL = - "create table test_table (" + + protected String getCreateTableIntDefaultSql() { + return "create table test_table (" + "id bigint default 1 " + ")"; + } - private static final String CREATE_TABLE_STRING_DEFAULT_SQL = - "create table test_table (" + + protected String getCreateTableStringDefaultSql() { + return "create table test_table (" + "id VARCHAR default 'defaultValue' " + ")"; + } - private static final String CREATE_TABLE_FUNCTION_DEFAULT_SQL = - "create table test_table (" + + protected String getCreateTableFunctionDefaultSql() { + return "create table test_table (" + "id bigint default char_length('defaultValue') " + ")"; + } public DefaultTest(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -33,20 +36,20 @@ public DefaultTest(TestDbUrlProvider testDbUrlProvider) { @Test public void testIntDefault() throws Exception { - doTestDefault(CREATE_TABLE_INT_DEFAULT_SQL, "1"); + doTestDefault(getCreateTableIntDefaultSql(), "1"); } @Test public void testStringDefault() throws Exception { - doTestDefault(CREATE_TABLE_STRING_DEFAULT_SQL, "'defaultValue'"); + doTestDefault(getCreateTableStringDefaultSql(), "'defaultValue'"); } @Test public void testFunctionDefault() throws Exception { - doTestDefault(CREATE_TABLE_FUNCTION_DEFAULT_SQL, "char_length('defaultValue')"); + doTestDefault(getCreateTableFunctionDefaultSql(), "char_length('defaultValue')"); } - private void doTestDefault(String sql, String expectedDefault) throws Exception { + protected void doTestDefault(String sql, String expectedDefault) throws Exception { // Setup db getSourceDbConnection().createStatement().execute(sql); @@ -64,7 +67,7 @@ private void doTestDefault(String sql, String expectedDefault) throws Exception @Test public void testDropDefault() throws Exception { - getSourceDbConnection().createStatement().execute(CREATE_TABLE_INT_DEFAULT_SQL); + getSourceDbConnection().createStatement().execute(getCreateTableIntDefaultSql()); sourceToTarget(); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -80,7 +83,7 @@ public void testDropDefault() throws Exception { @Test public void testChangeDefault() throws Exception { - getSourceDbConnection().createStatement().execute(CREATE_TABLE_INT_DEFAULT_SQL); + getSourceDbConnection().createStatement().execute(getCreateTableIntDefaultSql()); sourceToTarget(); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/EmptyDatabaseTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/EmptyDatabaseTest.java index 2ab0bb4..f7e09b7 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/EmptyDatabaseTest.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/EmptyDatabaseTest.java @@ -1,9 +1,9 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/IndexTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/IndexTests.java index a35697c..cfc8824 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/IndexTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/IndexTests.java @@ -1,26 +1,27 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; public abstract class IndexTests extends AbstractDbTestBase { - private static final String CREATE_TABLE_SQL = - "create table test_table (" + + protected String getCreateTableSql() { + return "create table test_table (" + "id integer null unique, " + "col1 varchar" + ")"; + } - private static final String CREATE_PARTIAL_INDEX_SQL = - "create unique index partial_idx on test_table(id) where col1 = 'test'"; + protected String getCreatePartialIndexSql() { + return "create unique index partial_idx on test_table(id) where col1 = 'test'"; + } public IndexTests(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -30,7 +31,7 @@ public IndexTests(TestDbUrlProvider testDbUrlProvider) { public void testCreateIndex() throws Exception { // Setup db - getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); + getSourceDbConnection().createStatement().execute(getCreateTableSql()); sourceToTarget(); DummySink dummySink = targetToDummy(); Schema schema = dummySink.getTargetSchema(); @@ -43,8 +44,29 @@ public void testCreateIndex() throws Exception { public void testRecreateIndexAfterTableChange() throws Exception { // Setup db - getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL); - getSourceDbConnection().createStatement().execute(CREATE_PARTIAL_INDEX_SQL); + getSourceDbConnection().createStatement().execute(getCreateTableSql()); + + sourceToTarget(); + DummySink dummySink = targetToDummy(); + Schema schema = dummySink.getTargetSchema(); + + // Apply change + schema.getTable("test_table").getField("id").setNullable(false); + migrateTargetWithSchema(schema); + dummySink = targetToDummy(); + schema = dummySink.getTargetSchema(); + + // Verify + assertFalse(schema.getTable("test_table").getField("id").isNullable()); + assertThat(schema.getTable("test_table").getIndexes().size(), is(1)); + } + + @Test + public void testRecreatePartialIndexAfterTableChange() throws Exception { + + // Setup db + getSourceDbConnection().createStatement().execute(getCreateTableSql()); + getSourceDbConnection().createStatement().execute(getCreatePartialIndexSql()); sourceToTarget(); DummySink dummySink = targetToDummy(); @@ -59,8 +81,6 @@ public void testRecreateIndexAfterTableChange() throws Exception { // Verify assertFalse(schema.getTable("test_table").getField("id").isNullable()); assertThat(schema.getTable("test_table").getIndexes().size(), is(2)); - if (!this.getClass().getSimpleName().contains("Sqlite")) { // https://github.com/jOOQ/jOOQ/issues/16683 - assertThat(schema.getTable("test_table").getIndex("partial_idx").getWhere(), is("(((col1)::text = 'test'::text))")); - } + assertThat(schema.getTable("test_table").getIndex("partial_idx").getWhere(), is("(((col1)::text = 'test'::text))")); } } diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RemoveFieldTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RemoveFieldTests.java index f5243de..831fcd3 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RemoveFieldTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RemoveFieldTests.java @@ -1,10 +1,10 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.core.db.schema.Table; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.schema.Schema; -import ch.ergon.adam.core.db.schema.Table; import org.junit.jupiter.api.Test; import java.sql.ResultSet; @@ -19,13 +19,13 @@ public abstract class RemoveFieldTests extends AbstractDbTestBase { private static final String CREATE_TABLE_SQL = - "create table test_table (" + - "col1 int, " + - "col2 int " + + "create table \"test_table\" (" + + "\"col1\" int, " + + "\"col2\" int " + ")"; private static final String INSERT_DATA_SQL = - "insert into test_table values (2, 3)"; + "insert into \"test_table\" values (2, 3)"; public RemoveFieldTests(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -53,7 +53,7 @@ public void testSimpleRemoveField() throws Exception { assertThat(table.getFields().size(), is(1)); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(col1) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"col1\") from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(2)); } @@ -83,7 +83,7 @@ public void testSimpleRemoveFieldWithTableRename() throws Exception { assertThat(newTable.getFields().size(), is(1)); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(col1) from new_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"col1\") from \"new_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(2)); } diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameFieldTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameFieldTests.java index efad966..c268886 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameFieldTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameFieldTests.java @@ -1,11 +1,11 @@ package ch.ergon.adam.integrationtest.testcases; -import ch.ergon.adam.integrationtest.AbstractDbTestBase; -import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.integrationtest.TestDbUrlProvider; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.TestDbUrlProvider; import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; @@ -20,12 +20,12 @@ public abstract class RenameFieldTests extends AbstractDbTestBase { private static final String CREATE_TABLE_SQL = - "create table test_table (" + - "col1 int " + + "create table \"test_table\" (" + + "\"col1\" int " + ")"; private static final String INSERT_DATA_SQL = - "insert into test_table values (2)"; + "insert into \"test_table\" values (2)"; public RenameFieldTests(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); @@ -43,7 +43,7 @@ public void testSimpleRenameField() throws Exception { // Apply change Table table = schema.getTable("test_table"); Field newColField = new Field("new_col"); - newColField.setSqlForNew("col1"); + newColField.setSqlForNew("\"col1\""); newColField.setDataType(INTEGER); newColField.setNullable(true); table.setFields(Lists.newArrayList(newColField)); @@ -59,7 +59,7 @@ public void testSimpleRenameField() throws Exception { assertNotNull(newColField); // Data still present? - ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(new_col) from test_table"); + ResultSet result = getTargetDbConnection().createStatement().executeQuery("select sum(\"new_col\") from \"test_table\""); assertTrue(result.next()); assertThat(result.getInt(1), is(2)); } diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameTableTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameTableTests.java index 46f9236..4f5d62f 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameTableTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/RenameTableTests.java @@ -1,11 +1,11 @@ package ch.ergon.adam.integrationtest.testcases; -import ch.ergon.adam.integrationtest.AbstractDbTestBase; -import ch.ergon.adam.integrationtest.DummySink; -import ch.ergon.adam.integrationtest.TestDbUrlProvider; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.integrationtest.AbstractDbTestBase; +import ch.ergon.adam.integrationtest.DummySink; +import ch.ergon.adam.integrationtest.TestDbUrlProvider; import org.junit.jupiter.api.Test; import java.sql.ResultSet; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/SequenceTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/SequenceTests.java index b7360e1..2983e02 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/SequenceTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/SequenceTests.java @@ -1,12 +1,12 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.SchemaDiffExtractor; +import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.core.db.schema.Sequence; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.AssertAnyChangeStrategy; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.SchemaDiffExtractor; -import ch.ergon.adam.core.db.schema.Schema; -import ch.ergon.adam.core.db.schema.Sequence; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ViewTests.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ViewTests.java index 5f8d909..62700d0 100644 --- a/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ViewTests.java +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/ViewTests.java @@ -1,9 +1,9 @@ package ch.ergon.adam.integrationtest.testcases; +import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.integrationtest.AbstractDbTestBase; import ch.ergon.adam.integrationtest.DummySink; import ch.ergon.adam.integrationtest.TestDbUrlProvider; -import ch.ergon.adam.core.db.schema.Schema; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -13,17 +13,17 @@ public abstract class ViewTests extends AbstractDbTestBase { private static final String CREATE_TABLE_SQL = - "create table test_table (" + - "id integer null " + + "create table \"test_table\" (" + + "\"id\" integer null " + ")"; private static final String CREATE_VIEW1_SQL = - "create view view1 as " + - "select * from test_table"; + "create view \"view1\" as " + + "select * from \"test_table\""; private static final String CREATE_VIEW2_SQL = - "create view view2 as " + - "select * from test_table"; + "create view \"view2\" as " + + "select * from \"test_table\""; public ViewTests(TestDbUrlProvider testDbUrlProvider) { super(testDbUrlProvider); diff --git a/jooq/src/main/java/ch/ergon/adam/jooq/JooqSink.java b/jooq/src/main/java/ch/ergon/adam/jooq/JooqSink.java index 2953f2d..581608c 100644 --- a/jooq/src/main/java/ch/ergon/adam/jooq/JooqSink.java +++ b/jooq/src/main/java/ch/ergon/adam/jooq/JooqSink.java @@ -51,7 +51,9 @@ public JooqSink(Connection connection, SQLDialect dialect) { protected JooqSink(Connection dbConnection, SQLDialect dialect, String schema) { context = DSL.using(dbConnection, dialect); - context.createSchemaIfNotExists(schema).execute(); + if (!dialect.family().name().contains("ORACLE")) { + context.createSchemaIfNotExists(schema).execute(); + } this.schema = schema; } @@ -94,7 +96,7 @@ public void dropForeignKey(ForeignKey foreignKey) { public void createForeignKey(ForeignKey foreignKey) { Index targetIndex = foreignKey.getTargetIndex(); org.jooq.Constraint constraint = DSL.constraint(foreignKey.getName()).foreignKey(foreignKey.getField().getName()) - .references(targetIndex.getTable().getName(), targetIndex.getFields().get(0).getName()); + .references(targetIndex.getTable().getName(), targetIndex.getFields().getFirst().getName()); context.alterTable(foreignKey.getTable().getName()).add(constraint).execute(); } diff --git a/jooq/src/main/java/ch/ergon/adam/jooq/JooqSource.java b/jooq/src/main/java/ch/ergon/adam/jooq/JooqSource.java index 365a3d9..44891ca 100644 --- a/jooq/src/main/java/ch/ergon/adam/jooq/JooqSource.java +++ b/jooq/src/main/java/ch/ergon/adam/jooq/JooqSource.java @@ -1,6 +1,7 @@ package ch.ergon.adam.jooq; import ch.ergon.adam.core.db.interfaces.SchemaSource; +import ch.ergon.adam.core.db.schema.*; import ch.ergon.adam.core.db.schema.DataType; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.ForeignKey; @@ -16,19 +17,19 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import static ch.ergon.adam.core.helper.CollectorsHelper.toLinkedMap; import static java.util.Arrays.stream; import static java.util.Comparator.comparing; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; +import static org.jooq.TableOptions.TableType.TABLE; +import static org.jooq.TableOptions.TableType.VIEW; -public class JooqSource implements SchemaSource { +public abstract class JooqSource implements SchemaSource { private final Connection connection; private final String schemaName; @@ -104,12 +105,18 @@ public Schema getSchema() { Schema schema = new Schema(); schema.setTables(getTables()); + schema.setViews(getViews()); + setViewDependencies(schema); return schema; } private Collection getTables() { List> jooqTables = getMeta().getTables(); + jooqTables = jooqTables.stream() + .filter(table -> table.getOptions().type() == TABLE) + .toList(); + Map tables = jooqTables.stream() .map(this::mapTableFromJooq) .sorted(comparing(Table::getName)) @@ -119,6 +126,35 @@ private Collection
getTables() { return tables.values(); } + private Collection getViews() { + List> jooqTables = getMeta().getTables(); + + Map views = jooqTables.stream() + .filter(table -> table.getOptions().type() == VIEW) + .map(this::mapViewFromJooq) + .sorted(comparing(View::getName)) + .collect(toLinkedMap(View::getName, identity())); + return views.values(); + } + + private void setViewDependencies(Schema schema) { + Map> dependencies = fetchViewDependencies(); + schema.getViews().stream() + .filter(v -> dependencies.containsKey(v.getName())) + .forEach(v -> { + dependencies.get(v.getName()).stream().map(base -> { + Relation r = schema.getTable(base); + if (r == null) { + r = schema.getView(base); + } + return r; + }) + .filter(Objects::nonNull) + .forEach(v::addBaseRelation); + }); + + } + private void mapForeignKeys(List> jooqTables, Map tables) { for (org.jooq.Table jooqTable : jooqTables) { Table table = tables.get(jooqTable.getName()); @@ -151,6 +187,17 @@ private Table mapTableFromJooq(org.jooq.Table jooqTable) { return table; } + private View mapViewFromJooq(org.jooq.Table jooqTable) { + View view = new View(jooqTable.getName()); + view.setFields(stream(jooqTable.fields()).map(this::mapFieldFromJooq).collect(toList())); + view.setViewDefinition(getViewDefinition(view.getName())); + return view; + } + + abstract protected String getViewDefinition(String name); + + abstract protected Map> fetchViewDependencies(); + private Index mapPrimaryKeyFromJooq(Table table, UniqueKey primaryKey) { Index index = mapUniqueKeyFromJooq(table, primaryKey); index.setPrimary(true); @@ -198,7 +245,6 @@ private Field mapFieldFromJooq(org.jooq.Field jooqField) { field.setPrecision(elementType.hasPrecision() && elementType.precision() > 0 && elementType.precision() < 10000 ? elementType.precision() : null); field.setScale(elementType.hasScale() && elementType.scale() > 0 ? elementType.scale() : null); - field.setSequence(isSequence(jooqField)); if (!field.isSequence() && jooqField.getDataType().defaulted()) { field.setDefaultValue(getDefaultValue(jooqField)); diff --git a/oracle/build.gradle b/oracle/build.gradle new file mode 100644 index 0000000..c226f59 --- /dev/null +++ b/oracle/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'java-library' + id "ch.ergon.gradle.goodies.versioning" +} + +ext.jooqProUser = hasProperty("jooqProUser") ? property("jooqProUser").toString() : rootProject.getLocalProperty("JOOQ_PRO_USER") +ext.jooqProPassword = hasProperty("jooqProPassword") ? property("jooqProPassword").toString() : rootProject.getLocalProperty("JOOQ_PRO_PASSWORD") + +repositories { + mavenCentral() + maven { + url = uri("https://repo.jooq.org/repo") + credentials { + username = jooqProUser + password = jooqProPassword + } + } +} + +group 'ch.ergon.adam' +description 'The Oracle plugin for ADAM' + +sourceCompatibility = 21 + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.1' + implementation project(':jooq') + implementation 'com.oracle.database.jdbc:ojdbc11:23.5.0.24.07' + api group: 'org.jooq.pro', name: 'jooq', version: '3.19.11' +} + +apply from: "${rootProject.projectDir}/publish.gradle" +apply from: "${rootProject.projectDir}/common.gradle" diff --git a/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlFactory.java b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlFactory.java new file mode 100644 index 0000000..b7d466b --- /dev/null +++ b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlFactory.java @@ -0,0 +1,69 @@ +package ch.ergon.adam.postgresql; + +import ch.ergon.adam.core.db.interfaces.SchemaSink; +import ch.ergon.adam.core.db.interfaces.SchemaSource; +import ch.ergon.adam.core.db.interfaces.SourceAndSinkAdapter; +import ch.ergon.adam.core.db.interfaces.SqlExecutor; + +import java.util.HashMap; +import java.util.Map; + +public class OracleSqlFactory implements SourceAndSinkAdapter { + + private static Map sqlSinksByUrl = new HashMap<>(); + + @Override + public boolean supportsUrl(String url) { + return url.toLowerCase().startsWith("jdbc:oracle:"); + } + + @Override + public SchemaSource createSource(String url) { + return getTransactionWrapper(url); + } + + @Override + public SchemaSink createSink(String url) { + return getTransactionWrapper(url); + } + + @Override + public SqlExecutor createSqlExecutor(String url) { + return getTransactionWrapper(url); + } + + public static synchronized OracleSqlTransactionWrapper getTransactionWrapper(String url) { + if (!sqlSinksByUrl.containsKey(url) || sqlSinksByUrl.get(url).isClosed()) { + sqlSinksByUrl.put(url, new OracleSqlTransactionWrapper(url, extractSchema(url), () -> closeConnection(url))); + } + OracleSqlTransactionWrapper connection = sqlSinksByUrl.get(url); + connection.increaseClientCount(); + return connection; + } + + public static void closeConnection(String url) { + OracleSqlTransactionWrapper connection = sqlSinksByUrl.get(url); + if (connection == null) { + return; + } + connection.decreaseClientCount(); + if (connection.getClientCount() > 0) { + return; + } + sqlSinksByUrl.get(url).reallyClose(); + sqlSinksByUrl.remove(url); + } + + private static String extractSchema(String url) { + int idx = url.indexOf("currentSchema="); + if (idx < 0) { + return "public"; + } + idx += "currentSchema=".length(); + int endIdx = url.indexOf("&", idx); + if (endIdx < 0) { + return url.substring(idx); + } + return url.substring(idx, endIdx); + } +} diff --git a/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlSink.java b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlSink.java new file mode 100644 index 0000000..4fa1e20 --- /dev/null +++ b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlSink.java @@ -0,0 +1,20 @@ +package ch.ergon.adam.postgresql; + +import ch.ergon.adam.jooq.JooqSink; +import ch.ergon.adam.core.db.schema.DbEnum; + +import java.sql.Connection; + +import static ch.ergon.adam.core.helper.CollectorsHelper.createQuotedList; +import static java.lang.String.format; +import static org.jooq.SQLDialect.ORACLE; + +public class OracleSqlSink extends JooqSink { + + private String schemaName; + + public OracleSqlSink(Connection dbConnection, String schema) { + super(dbConnection, ORACLE, schema); + this.schemaName = schema; + } +} diff --git a/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlSource.java b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlSource.java new file mode 100644 index 0000000..e3867e3 --- /dev/null +++ b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlSource.java @@ -0,0 +1,103 @@ +package ch.ergon.adam.postgresql; + +import ch.ergon.adam.core.db.schema.*; +import ch.ergon.adam.jooq.JooqSource; +import org.jooq.Record; +import org.jooq.Result; +import org.jooq.SQLDialect; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; + +import static java.lang.String.format; +import static java.util.Comparator.comparing; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.*; + +public class OracleSqlSource extends JooqSource { + + + private final String schemaName; + private Map enums; + + public OracleSqlSource(String url, String schemaName) throws SQLException { + super(url, schemaName); + this.schemaName = schemaName; + this.setSqlDialect(SQLDialect.ORACLE); + } + + public OracleSqlSource(Connection connection, String schemaName) { + super(connection, schemaName); + this.setSqlDialect(SQLDialect.ORACLE); + this.schemaName = schemaName; + } + + @Override + public Schema getSchema() { + Schema schema = super.getSchema(); + fetchConstraints(schema); + return schema; + } + + @Override + protected String getViewDefinition(String name) { + Result result = getContext().resultQuery("select TEXT from SYS.USER_VIEWS where VIEW_NAME = ?", name).fetch(); + return result.getFirst().getValue("TEXT").toString(); + } + + @Override + protected Map> fetchViewDependencies() { + Result result = getContext().resultQuery( + """ + SELECT NAME AS owner, REFERENCED_NAME AS base + FROM SYS.ALL_DEPENDENCIES + WHERE TYPE = 'VIEW' AND REFERENCED_TYPE IN ('VIEW', 'TABLE') + AND OWNER = ? + AND REFERENCED_OWNER = OWNER + """, schemaName).fetch(); + + return result.stream() + .collect( + groupingBy(r -> r.getValue("OWNER").toString(), + mapping(r -> r.getValue("BASE").toString(), toList()))); + } + + private void fetchConstraints(Schema schema) { + Result result = getContext().resultQuery( + """ + SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, SEARCH_CONDITION FROM ALL_CONSTRAINTS WHERE OWNER = ? + AND SEARCH_CONDITION_VC not like ('"%" IS NOT NULL') + """, schemaName).fetch(); + + Map> byTable = result.stream() + .filter(r -> schema.getTable(r.getValue("TABLE_NAME").toString()) != null) + .collect(groupingBy( + r -> schema.getTable(r.getValue("TABLE_NAME").toString()), + toList())); + + byTable.keySet().forEach(table -> + table.setConstraints(byTable.get(table).stream() + .map(this::mapConstraintFromOracle) + .collect(toList()))); + } + + private Constraint mapConstraintFromOracle(Record record) { + String constraintType = record.getValue("CONSTRAINT_TYPE").toString(); + String name = record.getValue("CONSTRAINT_NAME").toString(); + switch (constraintType) { + case "P": + return new PrimaryKeyConstraint(name); + case "C": + RuleConstraint ruleConstraint = new RuleConstraint(name); + String expression = record.getValue("SEARCH_CONDITION").toString(); + ruleConstraint.setRule(expression); + return ruleConstraint; + default: + throw new RuntimeException(format("Unsupported constraint type [%s]", constraintType)); + } + } + +} diff --git a/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlTransactionWrapper.java b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlTransactionWrapper.java new file mode 100644 index 0000000..cd99d14 --- /dev/null +++ b/oracle/src/main/java/ch/ergon/adam/postgresql/OracleSqlTransactionWrapper.java @@ -0,0 +1,262 @@ +package ch.ergon.adam.postgresql; + +import ch.ergon.adam.core.db.schema.*; +import ch.ergon.adam.jooq.JooqSqlExecutor; +import ch.ergon.adam.core.db.interfaces.SchemaSink; +import ch.ergon.adam.core.db.interfaces.SchemaSource; +import ch.ergon.adam.core.db.interfaces.SqlExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class OracleSqlTransactionWrapper implements SchemaSource, SchemaSink, SqlExecutor { + + private static final Logger logger = LoggerFactory.getLogger(OracleSqlTransactionWrapper.class); + + private final Connection dbConnection; + private final Runnable closeHandler; + JooqSqlExecutor sqlExecutor; + OracleSqlSink sqlSink; + OracleSqlSource sqlSource; + private boolean closed; + private int clientCount = 0; + + public OracleSqlTransactionWrapper(String url, String schema, Runnable closeHandler) { + this.closeHandler = closeHandler; + try { + dbConnection = DriverManager.getConnection(url); + sqlSink = new OracleSqlSink(dbConnection, schema); + sqlSource = new OracleSqlSource(dbConnection, schema); + beginTransaction(); + sqlExecutor = new JooqSqlExecutor(dbConnection, schema); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void increaseClientCount() { + clientCount++; + } + + public void decreaseClientCount() { + clientCount--; + } + + public int getClientCount() { + return clientCount; + } + + public void beginTransaction() { + try { + dbConnection.setAutoCommit(false); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void commitTransaction() { + try { + logger.info("Doing a commit of the changes."); + dbConnection.commit(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void rollbackTransaction() { + try { + if (dbConnection.isClosed()) { + return; + } + logger.info("Doing a rollback of the changes."); + dbConnection.rollback(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void setTargetSchema(Schema targetSchema) { + sqlSink.setTargetSchema(targetSchema); + } + + @Override + public void commitChanges() { + sqlSink.commitChanges(); + } + + @Override + public void rollback() { + rollbackTransaction(); + } + + @Override + public void dropForeignKey(ForeignKey foreignKey) { + sqlSink.dropForeignKey(foreignKey); + } + + @Override + public void createForeignKey(ForeignKey foreignKey) { + sqlSink.createForeignKey(foreignKey); + } + + @Override + public void dropIndex(Index index) { + sqlSink.dropIndex(index); + } + + @Override + public void createIndex(Index index) { + sqlSink.createIndex(index); + } + + @Override + public void addField(Field field) { + sqlSink.addField(field); + } + + @Override + public void dropField(Field field, Table table) { + sqlSink.dropField(field, table); + } + + @Override + public void setDefault(Field field) { + sqlSink.setDefault(field); + } + + @Override + public void dropDefault(Field field) { + sqlSink.dropDefault(field); + } + + @Override + public void createTable(Table table) { + sqlSink.createTable(table); + } + + @Override + public void dropTable(Table table) { + sqlSink.dropTable(table); + } + + @Override + public void renameTable(Table oldTable, String targetTableName) { + sqlSink.renameTable(oldTable, targetTableName); + } + + @Override + public void copyData(Table sourceTable, Table targetTable, String sourceTableName) { + sqlSink.copyData(sourceTable, targetTable, sourceTableName); + } + + @Override + public void createView(View view) { + sqlSink.createView(view); + } + + @Override + public void dropView(View view) { + sqlSink.dropView(view); + } + + @Override + public void dropEnum(DbEnum dbEnum) { + sqlSink.dropEnum(dbEnum); + } + + @Override + public void createEnum(DbEnum dbEnum) { + sqlSink.createEnum(dbEnum); + } + + @Override + public void changeFieldType(Field oldField, Field newField, DataType targetDataType) { + sqlSink.changeFieldType(oldField, newField, targetDataType); + } + + @Override + public void dropConstraint(Constraint constraint) { + sqlSink.dropConstraint(constraint); + } + + @Override + public void createConstraint(Constraint constraint) { + sqlSink.createConstraint(constraint); + } + + @Override + public void dropSequence(Sequence sequence) { + sqlSink.dropSequence(sequence); + } + + @Override + public void createSequence(Sequence sequence) { + sqlSink.createSequence(sequence); + } + + @Override + public void dropSequencesAndDefaults(Table table) { + sqlSink.dropSequencesAndDefaults(table); + } + + @Override + public void executeScript(String script) { + sqlExecutor.executeScript(script); + } + + @Override + public Object queryResult(String query, Object... params) { + return sqlExecutor.queryResult(query, params); + } + + @Override + public void dropSchema() { + sqlExecutor.dropSchema(); + } + + @Override + public void close() { + closeHandler.run(); + } + + public void reallyClose() { + this.closed = true; + try { + if (!dbConnection.getAutoCommit()) { + commitTransaction(); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + sqlExecutor.close(); + sqlSink.close(); + sqlSource.close(); + try { + dbConnection.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public boolean isClosed() { + return closed; + } + + @Override + public boolean supportAlterAndDropField() { + return sqlSink.supportAlterAndDropField(); + } + + @Override + public Schema getSchema() { + return sqlSource.getSchema(); + } + + public Connection getConnection() { + return dbConnection; + } +} diff --git a/postgresql/src/main/java/ch/ergon/adam/postgresql/PostgreSqlSource.java b/postgresql/src/main/java/ch/ergon/adam/postgresql/PostgreSqlSource.java index 152fb88..3c4e7d2 100644 --- a/postgresql/src/main/java/ch/ergon/adam/postgresql/PostgreSqlSource.java +++ b/postgresql/src/main/java/ch/ergon/adam/postgresql/PostgreSqlSource.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static ch.ergon.adam.core.db.schema.DataType.ENUM; import static ch.ergon.adam.core.helper.CollectorsHelper.toLinkedMap; @@ -20,8 +21,7 @@ import static java.util.Comparator.comparing; import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.*; public class PostgreSqlSource extends JooqSource { @@ -51,59 +51,11 @@ public Schema getSchema() { Schema schema = super.getSchema(); schema.setSequences(getSequences()); schema.setEnums(enums.values()); - schema.setViews(getViews()); - schema.getViews().forEach(view -> view.setFields(schema.getTable(view.getName()).getFields())); - schema.getTables().removeIf(table -> schema.getView(table.getName()) != null); - fetchDefaults(schema); setCustomTypes(schema); fetchConstraints(schema); - fetchViewDependencies(schema); return schema; } - private void fetchDefaults(Schema schema) { - //TODO: Remove as soon as https://github.com/jOOQ/jOOQ/issues/8875 is fixed - - Result result = getContext().resultQuery( - "SELECT column_name, column_default, table_name\n" + - "FROM information_schema.columns where table_schema = ? and column_default is not null", schemaName).fetch(); - - for (Record record : result) { - String tableName = record.getValue("table_name").toString(); - String columnName = record.getValue("column_name").toString(); - String defaultValue = record.getValue("column_default").toString(); - - Table table = schema.getTable(tableName); - if (table == null) { - continue; - } - - Field field = table.getField(columnName); - if (field == null || field.isSequence() || (field.getDefaultValue() != null && field.getDefaultValue() != "null")) { - continue; - } - - field.setDefaultValue(defaultValue); - } - - for (Table table : schema.getTables()) { - for (Field field : table.getFields()) { - String defaultValue = field.getDefaultValue(); - if (defaultValue == null) { - continue; - } - - if (field.isArray() && defaultValue.endsWith("[]")) { - defaultValue = defaultValue.substring(0, defaultValue.length() - 2); - } - - field.setDefaultValue(defaultValue.replaceAll("::[a-z A-Z_]*", "")); - } - - } - - } - @Override protected String getDefaultValue(org.jooq.Field jooqField) { String defaultValue = super.getDefaultValue(jooqField); @@ -115,9 +67,10 @@ protected String getDefaultValue(org.jooq.Field jooqField) { return defaultValue; } - private void fetchViewDependencies(Schema schema) { + @Override + protected Map> fetchViewDependencies() { Result result = getContext().resultQuery( - "SELECT cl_r.relname AS view, cl_d.relname AS base, cl_d.relkind basetype " + + "SELECT cl_r.relname AS view, cl_d.relname AS base " + "FROM pg_rewrite AS r " + "JOIN pg_class AS cl_r ON r.ev_class=cl_r.oid " + "JOIN pg_depend AS d ON r.oid = d.objid " + @@ -126,15 +79,10 @@ private void fetchViewDependencies(Schema schema) { "WHERE cl_d.relkind IN ('v', 'r') and cl_r.relname != cl_d.relname AND ns.nspname = ? " + "GROUP BY cl_r.relname, cl_d.relname, cl_d.relkind", schemaName).fetch(); - for (Record record : result) { - String viewName = record.getValue("view").toString(); - String baseName = record.getValue("base").toString(); - String baseType = record.getValue("basetype").toString(); - - View view = schema.getView(viewName); - Relation relation = "v".equals(baseType) ? schema.getView(baseName) : schema.getTable(baseName); - view.addBaseRelation(relation); - } + return result.stream() + .collect( + groupingBy(r -> r.getValue("view").toString(), + mapping(r -> r.getValue("base").toString(), toList()))); } private void setCustomTypes(Schema schema) { @@ -235,17 +183,10 @@ private DbEnum mapEnumFromPostgres(Record record) { return dbEnum; } - private Collection getViews() { - Result result = getContext().resultQuery("select table_name, view_definition from INFORMATION_SCHEMA.views where table_schema = ?", schemaName).fetch(); - return result.stream().map(this::mapViewFromJooq).sorted(comparing(View::getName)).collect(toList()); - } - - private View mapViewFromJooq(Record record) { - String viewName = record.getValue("table_name").toString(); - String viewDefinition = record.getValue("view_definition").toString(); - View view = new View(viewName); - view.setViewDefinition(viewDefinition); - return view; + @Override + protected String getViewDefinition(String name) { + Result result = getContext().resultQuery("select view_definition from INFORMATION_SCHEMA.views where table_schema = ? and table_name = ?", schemaName, name).fetch(); + return result.getFirst().getValue("view_definition").toString(); } @Override diff --git a/settings.gradle b/settings.gradle index b6eea88..0f17bdb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,6 +3,7 @@ include 'core' include 'jooq' include 'yml' include 'postgresql' +include 'oracle' include 'integration-test' include 'sqlite' include 'gradle-plugin' diff --git a/sqlite/src/main/java/ch/ergon/adam/sqlite/SqliteSource.java b/sqlite/src/main/java/ch/ergon/adam/sqlite/SqliteSource.java index dd80135..8822ac6 100644 --- a/sqlite/src/main/java/ch/ergon/adam/sqlite/SqliteSource.java +++ b/sqlite/src/main/java/ch/ergon/adam/sqlite/SqliteSource.java @@ -13,6 +13,7 @@ import java.sql.SQLException; import java.util.Collection; import java.util.List; +import java.util.Map; import static com.google.common.collect.Lists.newArrayList; import static java.util.Comparator.comparing; @@ -35,25 +36,21 @@ public SqliteSource(Connection connection) { @Override public Schema getSchema() { Schema schema = super.getSchema(); - schema.setViews(getViews()); - schema.getViews().forEach(view -> view.setFields(schema.getTable(view.getName()).getFields())); - schema.getTables().removeIf(table -> schema.getView(table.getName()) != null); setSequences(schema); return schema; } - private Collection getViews() { - Result result = getContext().resultQuery("select name, sql from sqlite_master where type = 'view'").fetch(); - return result.stream().map(this::mapViewFromJooq).sorted(comparing(View::getName)).collect(toList()); + @Override + protected String getViewDefinition(String name) { + Result result = getContext().resultQuery("select sql from sqlite_master where type = 'view' and name = ?", name).fetch(); + String viewDefinition = result.getFirst().getValue("sql").toString(); + viewDefinition = viewDefinition.replaceAll("^(?i)create view [^ ]+ as ", ""); + return viewDefinition; } - private View mapViewFromJooq(Record record) { - String viewName = record.getValue("name").toString(); - String viewDefinition = record.getValue("sql").toString(); - viewDefinition = viewDefinition.replaceAll("^(?i)create view [^ ]+ as ", ""); - View view = new View(viewName); - view.setViewDefinition(viewDefinition); - return view; + @Override + protected Map> fetchViewDependencies() { + return Map.of(); } private void setSequences(Schema schema) {