From 9c997d02a41c8ff593c661313497f3e451d54786 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Wed, 21 Feb 2024 18:06:59 +0100 Subject: [PATCH] Implement feature switch for data type converter --- doc/dialects/exasol.md | 2 +- .../exasol/ExasolFromExaQueryRewriter.java | 27 ++++ ...xasolFromExaWithDataTypeQueryRewriter.java | 6 +- .../dialects/exasol/ExasolSqlDialect.java | 2 +- .../ExasolColumnMetadataReaderTest.java | 12 +- .../ExasolFromExaQueryRewriterTest.java | 76 +++++++++++ ...lFromExaWithDataTypeQueryRewriterTest.java | 18 +-- .../ExasolSqlDialectExaConnectionIT.java | 124 ++++++++++++++++++ ...qlDialectExaConnectionWithDataTypesIT.java | 5 +- .../dialects/exasol/ExasolSqlDialectTest.java | 75 ++++++----- .../exasol/IntegrationTestConfiguration.java | 2 +- .../exasol/reflect/ReflectionException.java | 13 -- .../com/exasol/reflect/ReflectionUtils.java | 31 ----- 13 files changed, 294 insertions(+), 99 deletions(-) create mode 100644 src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriter.java create mode 100644 src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriterTest.java create mode 100644 src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionIT.java delete mode 100644 src/test/java/com/exasol/reflect/ReflectionException.java delete mode 100644 src/test/java/com/exasol/reflect/ReflectionUtils.java diff --git a/doc/dialects/exasol.md b/doc/dialects/exasol.md index 316112a..4af9e55 100644 --- a/doc/dialects/exasol.md +++ b/doc/dialects/exasol.md @@ -17,7 +17,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```sql CREATE JAVA ADAPTER SCRIPT SCHEMA_FOR_VS_SCRIPT.ADAPTER_SCRIPT_EXASOL AS %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-11.0.2-exasol-7.1.7.jar; + %jar /buckets///virtual-schema-dist-12.0.0-exasol-7.2.0.jar; / ``` diff --git a/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriter.java b/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriter.java new file mode 100644 index 0000000..fc6bbac --- /dev/null +++ b/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriter.java @@ -0,0 +1,27 @@ +package com.exasol.adapter.dialects.exasol; + +import com.exasol.adapter.dialects.SqlDialect; +import com.exasol.adapter.dialects.rewriting.AbstractQueryRewriter; +import com.exasol.adapter.jdbc.RemoteMetadataReader; + +/** + * Exasol-specific query rewriter for {@code IMPORT FROM EXA} that does not add data types to the pushdown query. Data + * types like {@code HASHTYPE} will be reported as {@code VARCHAR}. + */ +public class ExasolFromExaQueryRewriter extends AbstractQueryRewriter { + + /** + * Create a new instance of the {@link ExasolFromExaQueryRewriter}. + * + * @param dialect dialect + * @param remoteMetadataReader remote metadata reader + */ + public ExasolFromExaQueryRewriter(final SqlDialect dialect, final RemoteMetadataReader remoteMetadataReader) { + super(dialect, remoteMetadataReader, new ExasolConnectionDefinitionBuilder()); + } + + @Override + protected String generateImportStatement(final String connectionDefinition, final String pushdownQuery) { + return "IMPORT FROM EXA " + connectionDefinition + " STATEMENT '" + pushdownQuery.replace("'", "''") + "'"; + } +} diff --git a/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriter.java b/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriter.java index 5def2da..3fdc0ed 100644 --- a/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriter.java +++ b/src/main/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriter.java @@ -11,8 +11,10 @@ import com.exasol.adapter.metadata.DataType; /** - * Exasol-specific query rewriter for {@code IMPORT FROM EXA}. It is similar to {@link ExasolJdbcQueryRewriter} but uses - * {@code IMPORT INTO (...) FROM EXA}. + * Exasol-specific query rewriter for {@code IMPORT FROM EXA} that adds data types to the pushdown query. Data types + * like {@code HASHTYPE} will be reported correctly. + *

+ * This rewriter is similar to {@link ExasolJdbcQueryRewriter} but uses {@code IMPORT INTO (...) FROM EXA}. */ class ExasolFromExaWithDataTypeQueryRewriter extends AbstractQueryRewriter { diff --git a/src/main/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialect.java b/src/main/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialect.java index 53ec676..ee3d424 100644 --- a/src/main/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialect.java +++ b/src/main/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialect.java @@ -98,7 +98,7 @@ protected QueryRewriter createQueryRewriter() { return new ExasolFromExaWithDataTypeQueryRewriter(this, createRemoteMetadataReader(), this.connectionFactory); } else { - + return new ExasolFromExaQueryRewriter(this, createRemoteMetadataReader()); } } else { return new ExasolJdbcQueryRewriter(this, createRemoteMetadataReader(), this.connectionFactory); diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolColumnMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolColumnMetadataReaderTest.java index e16a9f2..e3e0d87 100644 --- a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolColumnMetadataReaderTest.java +++ b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolColumnMetadataReaderTest.java @@ -104,7 +104,8 @@ void testMapJdbcTypeVarchar() { @Test void testMapJdbcTypeVarcharAscii() { - assertTypeMapped(varchar(5).byteSize(5), DataType.createVarChar(5, ExaCharset.ASCII)); + // virtual-schema-common-jdbc 12.0.0 maps this to UTF8 + assertTypeMapped(varchar(5).byteSize(5), DataType.createVarChar(5, ExaCharset.UTF8)); } @Test @@ -124,7 +125,8 @@ void testMapJdbcTypeCharUtf8() { @Test void testMapJdbcTypeCharAscii() { - assertTypeMapped(charType(5).byteSize(5), DataType.createChar(5, ExaCharset.ASCII)); + // virtual-schema-common-jdbc 12.0.0 maps this to UTF8 + assertTypeMapped(charType(5).byteSize(5), DataType.createChar(5, ExaCharset.UTF8)); } @Test @@ -133,12 +135,12 @@ void testMapJdbcTypeLongCharMappedToVarchar() { } @Test - void testMapJdbcTypeLongCharMappedToVarcharMaxVarcharLenth() { + void testMapJdbcTypeLongCharMappedToVarcharMaxVarcharLength() { assertTypeMapped(charType(2_000_001), DataType.createVarChar(2_000_000, ExaCharset.UTF8)); } @Test - void testMapJdbcTypeCharWithUnknownJdbcTypenName() { + void testMapJdbcTypeCharWithUnknownJdbcTypeName() { assertTypeMapped(charType(5).typeName("unknown"), DataType.createChar(5, ExaCharset.UTF8)); } @@ -239,4 +241,4 @@ JDBCTypeDescription build() { this.typeName); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriterTest.java b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriterTest.java new file mode 100644 index 0000000..462ebc6 --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaQueryRewriterTest.java @@ -0,0 +1,76 @@ +package com.exasol.adapter.dialects.exasol; + +import static com.exasol.adapter.AdapterProperties.CONNECTION_NAME_PROPERTY; +import static com.exasol.adapter.dialects.exasol.ExasolProperties.EXASOL_CONNECTION_PROPERTY; +import static com.exasol.adapter.dialects.exasol.ExasolProperties.EXASOL_IMPORT_PROPERTY; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.exasol.ExaMetadata; +import com.exasol.adapter.AdapterException; +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.*; +import com.exasol.adapter.jdbc.ConnectionFactory; +import com.exasol.adapter.jdbc.RemoteMetadataReader; +import com.exasol.adapter.metadata.DataType; +import com.exasol.adapter.sql.TestSqlStatementFactory; + +@ExtendWith(MockitoExtension.class) +class ExasolFromExaQueryRewriterTest { + private static final List EMPTY_SELECT_LIST_DATA_TYPES = Collections.emptyList(); + @Mock + private RemoteMetadataReader metadataReaderMock; + @Mock + private ConnectionFactory connectionFactoryMock; + @Mock + private ExaMetadata exaMetadataMock; + @Mock + private SqlDialect dialectMock; + @Mock + private SqlGenerator sqlGeneratorMock; + @Mock + private Connection connectionMock; + + @Test + void rewritePushdownQuery() throws AdapterException, SQLException { + final AdapterProperties properties = createAdapterProperties(); + final SqlDialect dialect = new ExasolSqlDialect(connectionFactoryMock, properties); + final QueryRewriter queryRewriter = new ExasolFromExaQueryRewriter(dialect, + new ExasolMetadataReader(connectionMock, properties)); + assertThat( + queryRewriter.rewrite(TestSqlStatementFactory.createSelectOneFromDual(), EMPTY_SELECT_LIST_DATA_TYPES, + exaMetadataMock, properties), + equalTo("IMPORT FROM EXA AT \"THE_EXA_CONNECTION\" STATEMENT 'SELECT 1 FROM \"DUAL\"'")); + } + + private AdapterProperties createAdapterProperties() { + return new AdapterProperties(Map.of(EXASOL_IMPORT_PROPERTY, "true", // + CONNECTION_NAME_PROPERTY, "exasol_connection", // + EXASOL_CONNECTION_PROPERTY, "THE_EXA_CONNECTION", // + "GENERATE_JDBC_DATATYPE_MAPPING_FOR_EXA", "false")); + } + + @Test + void rewritePushdownQueryEscapesSingleQuotes() throws AdapterException, SQLException { + final AdapterProperties properties = createAdapterProperties(); + when(dialectMock.getSqlGenerator(any())).thenReturn(sqlGeneratorMock); + when(sqlGeneratorMock.generateSqlFor(any())).thenReturn("string ' with '' quotes \"..."); + final QueryRewriter queryRewriter = new ExasolFromExaQueryRewriter(dialectMock, + new ExasolMetadataReader(connectionMock, properties)); + assertThat( + queryRewriter.rewrite(TestSqlStatementFactory.createSelectOneFromDual(), EMPTY_SELECT_LIST_DATA_TYPES, + exaMetadataMock, properties), + equalTo("IMPORT FROM EXA AT \"THE_EXA_CONNECTION\" STATEMENT 'string '' with '''' quotes \"...'")); + } +} diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriterTest.java b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriterTest.java index 3a62e27..c9b24a3 100644 --- a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriterTest.java +++ b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolFromExaWithDataTypeQueryRewriterTest.java @@ -1,8 +1,7 @@ package com.exasol.adapter.dialects.exasol; import static com.exasol.adapter.AdapterProperties.CONNECTION_NAME_PROPERTY; -import static com.exasol.adapter.dialects.exasol.ExasolProperties.EXASOL_CONNECTION_PROPERTY; -import static com.exasol.adapter.dialects.exasol.ExasolProperties.EXASOL_IMPORT_PROPERTY; +import static com.exasol.adapter.dialects.exasol.ExasolProperties.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.ArgumentMatchers.any; @@ -44,9 +43,7 @@ class ExasolFromExaWithDataTypeQueryRewriterTest { void rewritePushdownQuery() throws AdapterException, SQLException { final Connection connectionMock = mockConnection(); when(connectionFactoryMock.getConnection()).thenReturn(connectionMock); - final AdapterProperties properties = new AdapterProperties(Map.of(EXASOL_IMPORT_PROPERTY, "true", // - CONNECTION_NAME_PROPERTY, "exasol_connection", // - EXASOL_CONNECTION_PROPERTY, "THE_EXA_CONNECTION")); + final AdapterProperties properties = createAdapterProperties(); final SqlDialect dialect = new ExasolSqlDialect(connectionFactoryMock, properties); final QueryRewriter queryRewriter = new ExasolFromExaWithDataTypeQueryRewriter(dialect, new ExasolMetadataReader(connectionMock, properties), connectionFactoryMock); @@ -57,13 +54,18 @@ void rewritePushdownQuery() throws AdapterException, SQLException { + " STATEMENT 'SELECT 1 FROM \"DUAL\"'")); } + private AdapterProperties createAdapterProperties() { + return new AdapterProperties(Map.of(EXASOL_IMPORT_PROPERTY, "true", // + CONNECTION_NAME_PROPERTY, "exasol_connection", // + EXASOL_CONNECTION_PROPERTY, "THE_EXA_CONNECTION", // + GENERATE_JDBC_DATATYPE_MAPPING_FOR_EXA, "true")); + } + @Test void rewritePushdownQueryEscapesSingleQuotes() throws AdapterException, SQLException { final Connection connectionMock = mockConnection(); when(connectionFactoryMock.getConnection()).thenReturn(connectionMock); - final AdapterProperties properties = new AdapterProperties(Map.of(EXASOL_IMPORT_PROPERTY, "true", // - CONNECTION_NAME_PROPERTY, "exasol_connection", // - EXASOL_CONNECTION_PROPERTY, "THE_EXA_CONNECTION")); + final AdapterProperties properties = createAdapterProperties(); when(dialectMock.getSqlGenerator(any())).thenReturn(sqlGeneratorMock); when(sqlGeneratorMock.generateSqlFor(any())).thenReturn("string ' with '' quotes \"..."); final QueryRewriter queryRewriter = new ExasolFromExaWithDataTypeQueryRewriter(dialectMock, diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionIT.java b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionIT.java new file mode 100644 index 0000000..62d20ff --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionIT.java @@ -0,0 +1,124 @@ +package com.exasol.adapter.dialects.exasol; + +import static com.exasol.adapter.dialects.exasol.ExasolProperties.EXASOL_CONNECTION_PROPERTY; +import static com.exasol.matcher.ResultSetStructureMatcher.table; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.*; +import org.testcontainers.containers.JdbcDatabaseContainer.NoDriverFoundException; + +import com.exasol.adapter.properties.PropertyValidationException; +import com.exasol.dbbuilder.dialects.Table; +import com.exasol.dbbuilder.dialects.exasol.ConnectionDefinition; + +/** + * This class exercises a set of tests defined in the base class on a local Exasol, using {@code IMPORT} via a EXA + * connection. + *

+ * In this case the Adapter uses a different (JDBC) connection to attach to the database than the ExaLoader which runs + * this {@code IMPORT}. + *

+ *

+ * These tests take the following specialties of a local connection into account: + *

+ *
    + *
  • {@code INTERVAL} types are reported with JDBC type name {@code VARCHAR} in ResultSets
  • + *
  • {@code HASHTYPE} types are reported with JDBC type name {@code VARCHAR} in ResultSets
  • + *
  • {@code GEOMETRY} types are reported with JDBC type name {@code VARCHAR} in ResultSets
  • + *
      + */ +class ExasolSqlDialectExaConnectionIT extends AbstractRemoteExasolVirtualSchemaConnectionIT { + private static final String EXA_CONNECTION_NAME = "EXA_CONNECTION"; + private ConnectionDefinition exaConnection; + + @Override + @BeforeEach + void beforeEach() { + super.beforeEach(); + this.exaConnection = objectFactory.createConnectionDefinition(EXA_CONNECTION_NAME, getTargetAddress(), + this.user.getName(), this.user.getPassword()); + } + + private String getTargetAddress() { + return "127.0.0.1" + "/" + EXASOL.getTlsCertificateFingerprint().orElseThrow() + ":" + + EXASOL.getDefaultInternalDatabasePort(); + } + + @Override + @AfterEach + void afterEach() { + dropAll(this.exaConnection); + this.exaConnection = null; + super.afterEach(); + } + + @Override + protected Set expectVarcharFor() { + return Set.of("GEOMETRY", "INTERVAL", "INTERVAL YEAR TO MONTH", "INTERVAL DAY TO SECOND", "HASHTYPE"); + } + + @Override + protected Map getConnectionSpecificVirtualSchemaProperties() { + return Map.of("IMPORT_FROM_EXA", "true", EXASOL_CONNECTION_PROPERTY, this.exaConnection.getName()); + } + + @Test + void testPasswordNotVisibleInImportFromExa() throws NoDriverFoundException, SQLException { + final Table table = this.sourceSchema.createTable("T1", "C1", "VARCHAR(20)").insert("Hello."); + this.virtualSchema = createVirtualSchema(this.sourceSchema); + final String sql = "SELECT * FROM " + this.virtualSchema.getFullyQualifiedName() + ".\"" + table.getName() + + "\""; + assertThat(explainVirtual(sql), // + table().row( // + anything(), // + not(anyOf( // + containsString(this.user.getName()), // + containsString(this.user.getPassword()), // + containsString(EXASOL.getUsername()), // + containsString(EXASOL.getPassword()) // + )), // + anything(), // + anything() // + ).matches()); + } + + @Test + void testAlterVirtualSchemaTriggersPropertyValidation() throws SQLException { + this.virtualSchema = createVirtualSchema(this.sourceSchema); + final String name = this.virtualSchema.getFullyQualifiedName(); + final SQLException exception = assertThrows(SQLException.class, + () -> query("alter virtual schema {0} set EXA_CONNECTION = Null", name)); + final String expected = PropertyValidationException.class.getName() + ": E-VSCJDBC-17"; + assertThat(exception.getMessage(), containsString(expected)); + } + + private ResultSet explainVirtual(final String sql) throws SQLException { + return query("EXPLAIN VIRTUAL " + sql); + } + + @Override + @Test + void testCharMappingAscii() { + final Table table = createSingleColumnTable("CHAR(20) ASCII").insert("sun").insert("rain"); + assertVirtualTableContents(table, table("VARCHAR").row(pad("sun", 20)).row(pad("rain", 20)).matches()); + } + + @Override + @Test + void testCharMappingUtf8() { + verifyCharMappingUtf8("VARCHAR"); + } + + @Override + @Test + void testCastVarcharToChar() { + castFrom("VARCHAR(20)").to("CHAR(40)").input("Hello.").accept("VARCHAR").verify(pad("Hello.", 40)); + } +} diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionWithDataTypesIT.java b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionWithDataTypesIT.java index df666d0..019de28 100644 --- a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionWithDataTypesIT.java +++ b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectExaConnectionWithDataTypesIT.java @@ -1,6 +1,5 @@ package com.exasol.adapter.dialects.exasol; -import static com.exasol.adapter.dialects.exasol.ExasolProperties.EXASOL_CONNECTION_PROPERTY; import static com.exasol.matcher.ResultSetStructureMatcher.table; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; @@ -58,7 +57,9 @@ protected Set expectVarcharFor() { @Override protected Map getConnectionSpecificVirtualSchemaProperties() { - return Map.of("IMPORT_FROM_EXA", "true", EXASOL_CONNECTION_PROPERTY, this.exaConnection.getName()); + return Map.of("IMPORT_FROM_EXA", "true", // + "EXA_CONNECTION", this.exaConnection.getName(), // + "GENERATE_JDBC_DATATYPE_MAPPING_FOR_EXA", "true"); } @Test diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectTest.java b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectTest.java index f90253a..c9a7f20 100644 --- a/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectTest.java +++ b/src/test/java/com/exasol/adapter/dialects/exasol/ExasolSqlDialectTest.java @@ -4,7 +4,6 @@ import static com.exasol.adapter.capabilities.MainCapability.*; import static com.exasol.adapter.dialects.exasol.ExasolProperties.*; import static com.exasol.adapter.dialects.exasol.ExasolSqlDialect.EXASOL_TIMESTAMP_WITH_LOCAL_TIME_ZONE_SWITCH; -import static com.exasol.reflect.ReflectionUtils.getMethodReturnViaReflection; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -32,8 +31,7 @@ import com.exasol.adapter.adapternotes.ColumnAdapterNotes; import com.exasol.adapter.adapternotes.ColumnAdapterNotesJsonConverter; import com.exasol.adapter.capabilities.*; -import com.exasol.adapter.dialects.SqlDialect; -import com.exasol.adapter.dialects.SqlGenerator; +import com.exasol.adapter.dialects.*; import com.exasol.adapter.dialects.rewriting.SqlGenerationContext; import com.exasol.adapter.jdbc.ConnectionFactory; import com.exasol.adapter.metadata.*; @@ -46,12 +44,18 @@ class ExasolSqlDialectTest { @Mock private ConnectionFactory connectionFactoryMock; private ExasolSqlDialect dialect; - private Map rawProperties; @BeforeEach void beforeEach() { - this.dialect = new ExasolSqlDialect(this.connectionFactoryMock, AdapterProperties.emptyProperties()); - this.rawProperties = new HashMap<>(); + this.dialect = testee(AdapterProperties.emptyProperties()); + } + + private ExasolSqlDialect testee(final Map rawProperties) { + return testee(new AdapterProperties(rawProperties)); + } + + private ExasolSqlDialect testee(final AdapterProperties properties) { + return new ExasolSqlDialect(this.connectionFactoryMock, properties); } @CsvSource({ "A1, \"A1\"", // @@ -140,50 +144,51 @@ private TableMetadata getClicksTableMetadata() { @Test void testCreateRemoteMetadataReader(@Mock final Connection connectionMock) throws SQLException { when(this.connectionFactoryMock.getConnection()).thenReturn(connectionMock); - assertThat(getMethodReturnViaReflection(this.dialect, "createRemoteMetadataReader"), - instanceOf(ExasolMetadataReader.class)); + assertThat(this.dialect.createRemoteMetadataReader(), instanceOf(ExasolMetadataReader.class)); } @Test - void testCreateJdbcQueryRewriter(@Mock final Connection connectionMock) throws SQLException { - when(this.connectionFactoryMock.getConnection()).thenReturn(connectionMock); - assertThat(getMethodReturnViaReflection(this.dialect, "createQueryRewriter"), - instanceOf(ExasolJdbcQueryRewriter.class)); + void createQueryRewriterForLocal() { + assertThat(createQueryRewriter(Map.of("IS_LOCAL", "true")), instanceOf(ExasolLocalQueryRewriter.class)); } @Test - void testCreateLocalQueryRewriter() { - this.rawProperties.put(EXASOL_IS_LOCAL_PROPERTY, "true"); - final AdapterProperties properties = new AdapterProperties(this.rawProperties); - final SqlDialect dialect = new ExasolSqlDialect(this.connectionFactoryMock, properties); - assertThat(getMethodReturnViaReflection(dialect, "createQueryRewriter"), - instanceOf(ExasolLocalQueryRewriter.class)); + void createQueryRewriterForExa() { + assertThat(createQueryRewriter(Map.of("IMPORT_FROM_EXA", "true")), + instanceOf(ExasolFromExaQueryRewriter.class)); } @Test - void testCreateFromExaQueryRewriter(@Mock final Connection connectionMock) throws SQLException { - when(this.connectionFactoryMock.getConnection()).thenReturn(connectionMock); - this.rawProperties.put(EXASOL_IMPORT_PROPERTY, "true"); - final AdapterProperties properties = new AdapterProperties(this.rawProperties); - final SqlDialect dialect = new ExasolSqlDialect(this.connectionFactoryMock, properties); - assertThat(getMethodReturnViaReflection(dialect, "createQueryRewriter"), + void createQueryRewriterForExaWithDataType() { + assertThat( + createQueryRewriter( + Map.of("IMPORT_FROM_EXA", "true", "GENERATE_JDBC_DATATYPE_MAPPING_FOR_EXA", "true")), instanceOf(ExasolFromExaWithDataTypeQueryRewriter.class)); } + @Test + void createQueryRewriterForJdbc() { + assertThat(createQueryRewriter(Map.of()), instanceOf(ExasolJdbcQueryRewriter.class)); + } + + private QueryRewriter createQueryRewriter(final Map rawProperties) { + return testee(rawProperties).createQueryRewriter(); + } + @Test void checkValidBoolOptionsWithExaConnection() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory() // .with(EXASOL_IMPORT_PROPERTY, "TrUe") // .with(EXASOL_CONNECTION_PROPERTY, "MY_EXA_CONNECTION") // .build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); sqlDialect.validateProperties(); } @Test void checkValidBoolOptionsWithExaConnectionExplicitlyDisabled() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory().with(EXASOL_IMPORT_PROPERTY, "FalSe").build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); sqlDialect.validateProperties(); } @@ -191,7 +196,7 @@ void checkValidBoolOptionsWithExaConnectionExplicitlyDisabled() throws PropertyV void testInconsistentExasolProperties() { final AdapterProperties adapterProperties = mandatory().with(EXASOL_CONNECTION_PROPERTY, "localhost:5555") .build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); final PropertyValidationException exception = assertThrows(PropertyValidationException.class, sqlDialect::validateProperties); assertThat(exception.getMessage(), @@ -203,7 +208,7 @@ void testInvalidExasolProperties() { final AdapterProperties adapterProperties = mandatory() // .with(EXASOL_IMPORT_PROPERTY, "True") // .build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); final PropertyValidationException exception = assertThrows(PropertyValidationException.class, sqlDialect::validateProperties); assertThat(exception.getMessage(), @@ -215,7 +220,7 @@ void testValidateCatalogProperty() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory() // .with(CATALOG_NAME_PROPERTY, "MY_CATALOG") // .build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); sqlDialect.validateProperties(); } @@ -224,7 +229,7 @@ void testValidateSchemaProperty() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory() // .with(SCHEMA_NAME_PROPERTY, "MY_SCHEMA") // .build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); sqlDialect.validateProperties(); } @@ -233,7 +238,7 @@ void checkInvalidIsLocalProperty() { final AdapterProperties adapterProperties = mandatory() // .with(EXASOL_IS_LOCAL_PROPERTY, "asdasd") // .build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); final PropertyValidationException exception = assertThrows(PropertyValidationException.class, sqlDialect::validateProperties); assertThat(exception.getMessage(), containsString("The value 'asdasd' for property 'IS_LOCAL' is invalid." @@ -243,14 +248,14 @@ void checkInvalidIsLocalProperty() { @Test void checkValidIsLocalProperty1() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory().with(EXASOL_IS_LOCAL_PROPERTY, "TrUe").build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); sqlDialect.validateProperties(); } @Test void checkValidIsLocalProperty() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory().with(EXASOL_IS_LOCAL_PROPERTY, "FalSe").build(); - final SqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final SqlDialect sqlDialect = testee(adapterProperties); sqlDialect.validateProperties(); } @@ -259,7 +264,7 @@ void testIsTimestampWithLocalTimeZoneEnabled() { final AdapterProperties adapterProperties = mandatory() // .with(IGNORE_ERRORS_PROPERTY, EXASOL_TIMESTAMP_WITH_LOCAL_TIME_ZONE_SWITCH) // .build(); - final ExasolSqlDialect exasolSqlDialect = new ExasolSqlDialect(null, adapterProperties); + final ExasolSqlDialect exasolSqlDialect = testee(adapterProperties); assertThat(exasolSqlDialect.isTimestampWithLocalTimeZoneEnabled(), equalTo(true)); } @@ -268,7 +273,7 @@ void testMissing() throws PropertyValidationException { final AdapterProperties adapterProperties = mandatory() // .remove(AdapterProperties.SCHEMA_NAME_PROPERTY) // .build(); - final ExasolSqlDialect sqlDialect = new ExasolSqlDialect(null, adapterProperties); + final ExasolSqlDialect sqlDialect = testee(adapterProperties); final Exception exception = assertThrows(PropertyValidationException.class, () -> sqlDialect.validateProperties()); assertThat(exception.getMessage(), diff --git a/src/test/java/com/exasol/adapter/dialects/exasol/IntegrationTestConfiguration.java b/src/test/java/com/exasol/adapter/dialects/exasol/IntegrationTestConfiguration.java index 394e9a6..ae532f3 100644 --- a/src/test/java/com/exasol/adapter/dialects/exasol/IntegrationTestConfiguration.java +++ b/src/test/java/com/exasol/adapter/dialects/exasol/IntegrationTestConfiguration.java @@ -8,7 +8,7 @@ public final class IntegrationTestConfiguration { * Do not use MavenProjectVersionGetter here to enable reference checker to check if reference points to the latest * version. */ - public static final String VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION = "virtual-schema-dist-11.0.2-exasol-7.1.7.jar"; + public static final String VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION = "virtual-schema-dist-12.0.0-exasol-7.2.0.jar"; public static final Path PATH_TO_VIRTUAL_SCHEMAS_JAR = Path.of("target", VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION); private IntegrationTestConfiguration() { diff --git a/src/test/java/com/exasol/reflect/ReflectionException.java b/src/test/java/com/exasol/reflect/ReflectionException.java deleted file mode 100644 index a4ee81b..0000000 --- a/src/test/java/com/exasol/reflect/ReflectionException.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.exasol.reflect; - -/** - * This class is intended for wrapping exceptions thrown during attempts to use reflection to keep the number of - * declared exceptions down in unit tests. - */ -public final class ReflectionException extends RuntimeException { - private static final long serialVersionUID = 63504710445197156L; - - public ReflectionException(final Throwable cause) { - super(cause); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/reflect/ReflectionUtils.java b/src/test/java/com/exasol/reflect/ReflectionUtils.java deleted file mode 100644 index dd9b0d9..0000000 --- a/src/test/java/com/exasol/reflect/ReflectionUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.exasol.reflect; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * This class contains helper methods to reduce the overhead for accessing class members via reflection. This code is - * targeted at making tests more compact and readable and should not be used in production code. - */ -public final class ReflectionUtils { - private ReflectionUtils() { - // prevent instantiation - } - - /** - * @param object instance on which the method is invoked - * @param methodName name of the method to be invoked - * @return resulting return value of the method invocation - */ - public static Object getMethodReturnViaReflection(final Object object, final String methodName) { - final Method method; - try { - method = object.getClass().getDeclaredMethod(methodName); - method.setAccessible(true); - return method.invoke(object); - } catch (final NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException exception) { - throw new com.exasol.reflect.ReflectionException(exception); - } - } -} \ No newline at end of file