diff --git a/README.md b/README.md index 7da88b63e..50252e125 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Running the Virtual Schema requires a Java Runtime version 9 or later. [athena-dialect-doc]: doc/dialects/athena.md [aurora-dialect-doc]: doc/dialects/aurora.md [big-query-dialect-doc]: doc/dialects/bigquery.md -[db2-dialect-doc]: doc/dialects/db2.md +[db2-dialect-doc]: https://github.com/exasol/mysql-virtual-schema/blob/main/doc/user_guide/db2_user_guide.md [exasol-dialect-doc]: https://github.com/exasol/exasol-virtual-schema/blob/master/doc/dialects/exasol.md [hive-dialect-doc]: doc/dialects/hive.md [impala-dialect-doc]: doc/dialects/impala.md diff --git a/doc/changes/changes_6.0.0.md b/doc/changes/changes_6.0.0.md index 1ca6d2458..9e3ebb72c 100644 --- a/doc/changes/changes_6.0.0.md +++ b/doc/changes/changes_6.0.0.md @@ -1,15 +1,17 @@ # Exasol Virtual Schemas 6.0.0, released 2021-XX-XX -Code name: Migrated Oracle dialect implementation to its own repository. +Code name: Migrated Oracle and DB2 dialect implementations to their own repository. ## Summary -Please we aware you can not create Oracle Virtual Schemas using this JAR anymore. +Please we aware you can not create Oracle nor DB2 Virtual Schemas using this JAR anymore. Oracle dialect implementation has been migrated to https://github.com/exasol/oracle-virtual-schema. +DB2 dialect implementation has been migrated to https://github.com/exasol/db2-virtual-schema. ## Refactoring * #438: Removed Oracle dialect implementation as it has been migrated to https://github.com/exasol/mysql-virtual-schema. +* #440: Removed DB2 dialect implementation as it has been migrated to https://github.com/exasol/db2-virtual-schema. ## Dependency updates diff --git a/doc/dialects/db2.md b/doc/dialects/db2.md index 217966325..c6765e29d 100644 --- a/doc/dialects/db2.md +++ b/doc/dialects/db2.md @@ -1,122 +1,3 @@ # DB2 SQL Dialect -[DB2](https://www.ibm.com/db2/) is an IBM database product. It is a Relational Database Management System (RDBMS). DB2 is extended with the support of Object-Oriented features and non-relational structures with XML. - -## Registering the JDBC Driver in EXAOperation - -First download the [DB2 JDBC driver](http://www-01.ibm.com/support/docview.wss?uid=swg21363866). - -Now register the driver in EXAOperation: - -1. Click "Software" -1. Switch to tab "JDBC Drivers" -1. Click "Browse..." -1. Select JDBC driver file -1. Click "Upload" -1. Click "Add" -1. In dialog "Add EXACluster JDBC driver" configure the JDBC driver (see below) - -You need to specify the following settings when adding the JDBC driver via EXAOperation. - -| Parameter | Value | -|-----------|-----------------------------------------------------| -| Name | `DB2` | -| Main | `com.ibm.db2.jcc.DB2Driver` | -| Prefix | `jdbc:db2:` | -| Files | `db2jcc4.jar`, `db2jcc_license_cu.jar` | - -Additionally there are 2 files for the DB2 Driver. - -* `db2jcc_license_cu.jar` - License File for DB2 on Linux Unix and Windows -* `db2jcc_license_cisuz.jar` - License File for DB2 on zOS (Mainframe) - -Make sure that you upload the necessary license file for the target platform you want to connect to. - -## Uploading the JDBC Driver to EXAOperation - -1. [Create a bucket in BucketFS](https://docs.exasol.com/administration/on-premise/bucketfs/create_new_bucket_in_bucketfs_service.htm) -1. Upload the driver and the license to BucketFS - -This step is necessary since the UDF container the adapter runs in has no access to the JDBC drivers installed via EXAOperation but it can access BucketFS. - -## Installing the Adapter Script - -Upload the latest available release of [Virtual Schema JDBC Adapter](https://github.com/exasol/virtual-schemas/releases) to Bucket FS. - -Then create a schema to hold the adapter script. - -```sql -CREATE SCHEMA ADAPTER; -``` - -The SQL statement below creates the adapter script, defines the Java class that serves as entry point and tells the UDF framework where to find the libraries (JAR files) for Virtual Schema and database driver. - -### For Regular DB2 Servers - -```sql -CREATE OR REPLACE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS - %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; - %jar /buckets///db2jcc4.jar; - %jar /buckets///db2jcc_license_cu.jar; -/ -; -``` - -### For Mainframes - -```sql -CREATE OR REPLACE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS - %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; - %jar /buckets///db2jcc4.jar; - %jar /buckets///db2jcc_license_cu.jar; - %jar /buckets///db2jcc_license_cisuz.jar; -/ -``` -## Defining a Named Connection - -Define the connection to DB2 as shown below. - -```sql -CREATE OR REPLACE CONNECTION DB2_CONNECTION -TO 'jdbc:db2://:/' -USER '' -IDENTIFIED BY ''; -``` - -## Creating a Virtual Schema - -Below you see how a DB2 Virtual Schema is created. - -```sql -CREATE VIRTUAL SCHEMA - USING ADAPTER.JDBC_ADAPTER - WITH - SQL_DIALECT = 'DB2' - CONNECTION_NAME = 'DB2_CONNECTION' - SCHEMA_NAME = '' -; -``` - -## Testing information - -DB2 was tested with the IBM DB2 JCC Drivers that come with DB2 LUW V10.1 and V11. As these drivers didn't have any major changes in the past years any DB2 driver should work (back to V9.1). The driver comes with 2 different implementations `db2jcc.jar` and `db2jcc4.jar`. All tests were made with the `db2jcc4.jar`. - -## Supported Capabilities - -The DB2 dialect handles implements specific casts for time data types and functions. - -## Casting of Data Types - -* `TIMESTAMP` and `TIMESTAMP(x)` is cast to `VARCHAR` to not lose precision. -* `VARCHAR` and `CHAR` for bit data will be cast to a hex string with double the original size -* `TIME` will be cast to `VARCHAR(8)` -* `XML` will be cast to `VARCHAR(DB2_MAX_LENGTH)` -* `BLOB` is not supported - -## Casting of Functions - -* `LIMIT` will replaced by `FETCH FIRST x ROWS ONLY` -* `OFFSET` is currently not supported as only DB2 V11 support this natively -* `ADD_DAYS`, `ADD_WEEKS` ... will be replaced by `COLUMN + DAYS`, `COLUMN + ....` +The DB2 Virtual Schema has been migrated to its [own repository](https://github.com/exasol/db2-virtual-schema/). \ No newline at end of file diff --git a/doc/user-guide/dialects.md b/doc/user-guide/dialects.md index 3a7e73570..8f682b92a 100644 --- a/doc/user-guide/dialects.md +++ b/doc/user-guide/dialects.md @@ -5,7 +5,7 @@ Dialect name | Repository [Athena][athena-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | [Aurora][aurora-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | [Big Query][big-query-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | -[DB2][db2-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | +[DB2][db2-dialect-doc] | [DB2 Virtual Schemas][db2-virtual-schema-repository] | [Latest release][db2-virtual-schema-releases] | [Exasol][exasol-dialect-doc] | [Exasol Virtual Schema][exasol-virtual-schema-repository] | [Latest release][exasol-virtual-schema-releases] | [Hive][hive-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | [Impala][impala-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | @@ -25,7 +25,7 @@ You can also [develop a custom dialect](../development/developing-sql-dialect/de [athena-dialect-doc]: ../dialects/athena.md [aurora-dialect-doc]: ../dialects/aurora.md [big-query-dialect-doc]: ../dialects/bigquery.md -[db2-dialect-doc]: ../dialects/db2.md +[db2-dialect-doc]: https://github.com/exasol/mysql-virtual-schema/blob/main/doc/user_guide/db2_user_guide.md [exasol-dialect-doc]: https://github.com/exasol/exasol-virtual-schema/blob/master/doc/dialects/exasol.md [hive-dialect-doc]: ../dialects/hive.md [impala-dialect-doc]: ../dialects/impala.md @@ -40,6 +40,8 @@ You can also [develop a custom dialect](../development/developing-sql-dialect/de [virtual-schemas-repository]: https://github.com/exasol/virtual-schemas [virtual-schemas-releases]: https://github.com/exasol/virtual-schemas/releases +[db2-virtual-schema-repository]: https://github.com/exasol/db2-virtual-schema +[db2-virtual-schema-releases]: https://github.com/exasol/db2-virtual-schema/releases [exasol-virtual-schema-repository]: https://github.com/exasol/exasol-virtual-schema [exasol-virtual-schema-releases]: https://github.com/exasol/exasol-virtual-schema/releases [mysql-virtual-schema-repository]: https://github.com/exasol/mysql-virtual-schema diff --git a/src/main/java/com/exasol/adapter/dialects/db2/DB2ColumnMetadataReader.java b/src/main/java/com/exasol/adapter/dialects/db2/DB2ColumnMetadataReader.java deleted file mode 100644 index aa8790343..000000000 --- a/src/main/java/com/exasol/adapter/dialects/db2/DB2ColumnMetadataReader.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import java.sql.Connection; -import java.sql.Types; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.IdentifierConverter; -import com.exasol.adapter.jdbc.BaseColumnMetadataReader; -import com.exasol.adapter.jdbc.JdbcTypeDescription; -import com.exasol.adapter.metadata.DataType; - -/** - * This class implements DB2-specific reading of column metadata. - */ -public class DB2ColumnMetadataReader extends BaseColumnMetadataReader { - /** - * Create a new instance of the {@link DB2ColumnMetadataReader}. - * - * @param connection connection to the remote data source - * @param properties user-defined adapter properties - * @param identifierConverter converter between source and Exasol identifiers - */ - public DB2ColumnMetadataReader(final Connection connection, final AdapterProperties properties, - final IdentifierConverter identifierConverter) { - super(connection, properties, identifierConverter); - } - - @Override - public DataType mapJdbcType(final JdbcTypeDescription jdbcTypeDescription) { - final int size = jdbcTypeDescription.getPrecisionOrSize(); - switch (jdbcTypeDescription.getJdbcType()) { - case Types.CLOB: - case Types.OTHER: - return DataType.createVarChar(DataType.MAX_EXASOL_VARCHAR_SIZE, DataType.ExaCharset.UTF8); - case Types.TIMESTAMP: - return DataType.createVarChar(32, DataType.ExaCharset.UTF8); - case Types.VARCHAR: - case Types.NVARCHAR: - case Types.LONGVARCHAR: - case Types.CHAR: - case Types.NCHAR: - case Types.LONGNVARCHAR: - return getLiteralDataType(size); - case Types.BINARY: - return DataType.createChar(size * 2, DataType.ExaCharset.ASCII); - case Types.VARBINARY: - return DataType.createVarChar(size * 2, DataType.ExaCharset.ASCII); - default: - return super.mapJdbcType(jdbcTypeDescription); - } - } - - private DataType getLiteralDataType(final int size) { - final DataType.ExaCharset charset = DataType.ExaCharset.UTF8; - if (size <= DataType.MAX_EXASOL_VARCHAR_SIZE) { - return DataType.createVarChar(size, charset); - } else { - return DataType.createVarChar(DataType.MAX_EXASOL_VARCHAR_SIZE, charset); - } - } -} diff --git a/src/main/java/com/exasol/adapter/dialects/db2/DB2MetadataReader.java b/src/main/java/com/exasol/adapter/dialects/db2/DB2MetadataReader.java deleted file mode 100644 index 7908464ec..000000000 --- a/src/main/java/com/exasol/adapter/dialects/db2/DB2MetadataReader.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import java.sql.Connection; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.BaseIdentifierConverter; -import com.exasol.adapter.dialects.IdentifierConverter; -import com.exasol.adapter.jdbc.*; - -/** - * This class reads and DB2-specific database metadata. - */ -public class DB2MetadataReader extends AbstractRemoteMetadataReader { - /** - * Create a new instance of the {@link DB2MetadataReader}. - * - * @param connection database connection through which the reader retrieves the metadata from the remote source - * @param properties user-defined properties - */ - public DB2MetadataReader(final Connection connection, final AdapterProperties properties) { - super(connection, properties); - } - - @Override - protected ColumnMetadataReader createColumnMetadataReader() { - return new DB2ColumnMetadataReader(this.connection, this.properties, getIdentifierConverter()); - } - - @Override - protected TableMetadataReader createTableMetadataReader() { - return new BaseTableMetadataReader(this.connection, this.columnMetadataReader, this.properties, - this.identifierConverter); - } - - @Override - protected IdentifierConverter createIdentifierConverter() { - return BaseIdentifierConverter.createDefault(); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlDialect.java b/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlDialect.java deleted file mode 100644 index 21ce27e11..000000000 --- a/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlDialect.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import static com.exasol.adapter.AdapterProperties.SCHEMA_NAME_PROPERTY; -import static com.exasol.adapter.capabilities.AggregateFunctionCapability.*; -import static com.exasol.adapter.capabilities.LiteralCapability.*; -import static com.exasol.adapter.capabilities.MainCapability.*; -import static com.exasol.adapter.capabilities.PredicateCapability.*; -import static com.exasol.adapter.capabilities.ScalarFunctionCapability.*; - -import java.sql.SQLException; -import java.util.Set; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.capabilities.Capabilities; -import com.exasol.adapter.dialects.*; -import com.exasol.adapter.jdbc.*; -import com.exasol.adapter.sql.SqlNodeVisitor; - -/** - * Dialect for DB2 using the DB2 Connector JDBC driver. - */ -public class DB2SqlDialect extends AbstractSqlDialect { - static final String NAME = "DB2"; - private static final Capabilities CAPABILITIES = createCapabilityList(); - - /** - * Create a new instance of the {@link DB2SqlDialect}. - * - * @param connectionFactory factory for the JDBC connection to the remote data source - * @param properties user-defined adapter properties - */ - public DB2SqlDialect(final ConnectionFactory connectionFactory, final AdapterProperties properties) { - super(connectionFactory, properties, Set.of(SCHEMA_NAME_PROPERTY)); - } - - @Override - protected RemoteMetadataReader createRemoteMetadataReader() { - try { - return new DB2MetadataReader(this.connectionFactory.getConnection(), this.properties); - } catch (final SQLException exception) { - throw new RemoteMetadataReaderException( - "Unable to create DB2 remote metadata reader. Caused by: " + exception.getMessage(), exception); - } - } - - @Override - protected QueryRewriter createQueryRewriter() { - return new ImportIntoQueryRewriter(this, createRemoteMetadataReader(), this.connectionFactory); - } - - @Override - public String getName() { - return NAME; - } - - private static Capabilities createCapabilityList() { - return Capabilities.builder() - .addMain(SELECTLIST_PROJECTION, SELECTLIST_EXPRESSIONS, FILTER_EXPRESSIONS, AGGREGATE_SINGLE_GROUP, - AGGREGATE_GROUP_BY_COLUMN, AGGREGATE_GROUP_BY_EXPRESSION, AGGREGATE_GROUP_BY_TUPLE, - AGGREGATE_HAVING, ORDER_BY_COLUMN, ORDER_BY_EXPRESSION, LIMIT) - .addPredicate(AND, OR, NOT, EQUAL, NOTEQUAL, LESS, LESSEQUAL, LIKE, LIKE_ESCAPE, BETWEEN, IN_CONSTLIST, - IS_NULL, IS_NOT_NULL) - .addLiteral(NULL, DATE, TIMESTAMP, TIMESTAMP_UTC, DOUBLE, EXACTNUMERIC, STRING, INTERVAL) - .addAggregateFunction(COUNT, COUNT_STAR, COUNT_DISTINCT, GROUP_CONCAT, GROUP_CONCAT_SEPARATOR, - GROUP_CONCAT_ORDER_BY, SUM, SUM_DISTINCT, MIN, MAX, AVG, AVG_DISTINCT, MEDIAN, FIRST_VALUE, - LAST_VALUE, STDDEV, STDDEV_POP, STDDEV_SAMP, VARIANCE, VARIANCE_DISTINCT, VAR_POP, VAR_SAMP) - .addScalarFunction(CEIL, DIV, FLOOR, SIGN, ADD, SUB, MULT, FLOAT_DIV, NEG, ABS, ACOS, ASIN, ATAN, ATAN2, - COS, COSH, COT, DEGREES, EXP, GREATEST, LEAST, LN, LOG, MOD, POWER, RADIANS, SIN, SINH, SQRT, - TAN, TANH, ASCII, CHR, INSTR, LENGTH, LOCATE, LOWER, LPAD, LTRIM, REPEAT, REPLACE, RIGHT, RPAD, - RTRIM, SOUNDEX, SUBSTR, TRANSLATE, TRIM, UPPER, ADD_DAYS, ADD_HOURS, ADD_MINUTES, ADD_MONTHS, - ADD_SECONDS, ADD_WEEKS, ADD_YEARS, CURRENT_DATE, CURRENT_TIMESTAMP, LOCALTIMESTAMP, SYSDATE, - SYSTIMESTAMP, CAST, TO_CHAR, TO_DATE, TO_NUMBER, TO_TIMESTAMP, CASE, CURRENT_SCHEMA, - CURRENT_USER, NULLIFZERO, ZEROIFNULL) // - .build(); - } - - /** - * Get the DB2 dialect name. - * - * @return always "DB2" - */ - public static String getPublicName() { - return NAME; - } - - @Override - public Capabilities getCapabilities() { - return CAPABILITIES; - } - - @Override - public StructureElementSupport supportsJdbcCatalogs() { - return StructureElementSupport.NONE; - } - - @Override - public StructureElementSupport supportsJdbcSchemas() { - return StructureElementSupport.MULTIPLE; - } - - @Override - // https://www.ibm.com/support/knowledgecenter/SSEPGG_11.5.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000720.html - public String applyQuote(final String identifier) { - return super.quoteIdentifierWithDoubleQuotes(identifier); - } - - @Override - public boolean requiresCatalogQualifiedTableNames(final SqlGenerationContext context) { - return false; - } - - @Override - public boolean requiresSchemaQualifiedTableNames(final SqlGenerationContext context) { - return true; - } - - @Override - public SqlNodeVisitor getSqlGenerationVisitor(final SqlGenerationContext context) { - return new DB2SqlGenerationVisitor(this, context); - } - - @Override - public NullSorting getDefaultNullSorting() { - return NullSorting.NULLS_SORTED_AT_END; - } - - @Override - // https://www.ibm.com/support/knowledgecenter/SSEPGG_11.5.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000731.html - public String getStringLiteral(final String value) { - return super.quoteLiteralStringWithSingleQuote(value); - } -} diff --git a/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlDialectFactory.java b/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlDialectFactory.java deleted file mode 100644 index b91d167e9..000000000 --- a/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlDialectFactory.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.AbstractSqlDialectFactory; -import com.exasol.adapter.dialects.SqlDialect; -import com.exasol.adapter.jdbc.ConnectionFactory; - -/** - * Factory for the DB2 SQL dialect. - */ -public class DB2SqlDialectFactory extends AbstractSqlDialectFactory { - @Override - public String getSqlDialectName() { - return DB2SqlDialect.NAME; - } - - @Override - public SqlDialect createSqlDialect(final ConnectionFactory connectionFactory, final AdapterProperties properties) { - return new DB2SqlDialect(connectionFactory, properties); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlGenerationVisitor.java b/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlGenerationVisitor.java deleted file mode 100644 index 48f0b7434..000000000 --- a/src/main/java/com/exasol/adapter/dialects/db2/DB2SqlGenerationVisitor.java +++ /dev/null @@ -1,360 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import java.util.*; - -import com.exasol.adapter.AdapterException; -import com.exasol.adapter.dialects.*; -import com.exasol.adapter.metadata.ColumnMetadata; -import com.exasol.adapter.metadata.TableMetadata; -import com.exasol.adapter.sql.*; - -/** - * This class generates SQL queries for the {@link DB2SqlDialect}. - */ -public class DB2SqlGenerationVisitor extends SqlGenerationVisitor { - private static final List TYPE_NAMES_REQUIRING_CAST = List.of("TIMESTAMP", "DECFLOAT", "CLOB", "XML", - "TIME"); - private static final List TYPE_NAMES_NOT_SUPPORTED = List.of("BLOB"); - - /** - * Create a new instance of the {@link DB2SqlGenerationVisitor}. - * - * @param dialect {@link DB2SqlDialect} SQL dialect - * @param context SQL generation context - */ - public DB2SqlGenerationVisitor(final SqlDialect dialect, final SqlGenerationContext context) { - super(dialect, context); - } - - protected List getListOfTypeNamesRequiringCast() { - return TYPE_NAMES_REQUIRING_CAST; - } - - protected List getListOfTypeNamesNotSupported() { - return TYPE_NAMES_NOT_SUPPORTED; - } - - @Override - protected String representAnyColumnInSelectList() { - return SqlConstants.ONE; - } - - @Override - protected String representAsteriskInSelectList(final SqlSelectList selectList) throws AdapterException { - final List selectStarList = buildSelectStar(selectList); - final List selectListElements = new ArrayList<>(selectStarList.size()); - selectListElements.addAll(selectStarList); - return String.join(", ", selectListElements); - } - - private List buildSelectStar(final SqlSelectList selectList) throws AdapterException { - if (SqlGenerationHelper.selectListRequiresCasts(selectList, this.nodeRequiresCast)) { - return buildSelectStarWithNodeCast(selectList); - } else { - return new ArrayList<>(Collections.singletonList("*")); - } - } - - private List buildSelectStarWithNodeCast(final SqlSelectList selectList) throws AdapterException { - final SqlStatementSelect select = (SqlStatementSelect) selectList.getParent(); - int columnId = 0; - final List tableMetadata = new ArrayList<>(); - SqlGenerationHelper.addMetadata(select.getFromClause(), tableMetadata); - final List selectListElements = new ArrayList<>(tableMetadata.size()); - for (final TableMetadata tableMeta : tableMetadata) { - for (final ColumnMetadata columnMeta : tableMeta.getColumns()) { - final SqlColumn sqlColumn = new SqlColumn(columnId, columnMeta); - selectListElements.add(buildColumnProjectionString(sqlColumn, super.visit(sqlColumn))); - ++columnId; - } - } - return selectListElements; - } - - private String buildColumnProjectionString(final SqlColumn column, final String projectionString) - throws AdapterException { - final String typeName = getTypeNameFromColumn(column); - return buildColumnProjectionString(typeName, projectionString); - } - - private final java.util.function.Predicate nodeRequiresCast = node -> { - try { - if (node.getType() == SqlNodeType.COLUMN) { - final SqlColumn column = (SqlColumn) node; - final String typeName = getTypeNameFromColumn(column); - return getListOfTypeNamesRequiringCast().contains(typeName) - || getListOfTypeNamesNotSupported().contains(typeName); - } - return false; - } catch (final AdapterException exception) { - throw new SqlGenerationVisitorException("Exception during deserialization of ColumnAdapterNotes. ", - exception); - } - }; - - @Override - public String visit(final SqlColumn column) throws AdapterException { - final String projectionString = super.visit(column); - return getColumnProjectionString(column, projectionString); - } - - private String getColumnProjectionString(final SqlColumn column, final String projectionString) - throws AdapterException { - if (!super.isDirectlyInSelectList(column)) { - return projectionString; - } else { - final String typeName = getTypeNameFromColumn(column); - return buildColumnProjectionString(typeName, projectionString); - } - } - - private String buildColumnProjectionString(final String typeName, final String projectionString) { - if (TYPE_NAMES_NOT_SUPPORTED.contains(typeName)) { - return "'" + typeName + " NOT SUPPORTED'"; - } else { - return getProjectionStringWithSupportedTypes(typeName, projectionString); - } - } - - private String getProjectionStringWithSupportedTypes(final String typeName, final String projectionString) { - switch (typeName) { - case "XML": - return "XMLSERIALIZE(" + projectionString + " as VARCHAR(32000) INCLUDING XMLDECLARATION)"; - // db2 does not support cast of clobs to varchar in full length -> max 32672 - case "CLOB": - return "CAST(SUBSTRING(" + projectionString + ",32672) AS VARCHAR(32672))"; - case "CHAR () FOR BIT DATA": - case "VARCHAR () FOR BIT DATA": - return "HEX(" + projectionString + ")"; - case "TIME": - // cast timestamp to not lose precision - case "TIMESTAMP": - return "VARCHAR(" + projectionString + ")"; - default: - return projectionString; - } - } - - @Override - public String visit(final SqlStatementSelect select) throws AdapterException { - if (!select.hasLimit()) { - return super.visit(select); - } else { - return getSelect(select); - } - } - - private String getSelect(final SqlStatementSelect select) throws AdapterException { - final SqlLimit limit = select.getLimit(); - final StringBuilder builder = new StringBuilder(); - builder.append("SELECT "); - builder.append(select.getSelectList().accept(this)); - builder.append(" FROM "); - builder.append(select.getFromClause().accept(this)); - if (select.hasFilter()) { - builder.append(" WHERE "); - builder.append(select.getWhereClause().accept(this)); - } - if (select.hasGroupBy()) { - builder.append(" GROUP BY "); - builder.append(select.getGroupBy().accept(this)); - } - if (select.hasHaving()) { - builder.append(" HAVING "); - builder.append(select.getHaving().accept(this)); - } - if (select.hasOrderBy()) { - builder.append(" "); - builder.append(select.getOrderBy().accept(this)); - } - builder.append(" FETCH FIRST "); - builder.append(limit.getLimit()); - builder.append(" ROWS ONLY"); - return builder.toString(); - } - - @Override - public String visit(final SqlFunctionScalar function) throws AdapterException { - switch (function.getFunction()) { - case TRIM: - return getTrim(function); - case ADD_DAYS: - case ADD_HOURS: - case ADD_MINUTES: - case ADD_SECONDS: - case ADD_WEEKS: - case ADD_YEARS: - return getAddTimeOrDate(function); - case CURRENT_DATE: - case SYSDATE: - return "CURRENT DATE"; - case CURRENT_TIMESTAMP: - case SYSTIMESTAMP: - return "VARCHAR(CURRENT TIMESTAMP)"; - case DBTIMEZONE: - return "DBTIMEZONE"; - case LOCALTIMESTAMP: - return "LOCALTIMESTAMP"; - case SESSIONTIMEZONE: - return "SESSIONTIMEZONE"; - case BIT_AND: - return super.visit(function).replaceFirst("^BIT_AND", "BITAND"); - case BIT_TO_NUM: - return super.visit(function).replaceFirst("^BIT_TO_NUM", "BIN_TO_NUM"); - case NULLIFZERO: - return getNullZero(function, "NULLIF("); - case ZEROIFNULL: - return getNullZero(function, "IFNULL("); - case DIV: - return getDiv(function); - default: - return super.visit(function); - } - } - - private String getDiv(final SqlFunctionScalar function) throws AdapterException { - final List arguments = function.getArguments(); - final List argumentsSql = new ArrayList<>(arguments.size()); - for (final SqlNode node : arguments) { - argumentsSql.add(node.accept(this)); - } - final StringBuilder builder = new StringBuilder(); - builder.append("CAST(FLOOR("); - builder.append(argumentsSql.get(0)); - builder.append(" / FLOOR("); - builder.append(argumentsSql.get(1)); - builder.append(")) AS DECIMAL(36, 0))"); - return builder.toString(); - } - - private String getNullZero(final SqlFunctionScalar function, final String expression) throws AdapterException { - final List argumentsSql = new ArrayList<>(); - for (final SqlNode node : function.getArguments()) { - argumentsSql.add(node.accept(this)); - } - final StringBuilder builder = new StringBuilder(); - builder.append(expression); - builder.append(argumentsSql.get(0)); - builder.append(", 0)"); - return builder.toString(); - } - - private String getAddTimeOrDate(final SqlFunctionScalar function) throws AdapterException { - final List argumentsSql = new ArrayList<>(); - for (final SqlNode node : function.getArguments()) { - argumentsSql.add(node.accept(this)); - } - final StringBuilder builder = new StringBuilder(); - final SqlColumn column = (SqlColumn) function.getArguments().get(0); - final String typeName = getTypeNameFromColumn(column); - boolean isTimestamp = false; // special cast required - if (typeName.contains("TIMESTAMP")) { - isTimestamp = true; - builder.append("VARCHAR("); - } - builder.append(argumentsSql.get(0)); - builder.append(" + "); - if (function.getFunction() == ScalarFunction.ADD_WEEKS) { - builder.append(7 * Integer.parseInt(argumentsSql.get(1))); - } else { - builder.append(argumentsSql.get(1)); - } - builder.append(" "); - switch (function.getFunction()) { - case ADD_DAYS: - case ADD_WEEKS: - builder.append("DAYS"); - break; - case ADD_HOURS: - builder.append("HOURS"); - break; - case ADD_MINUTES: - builder.append("MINUTES"); - break; - case ADD_SECONDS: - builder.append("SECONDS"); - break; - case ADD_YEARS: - builder.append("YEARS"); - break; - default: - break; - } - if (isTimestamp) { - builder.append(")"); - } - return builder.toString(); - } - - private String getTrim(final SqlFunctionScalar function) throws AdapterException { - final List argumentsSql = new ArrayList<>(); - for (final SqlNode node : function.getArguments()) { - argumentsSql.add(node.accept(this)); - } - final StringBuilder builder = new StringBuilder(); - builder.append("TRIM("); - if (argumentsSql.size() > 1) { - builder.append(argumentsSql.get(1)); - builder.append(" FROM "); - builder.append(argumentsSql.get(0)); - } else { - builder.append(argumentsSql.get(0)); - } - builder.append(")"); - return builder.toString(); - } - - @Override - public String visit(final SqlFunctionAggregate function) throws AdapterException { - final String sql = super.visit(function); - if (function.getFunction() == AggregateFunction.VAR_SAMP) { - return sql.replaceFirst("^VAR_SAMP", "VARIANCE_SAMP"); - } else { - return sql; - } - } - - @Override - public String visit(final SqlFunctionAggregateGroupConcat function) throws AdapterException { - final StringBuilder builder = new StringBuilder(); - builder.append("LISTAGG"); - builder.append("("); - if (function.getArgument() != null) { - return getGroupConcat(function, builder); - } else { - throw new SqlGenerationVisitorException( - "Arguments of SqlFunctionAggregateGroupConcat shouldn't be null or empty."); - } - } - - private String getGroupConcat(final SqlFunctionAggregateGroupConcat function, final StringBuilder builder) - throws AdapterException { - final String expression = function.getArgument().accept(this); - builder.append(expression); - builder.append(", "); - final String separator = function.getSeparator() == null ? "','" : function.getSeparator().accept(this); - builder.append(separator); - builder.append(") "); - builder.append("WITHIN GROUP(ORDER BY "); - if (function.hasOrderBy()) { - getOrderBy(function, builder); - } else { - builder.append(expression); - } - builder.append(")"); - return builder.toString(); - } - - private void getOrderBy(final SqlFunctionAggregateGroupConcat function, final StringBuilder builder) - throws AdapterException { - for (int i = 0; i < function.getOrderBy().getExpressions().size(); i++) { - if (i > 0) { - builder.append(", "); - } - builder.append(function.getOrderBy().getExpressions().get(i).accept(this)); - if (!function.getOrderBy().isAscending().get(i)) { - builder.append(" DESC"); - } - } - } -} diff --git a/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory b/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory index e984e3fd0..211cf2a7f 100644 --- a/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory +++ b/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory @@ -1,6 +1,5 @@ com.exasol.adapter.dialects.athena.AthenaSqlDialectFactory com.exasol.adapter.dialects.bigquery.BigQuerySqlDialectFactory -com.exasol.adapter.dialects.db2.DB2SqlDialectFactory com.exasol.adapter.dialects.generic.GenericSqlDialectFactory com.exasol.adapter.dialects.hive.HiveSqlDialectFactory com.exasol.adapter.dialects.impala.ImpalaSqlDialectFactory diff --git a/src/test/java/com/exasol/adapter/dialects/db2/DB2ColumnMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/db2/DB2ColumnMetadataReaderTest.java deleted file mode 100644 index 602c73b51..000000000 --- a/src/test/java/com/exasol/adapter/dialects/db2/DB2ColumnMetadataReaderTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; - -import java.sql.Types; - -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.BaseIdentifierConverter; -import com.exasol.adapter.jdbc.JdbcTypeDescription; -import com.exasol.adapter.metadata.DataType; - -class DB2ColumnMetadataReaderTest { - private DB2ColumnMetadataReader db2ColumnMetadataReader; - - @BeforeEach - void beforeEach() { - this.db2ColumnMetadataReader = new DB2ColumnMetadataReader(null, AdapterProperties.emptyProperties(), - BaseIdentifierConverter.createDefault()); - } - - @Test - void testMapJdbcTypeClob() { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(Types.CLOB, 0, 0, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - equalTo(DataType.createVarChar(DataType.MAX_EXASOL_VARCHAR_SIZE, DataType.ExaCharset.UTF8))); - } - - @Test - void testMapJdbcTypeOther() { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(Types.OTHER, 0, 0, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - equalTo(DataType.createVarChar(DataType.MAX_EXASOL_VARCHAR_SIZE, DataType.ExaCharset.UTF8))); - } - - @Test - void testMapJdbcTypetimestamp() { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(Types.TIMESTAMP, 0, 0, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - equalTo(DataType.createVarChar(32, DataType.ExaCharset.UTF8))); - } - - @ValueSource(ints = { Types.VARCHAR, Types.NVARCHAR, Types.LONGNVARCHAR, Types.CHAR, Types.NCHAR, - Types.LONGNVARCHAR }) - @ParameterizedTest - void testMapJdbcVarcharSizeLesserThanExasolMaxVarcharSize(final int type) { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(type, 0, 10, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - equalTo(DataType.createVarChar(10, DataType.ExaCharset.UTF8))); - } - - @ValueSource(ints = { Types.VARCHAR, Types.NVARCHAR, Types.LONGNVARCHAR, Types.CHAR, Types.NCHAR, - Types.LONGNVARCHAR }) - @ParameterizedTest - void testMapJdbcVarcharSizeGreaterThanExasolMaxVarcharSize(final int type) { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(type, 0, - DataType.MAX_EXASOL_VARCHAR_SIZE + 1, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - equalTo(DataType.createVarChar(DataType.MAX_EXASOL_VARCHAR_SIZE, DataType.ExaCharset.UTF8))); - } - - @Test - void testMapJdbcTypeBinary() { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(Types.BINARY, 0, 0, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - equalTo(DataType.createChar(jdbcTypeDescription.getPrecisionOrSize() * 2, DataType.ExaCharset.ASCII))); - } - - @Test - void testMapJdbcTypeVarbinary() { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(Types.VARBINARY, 0, 0, 0, ""); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), equalTo( - DataType.createVarChar(jdbcTypeDescription.getPrecisionOrSize() * 2, DataType.ExaCharset.ASCII))); - } - - @Test - void testMapJdbcTypeDefault() { - final JdbcTypeDescription jdbcTypeDescription = new JdbcTypeDescription(Types.BOOLEAN, 0, 0, 0, "BOOLEAN"); - assertThat(this.db2ColumnMetadataReader.mapJdbcType(jdbcTypeDescription), - CoreMatchers.equalTo(DataType.createBool())); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/db2/DB2MetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/db2/DB2MetadataReaderTest.java deleted file mode 100644 index a67ab1e8f..000000000 --- a/src/test/java/com/exasol/adapter/dialects/db2/DB2MetadataReaderTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.jdbc.BaseTableMetadataReader; - -class DB2MetadataReaderTest { - private DB2MetadataReader db2MetadataReader; - - @BeforeEach - void beforeEach() { - this.db2MetadataReader = new DB2MetadataReader(null, AdapterProperties.emptyProperties()); - } - - @Test - void testGetTableMetadataReader() { - assertThat(this.db2MetadataReader.getTableMetadataReader(), instanceOf(BaseTableMetadataReader.class)); - } - - @Test - void testGetColumnMetadataReader() { - assertThat(this.db2MetadataReader.getColumnMetadataReader(), instanceOf(DB2ColumnMetadataReader.class)); - } -} diff --git a/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlDialectFactoryTest.java b/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlDialectFactoryTest.java deleted file mode 100644 index d5b28ad6a..000000000 --- a/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlDialectFactoryTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.exasol.adapter.AdapterProperties; - -class DB2SqlDialectFactoryTest { - private DB2SqlDialectFactory factory; - - @BeforeEach - void beforeEach() { - this.factory = new DB2SqlDialectFactory(); - } - - @Test - void testGetName() { - assertThat(this.factory.getSqlDialectName(), equalTo("DB2")); - } - - @Test - void testCreateDialect() { - assertThat(this.factory.createSqlDialect(null, AdapterProperties.emptyProperties()), - instanceOf(DB2SqlDialect.class)); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlDialectTest.java b/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlDialectTest.java deleted file mode 100644 index ac3f65caf..000000000 --- a/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlDialectTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import static com.exasol.adapter.AdapterProperties.CATALOG_NAME_PROPERTY; -import static com.exasol.adapter.AdapterProperties.SCHEMA_NAME_PROPERTY; -import static com.exasol.adapter.capabilities.AggregateFunctionCapability.*; -import static com.exasol.adapter.capabilities.LiteralCapability.*; -import static com.exasol.adapter.capabilities.MainCapability.*; -import static com.exasol.adapter.capabilities.PredicateCapability.*; -import static com.exasol.adapter.capabilities.ScalarFunctionCapability.*; -import static com.exasol.reflect.ReflectionUtils.getMethodReturnViaReflection; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.HashMap; -import java.util.Map; - -import org.hamcrest.CoreMatchers; -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.capabilities.Capabilities; -import com.exasol.adapter.dialects.PropertyValidationException; -import com.exasol.adapter.dialects.SqlDialect; -import com.exasol.adapter.jdbc.ConnectionFactory; - -@ExtendWith(MockitoExtension.class) -class DB2SqlDialectTest { - private SqlDialect dialect; - private Map rawProperties; - - @BeforeEach - void beforeEach(@Mock final ConnectionFactory connectionFactoryMock) { - this.rawProperties = new HashMap<>(); - this.dialect = new DB2SqlDialect(connectionFactoryMock, AdapterProperties.emptyProperties()); - } - - @Test - void testGetCapabilities() { - final Capabilities capabilities = this.dialect.getCapabilities(); - assertAll(() -> assertThat(capabilities.getMainCapabilities(), - containsInAnyOrder(SELECTLIST_PROJECTION, SELECTLIST_EXPRESSIONS, FILTER_EXPRESSIONS, - AGGREGATE_SINGLE_GROUP, AGGREGATE_GROUP_BY_COLUMN, AGGREGATE_GROUP_BY_EXPRESSION, - AGGREGATE_GROUP_BY_TUPLE, AGGREGATE_HAVING, ORDER_BY_COLUMN, ORDER_BY_EXPRESSION, LIMIT)), - () -> assertThat(capabilities.getLiteralCapabilities(), - containsInAnyOrder(NULL, DATE, TIMESTAMP, TIMESTAMP_UTC, DOUBLE, EXACTNUMERIC, STRING, - INTERVAL)), - () -> assertThat(capabilities.getPredicateCapabilities(), - containsInAnyOrder(AND, OR, NOT, EQUAL, NOTEQUAL, LESS, LESSEQUAL, LIKE, LIKE_ESCAPE, BETWEEN, - IN_CONSTLIST, IS_NULL, IS_NOT_NULL)), - () -> assertThat(capabilities.getAggregateFunctionCapabilities(), - containsInAnyOrder(COUNT, COUNT_STAR, COUNT_DISTINCT, GROUP_CONCAT, GROUP_CONCAT_SEPARATOR, - GROUP_CONCAT_ORDER_BY, SUM, SUM_DISTINCT, MIN, MAX, AVG, AVG_DISTINCT, MEDIAN, - FIRST_VALUE, LAST_VALUE, STDDEV, STDDEV_POP, STDDEV_SAMP, VARIANCE, VARIANCE_DISTINCT, - VAR_POP, VAR_SAMP)), - () -> assertThat(capabilities.getScalarFunctionCapabilities(), - containsInAnyOrder(CEIL, DIV, FLOOR, SIGN, ADD, SUB, MULT, FLOAT_DIV, NEG, ABS, ACOS, ASIN, - ATAN, ATAN2, COS, COSH, COT, DEGREES, EXP, GREATEST, LEAST, LN, LOG, MOD, POWER, - RADIANS, SIN, SINH, SQRT, TAN, TANH, ASCII, CHR, INSTR, LENGTH, LOCATE, LOWER, LPAD, - LTRIM, REPEAT, REPLACE, RIGHT, RPAD, RTRIM, SOUNDEX, SUBSTR, TRANSLATE, TRIM, UPPER, - ADD_DAYS, ADD_HOURS, ADD_MINUTES, ADD_MONTHS, ADD_SECONDS, ADD_WEEKS, ADD_YEARS, - CURRENT_DATE, CURRENT_TIMESTAMP, LOCALTIMESTAMP, SYSDATE, SYSTIMESTAMP, CAST, TO_CHAR, - TO_DATE, TO_NUMBER, TO_TIMESTAMP, CASE, CURRENT_SCHEMA, CURRENT_USER, NULLIFZERO, - ZEROIFNULL))); - } - - @Test - void testMetadataReaderClass() { - assertThat(getMethodReturnViaReflection(this.dialect, "createRemoteMetadataReader"), - instanceOf(DB2MetadataReader.class)); - } - - @Test - void testValidateCatalogProperty() { - setMandatoryProperties(); - this.rawProperties.put(CATALOG_NAME_PROPERTY, "MY_CATALOG"); - final AdapterProperties adapterProperties = new AdapterProperties(this.rawProperties); - final SqlDialect sqlDialect = new DB2SqlDialect(null, adapterProperties); - final PropertyValidationException exception = assertThrows(PropertyValidationException.class, - sqlDialect::validateProperties); - MatcherAssert.assertThat(exception.getMessage(), containsString( - "The dialect DB2 does not support CATALOG_NAME property. Please, do not set the \"CATALOG_NAME\" property.")); - } - - private void setMandatoryProperties() { - this.rawProperties.put(AdapterProperties.SQL_DIALECT_PROPERTY, "DB2"); - this.rawProperties.put(AdapterProperties.CONNECTION_NAME_PROPERTY, "MY_CONN"); - } - - @Test - void testValidateSchemaProperty() throws PropertyValidationException { - setMandatoryProperties(); - this.rawProperties.put(SCHEMA_NAME_PROPERTY, "MY_SCHEMA"); - final AdapterProperties adapterProperties = new AdapterProperties(this.rawProperties); - final SqlDialect sqlDialect = new DB2SqlDialect(null, adapterProperties); - sqlDialect.validateProperties(); - } - - @Test - void testSupportsJdbcCatalogs() { - assertThat(this.dialect.supportsJdbcCatalogs(), equalTo(SqlDialect.StructureElementSupport.NONE)); - } - - @Test - void testSupportsJdbcSchemas() { - assertThat(this.dialect.supportsJdbcSchemas(), equalTo(SqlDialect.StructureElementSupport.MULTIPLE)); - } - - @CsvSource({ "tableName, \"tableName\"", // - "\"tableName, \"\"\"tableName\"" // - }) - @ParameterizedTest - void testApplyQuote(final String unquoted, final String quoted) { - assertThat(this.dialect.applyQuote(unquoted), equalTo(quoted)); - } - - @ValueSource(strings = { "ab:'ab'", "a'b:'a''b'", "a''b:'a''''b'", "'ab':'''ab'''" }) - @ParameterizedTest - void testGetLiteralString(final String definition) { - assertThat(this.dialect.getStringLiteral(definition.substring(0, definition.indexOf(':'))), - equalTo(definition.substring(definition.indexOf(':') + 1))); - } - - @Test - void testGetLiteralStringNull() { - assertThat(this.dialect.getStringLiteral(null), CoreMatchers.equalTo("NULL")); - } - - @Test - void testRequiresCatalogQualifiedTableNames() { - assertThat(this.dialect.requiresCatalogQualifiedTableNames(null), equalTo(false)); - } - - @Test - void testRequiresSchemaQualifiedTableNames() { - assertThat(this.dialect.requiresSchemaQualifiedTableNames(null), equalTo(true)); - } - - @Test - void testGetSqlGenerationVisitor() { - assertThat(this.dialect.getSqlGenerationVisitor(null), CoreMatchers.instanceOf(DB2SqlGenerationVisitor.class)); - } - - @Test - void testGetDefaultNullSorting() { - assertThat(this.dialect.getDefaultNullSorting(), equalTo(SqlDialect.NullSorting.NULLS_SORTED_AT_END)); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlGenerationVisitorTest.java b/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlGenerationVisitorTest.java deleted file mode 100644 index 1b387d173..000000000 --- a/src/test/java/com/exasol/adapter/dialects/db2/DB2SqlGenerationVisitorTest.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.exasol.adapter.dialects.db2; - -import static com.exasol.adapter.dialects.VisitorAssertions.assertSqlNodeConvertedToAsterisk; -import static com.exasol.adapter.dialects.VisitorAssertions.assertSqlNodeConvertedToOne; -import static com.exasol.adapter.sql.AggregateFunction.AVG; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.math.BigDecimal; -import java.util.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -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.metadata.*; -import com.exasol.adapter.sql.*; - -@ExtendWith(MockitoExtension.class) -class DB2SqlGenerationVisitorTest { - private SqlGenerationVisitor visitor; - - @BeforeEach - void beforeEach(@Mock final ConnectionFactory connectionFactoryMock) { - final SqlDialectFactory dialectFactory = new DB2SqlDialectFactory(); - final SqlDialect dialect = dialectFactory.createSqlDialect(connectionFactoryMock, - AdapterProperties.emptyProperties()); - final SqlGenerationContext context = new SqlGenerationContext("test_catalog", "test_schema", false); - this.visitor = new DB2SqlGenerationVisitor(dialect, context); - } - - @Test - void testVisitSqlColumnWithoutParent() throws AdapterException { - final ColumnMetadata columnMetadata = ColumnMetadata.builder().name("test_column").type(DataType.createBool()) - .build(); - final SqlColumn column = new SqlColumn(1, columnMetadata); - assertThat(this.visitor.visit(column), equalTo("\"test_column\"")); - } - - @CsvSource(value = { "XML : XMLSERIALIZE(\"test_column\" as VARCHAR(32000) INCLUDING XMLDECLARATION)", // - "CLOB : CAST(SUBSTRING(\"test_column\",32672) AS VARCHAR(32672))", // - "CHAR () FOR BIT DATA : HEX(\"test_column\")", // - "VARCHAR () FOR BIT DATA : HEX(\"test_column\")", // - "TIME : VARCHAR(\"test_column\")", // - "TIMESTAMP : VARCHAR(\"test_column\")" // - }, delimiter = ':') - @ParameterizedTest - void testVisitSqlColumnWithParent(final String typeName, final String expected) throws AdapterException { - final SqlColumn column = getSqlColumn(typeName); - final SqlNode node = SqlSelectList.createSelectStarSelectList(); - column.setParent(node); - assertThat(this.visitor.visit(column), equalTo(expected)); - } - - private SqlColumn getSqlColumn(final String typeName) { - final ColumnMetadata columnMetadata = ColumnMetadata.builder().name("test_column").type(DataType.createBool()) - .adapterNotes("{\"jdbcDataType\":2009, \"typeName\":\"" + typeName + "\"}").build(); - return new SqlColumn(1, columnMetadata); - } - - @Test - void testVisitSqlColumnWithParentTypeNameIsNotSupported() throws AdapterException { - final SqlColumn column = getSqlColumn("BLOB"); - final SqlNode node = SqlSelectList.createSelectStarSelectList(); - column.setParent(node); - assertThat(this.visitor.visit(column), equalTo("'BLOB NOT SUPPORTED'")); - } - - @Test - void testVisitSqlStatementSelect() throws AdapterException { - final SqlStatementSelect select = (SqlStatementSelect) DialectTestData.getTestSqlNode(); - assertThat(this.visitor.visit(select), // - equalTo("SELECT \"USER_ID\", " // - + "COUNT(\"URL\") FROM \"test_schema\".\"CLICKS\" " // - + "WHERE 1 < \"USER_ID\" " // - + "GROUP BY \"USER_ID\" " // - + "HAVING 1 < COUNT(\"URL\") " // - + "ORDER BY \"USER_ID\" FETCH FIRST 10 ROWS ONLY")); - } - - @Test - void testVisitSqlSelectListAnyValue() throws AdapterException { - final SqlSelectList sqlSelectList = SqlSelectList.createAnyValueSelectList(); - assertSqlNodeConvertedToOne(sqlSelectList, this.visitor); - } - - @Test - void testVisitSqlSelectListSelectStar() throws AdapterException { - final SqlSelectList sqlSelectList = SqlSelectList.createSelectStarSelectList(); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("", tableMetadata); - final SqlNode sqlStatementSelect = SqlStatementSelect.builder().selectList(sqlSelectList).fromClause(fromClause) - .build(); - sqlSelectList.setParent(sqlStatementSelect); - assertSqlNodeConvertedToAsterisk(sqlSelectList, this.visitor); - } - - @Test - void testVisitSqlSelectListSelectStarRequiresCast() throws AdapterException { - final SqlSelectList sqlSelectList = createSqlSelectStarListWithOneColumn( - "{\"jdbcDataType\":2009, \"typeName\":\"XML\"}", DataType.createVarChar(10, DataType.ExaCharset.UTF8)); - assertThat(this.visitor.visit(sqlSelectList), - equalTo("XMLSERIALIZE(\"test_column\" as VARCHAR(32000) INCLUDING XMLDECLARATION)")); - } - - private SqlSelectList createSqlSelectStarListWithOneColumn(final String adapterNotes, final DataType dataType) { - final SqlSelectList selectList = SqlSelectList.createSelectStarSelectList(); - final List columns = new ArrayList<>(); - columns.add(ColumnMetadata.builder().name("test_column").adapterNotes(adapterNotes).type(dataType).build()); - final TableMetadata tableMetadata = new TableMetadata("", "", columns, ""); - final SqlTable fromClause = new SqlTable("", tableMetadata); - final SqlNode sqlStatementSelect = SqlStatementSelect.builder().selectList(selectList).fromClause(fromClause) - .build(); - selectList.setParent(sqlStatementSelect); - return selectList; - } - - @Test - void testVisitSqlSelectListSelectStarThrowsException() { - final SqlSelectList sqlSelectList = createSqlSelectStarListWithOneColumn("", - DataType.createVarChar(10, DataType.ExaCharset.UTF8)); - assertThrows(SqlGenerationVisitorException.class, () -> this.visitor.visit(sqlSelectList)); - } - - @Test - void testVisitSqlFunctionScalarTrimOneArgument() throws AdapterException { - final List arguments = new ArrayList<>(); - arguments.add(new SqlLiteralString("test")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(ScalarFunction.TRIM, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("TRIM('test')")); - } - - @Test - void testVisitSqlFunctionScalarTrimOTwoArguments() throws AdapterException { - final List arguments = List.of(new SqlLiteralString("ab cdef"), new SqlLiteralString("ab")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(ScalarFunction.TRIM, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("TRIM('ab' FROM 'ab cdef')")); - } - - @CsvSource({ "ADD_DAYS, 10, 10 DAYS", // - "ADD_HOURS, 10, 10 HOURS", // - "ADD_MINUTES, 10, 10 MINUTES", // - "ADD_SECONDS, 10, 10 SECONDS", // - "ADD_YEARS, 10, 10 YEARS", // - "ADD_WEEKS, 10, 70 DAYS" }) - @ParameterizedTest - void testVisitSqlFunctionScalarAddDateValues(final ScalarFunction scalarFunction, final int value, - final String expected) throws AdapterException { - final SqlColumn firstArgument = new SqlColumn(1, - ColumnMetadata.builder().name("test_column") - .adapterNotes("{\"jdbcDataType\":93, \"typeName\":\"TIMESTAMP\"}") - .type(DataType.createChar(20, DataType.ExaCharset.UTF8)).build()); - final List arguments = List.of(firstArgument, new SqlLiteralExactnumeric(new BigDecimal(value))); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(scalarFunction, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("VARCHAR(\"test_column\" + " + expected + ")")); - } - - @CsvSource({ "SYSDATE, CURRENT DATE", // - "CURRENT_DATE, CURRENT DATE", // - "DBTIMEZONE, DBTIMEZONE", // - "LOCALTIMESTAMP, LOCALTIMESTAMP", // - "SESSIONTIMEZONE, SESSIONTIMEZONE", // - "SYSTIMESTAMP, VARCHAR(CURRENT TIMESTAMP)", // - "CURRENT_TIMESTAMP, VARCHAR(CURRENT TIMESTAMP)" // - }) - @ParameterizedTest - void testVisitSqlFunctionScalar1(final ScalarFunction scalarFunction, final String expected) - throws AdapterException { - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(scalarFunction, null); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo(expected)); - } - - @CsvSource(value = { "BIT_AND : BITAND('left', 'right')", // - "BIT_TO_NUM : BIN_TO_NUM('left', 'right')", // - "NULLIFZERO : NULLIF('left', 0)", // - "ZEROIFNULL : IFNULL('left', 0)", // - "DIV : CAST(FLOOR('left' / FLOOR('right')) AS DECIMAL(36, 0))"// - }, delimiter = ':') - @ParameterizedTest - void testVisitSqlFunctionScalar2(final ScalarFunction scalarFunction, final String expected) - throws AdapterException { - final List arguments = List.of(new SqlLiteralString("left"), new SqlLiteralString("right")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(scalarFunction, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo(expected)); - } - - @Test - void testVisitSqlFunctionScalarDiv() throws AdapterException { - final List arguments = List.of(new SqlLiteralString("left"), new SqlLiteralString("right")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(ScalarFunction.DIV, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), - equalTo("CAST(FLOOR('left' / FLOOR('right')) AS DECIMAL(36, 0))")); - } - - @Test - void testVisitSqlFunctionAggregate() throws AdapterException { - final ColumnMetadata columnMetadata = ColumnMetadata.builder().name("test_column").type(DataType.createBool()) - .build(); - final List arguments = List.of(new SqlColumn(1, columnMetadata)); - final SqlFunctionAggregate sqlFunctionAggregate = new SqlFunctionAggregate(AVG, arguments, true); - assertThat(this.visitor.visit(sqlFunctionAggregate), equalTo("AVG(DISTINCT \"test_column\")")); - } - - @Test - void testVisitSqlFunctionAggregateVarSamp() throws AdapterException { - final List arguments = new ArrayList<>(); - arguments.add(new SqlLiteralString("test")); - final SqlFunctionAggregate sqlFunctionAggregate = new SqlFunctionAggregate(AggregateFunction.VAR_SAMP, - arguments, false); - assertThat(this.visitor.visit(sqlFunctionAggregate), equalTo("VARIANCE_SAMP('test')")); - } - - @Test - void testVisitSqlFunctionAggregateGroupConcat() throws AdapterException { - final SqlLiteralString argument = new SqlLiteralString("test"); - final ColumnMetadata columnMetadata = ColumnMetadata.builder().name("\"test_column").type(DataType.createBool()) - .build(); - final ColumnMetadata columnMetadata2 = ColumnMetadata.builder().name("test_column2\"") - .type(DataType.createDouble()).build(); - final List orderByArguments = List.of(new SqlColumn(1, columnMetadata), - new SqlColumn(2, columnMetadata2)); - final SqlOrderBy orderBy = new SqlOrderBy(orderByArguments, List.of(false, true), List.of(false, true)); - final SqlFunctionAggregateGroupConcat sqlFunctionAggregateGroupConcat = SqlFunctionAggregateGroupConcat - .builder(argument).orderBy(orderBy).separator(new SqlLiteralString("'")).build(); - assertThat(this.visitor.visit(sqlFunctionAggregateGroupConcat), - equalTo("LISTAGG('test', '''') WITHIN GROUP(ORDER BY \"\"\"test_column\" DESC, \"test_column2\"\"\")")); - } -} \ No newline at end of file