From 4e5adc4480beae567870c94edc30b9ce016e91a7 Mon Sep 17 00:00:00 2001 From: chiaradiamarcelo Date: Tue, 12 Jan 2021 12:09:50 +0100 Subject: [PATCH] Migrate Oracle dialect to its own repository (#439) * #438: Migrate Oracle dialect to its own repository --- .gitignore | 2 - README.md | 4 +- doc/changes/changelog.md | 1 + doc/changes/changes_6.0.0.md | 17 + .../integration_testing_with_containers.md | 11 +- doc/dialects/athena.md | 2 +- doc/dialects/aurora.md | 2 +- doc/dialects/bigquery.md | 2 +- doc/dialects/db2.md | 4 +- doc/dialects/hive.md | 4 +- doc/dialects/impala.md | 2 +- doc/dialects/oracle.md | 198 +--- doc/dialects/redshift.md | 2 +- doc/dialects/saphana.md | 2 +- doc/dialects/sql_server.md | 2 +- doc/dialects/sybase.md | 2 +- doc/dialects/teradata.md | 2 +- doc/user-guide/dialects.md | 6 +- pom.xml | 14 +- .../oracle/OracleColumnMetadataReader.java | 88 -- .../OracleConnectionDefinitionBuilder.java | 41 - .../dialects/oracle/OracleIdentifier.java | 64 -- .../dialects/oracle/OracleMetadataReader.java | 39 - .../dialects/oracle/OracleProperties.java | 14 - .../dialects/oracle/OracleQueryRewriter.java | 34 - .../dialects/oracle/OracleSqlDialect.java | 187 ---- .../oracle/OracleSqlDialectFactory.java | 21 - .../oracle/OracleSqlGenerationVisitor.java | 577 ------------ .../oracle/OracleTableMetadataReader.java | 37 - ....exasol.adapter.dialects.SqlDialectFactory | 1 - .../dialects/IntegrationTestConstants.java | 2 +- .../OracleColumnMetadataReaderTest.java | 68 -- ...OracleConnectionDefinitionBuilderTest.java | 38 - .../dialects/oracle/OracleIdentifierTest.java | 29 - .../oracle/OracleMetadataReaderTest.java | 28 - .../oracle/OracleQueryRewriterTest.java | 52 - .../oracle/OracleSqlDialectFactoryTest.java | 30 - .../dialects/oracle/OracleSqlDialectIT.java | 885 ------------------ .../dialects/oracle/OracleSqlDialectTest.java | 166 ---- .../OracleSqlGenerationVisitorTest.java | 407 -------- .../oracle/OracleTableMetadataReaderTest.java | 61 -- .../driver/oracle/oracle.properties | 4 - .../integration/driver/oracle/settings.cfg | 6 - 43 files changed, 43 insertions(+), 3115 deletions(-) create mode 100644 doc/changes/changes_6.0.0.md delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReader.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilder.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleIdentifier.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleMetadataReader.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleProperties.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriter.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialect.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactory.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitor.java delete mode 100644 src/main/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReader.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReaderTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilderTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleIdentifierTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleMetadataReaderTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriterTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactoryTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectIT.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitorTest.java delete mode 100644 src/test/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReaderTest.java delete mode 100644 src/test/resources/integration/driver/oracle/oracle.properties delete mode 100644 src/test/resources/integration/driver/oracle/settings.cfg diff --git a/.gitignore b/.gitignore index 867c8a6a8..779293357 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,6 @@ dependency-reduced-pom.xml # Integration tests src/test/resources/integration/driver/hive/*.jar -src/test/resources/integration/driver/oracle/*.jar -src/test/resources/integration/driver/oracle/*.zip # Others .DS_Store diff --git a/README.md b/README.md index 98942f179..7da88b63e 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,6 @@ Running the Virtual Schema requires a Java Runtime version 9 or later. | [JUnit](https://junit.org/junit5) | Unit testing framework | Eclipse Public License 1.0 | | [Mockito](http://site.mockito.org/) | Mocking framework | MIT License | | [Microsoft JDBC Driver for SQL Server][sql-server-jdbc-driver] | JDBC driver for SQL Server database | MIT License | -| [Oracle JDBC Driver][oracle-jdbc-driver] | JDBC driver for Oracle database | Oracle Technology Network License| | [Testcontainers](https://www.testcontainers.org/) | Container-based integration tests | MIT License | | [Test Database Builder][test-bd-builder] | Fluent database interfaces for testing | MIT License | @@ -166,7 +165,6 @@ Running the Virtual Schema requires a Java Runtime version 9 or later. [hbase-server]: http://hbase.apache.org/ [hive-jdbc-driver]: https://github.com/apache/hive/tree/master/jdbc/src/java/org/apache/hive/jdbc [maven-enforcer-plugin]: http://maven.apache.org/enforcer/maven-enforcer-plugin/ -[oracle-jdbc-driver]: https://www.oracle.com/database/technologies/appdev/jdbc.html [sql-server-jdbc-driver]: https://github.com/microsoft/mssql-jdbc [sonatype-oss-index-maven-plugin]: https://sonatype.github.io/ossindex-maven/maven-plugin/ [test-bd-builder]: https://github.com/exasol/test-db-builder-java @@ -180,7 +178,7 @@ Running the Virtual Schema requires a Java Runtime version 9 or later. [hive-dialect-doc]: doc/dialects/hive.md [impala-dialect-doc]: doc/dialects/impala.md [mysql-dialect-doc]: https://github.com/exasol/mysql-virtual-schema/blob/main/doc/user_guide/mysql_user_guide.md -[oracle-dialect-doc]: doc/dialects/oracle.md +[oracle-dialect-doc]: https://github.com/exasol/oracle-virtual-schema/blob/main/doc/user_guide/oracle_user_guide.md [postgresql-dialect-doc]: https://github.com/exasol/postgresql-virtual-schema/blob/main/doc/dialects/postgresql.md [redshift-dialect-doc]: doc/dialects/redshift.md [sap-hana-dialect-doc]: doc/dialects/saphana.md diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index 4e66bbc3f..7d50a2c3d 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,5 +1,6 @@ # Changes +* [6.0.0](changes_6.0.0.md) * [5.0.0](changes_5.0.0.md) * [4.0.5](changes_4.0.5.md) * [4.0.4](changes_4.0.4.md) diff --git a/doc/changes/changes_6.0.0.md b/doc/changes/changes_6.0.0.md new file mode 100644 index 000000000..1ca6d2458 --- /dev/null +++ b/doc/changes/changes_6.0.0.md @@ -0,0 +1,17 @@ +# Exasol Virtual Schemas 6.0.0, released 2021-XX-XX + +Code name: Migrated Oracle dialect implementation to its own repository. + +## Summary + +Please we aware you can not create Oracle Virtual Schemas using this JAR anymore. +Oracle dialect implementation has been migrated to https://github.com/exasol/oracle-virtual-schema. + +## Refactoring + +* #438: Removed Oracle dialect implementation as it has been migrated to https://github.com/exasol/mysql-virtual-schema. + +## Dependency updates + +* Removed `org.testcontainers:oracle-xe:1.15.0` +* Removed `com.oracle.ojdbc:ojdbc8:19.3.0.0` \ No newline at end of file diff --git a/doc/development/developing-sql-dialect/integration_testing_with_containers.md b/doc/development/developing-sql-dialect/integration_testing_with_containers.md index 925a97cc8..f01817cdd 100644 --- a/doc/development/developing-sql-dialect/integration_testing_with_containers.md +++ b/doc/development/developing-sql-dialect/integration_testing_with_containers.md @@ -79,18 +79,15 @@ Therefore we cannot include some jdbc drivers to the projects and you need to do List of disabled integration tests: * HiveSqlDialectIT -* OracleSqlDialectIT ### Starting Disabled Integration Test Locally -1. Download a JDBC driver and other necessary files: - - Hive [`HiveJDBC41.jar`](https://www.cloudera.com/downloads/connectors/hive/jdbc/2-5-4.html) - - Oracle [`ojdbc8.jar`](https://www.oracle.com/database/technologies/appdev/jdbc-ucp-19c-downloads.html) and oracle instant client [`instantclient-basic-linux.x64-12.1.0.2.0.zip`](https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html). Please be aware that Exasol currently supports only mentioned version of the Oracle instant client. -2. Temporarily put the files into `src/test/resources/integration/driver/` directory. Existing dialect directories: `hive`, `oracle`. +1. Download the [Hive JDBC driver `HiveJDBC41.jar`](https://www.cloudera.com/downloads/connectors/hive/jdbc/2-5-4.html) +2. Temporarily put the driver into `src/test/resources/integration/driver/hive` directory. -3. If the files' names are different (you renamed the file, or it has a different version number, for example) from the mentioned above, edit `src/test/resources/integration/driver//.properties` and `settings.cfg` files. +3. If the files' names are different (you renamed the file, or it has a different version number, for example) from the mentioned above, edit `src/test/resources/integration/driver/hive/hive.properties` and `settings.cfg` files. 4. Run the tests from an IDE or temporarily add the integration test name into the `maven-failsafe-plugin`'s includes a section and execute `mvn verify` command. -5. Remove the driver after the test. Do not upload it to the GitHub repository. +5. Remove the driver after the test and **do not upload it to the GitHub repository**. ## See also diff --git a/doc/dialects/athena.md b/doc/dialects/athena.md index 0cb7aa234..fcfdafd14 100644 --- a/doc/dialects/athena.md +++ b/doc/dialects/athena.md @@ -52,7 +52,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///AthenaJDBC42-.jar; / ; diff --git a/doc/dialects/aurora.md b/doc/dialects/aurora.md index ae696aa8e..70600a405 100644 --- a/doc/dialects/aurora.md +++ b/doc/dialects/aurora.md @@ -62,7 +62,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///postgresql-.jar; / ``` diff --git a/doc/dialects/bigquery.md b/doc/dialects/bigquery.md index 360ce07f1..fb1dc25a4 100644 --- a/doc/dialects/bigquery.md +++ b/doc/dialects/bigquery.md @@ -33,7 +33,7 @@ List all the JAR files from Magnitude Simba JDBC driver. ```sql CREATE JAVA ADAPTER SCRIPT SCHEMA_FOR_VS_SCRIPT.ADAPTER_SCRIPT_BIGQUERY AS %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-8.0.0-bundle-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///GoogleBigQueryJDBC42.jar; ... ... diff --git a/doc/dialects/db2.md b/doc/dialects/db2.md index c04d53de8..217966325 100644 --- a/doc/dialects/db2.md +++ b/doc/dialects/db2.md @@ -56,7 +56,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///db2jcc4.jar; %jar /buckets///db2jcc_license_cu.jar; / @@ -68,7 +68,7 @@ CREATE OR REPLACE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS ```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-5.0.0.jar; + %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; diff --git a/doc/dialects/hive.md b/doc/dialects/hive.md index 5ae521466..5259e7d73 100644 --- a/doc/dialects/hive.md +++ b/doc/dialects/hive.md @@ -47,7 +47,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```sql CREATE OR REPLACE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///jars/virtual-schema-dist-8.0.0-bundle-5.0.0.jar; + %jar /buckets///jars/virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///jars/HiveJDBC41.jar; / ``` @@ -302,7 +302,7 @@ In Virtual Schema adapter: CREATE OR REPLACE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS %jvmoption -Dsun.security.krb5.disableReferrals=true; %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///jars/virtual-schema-dist-8.0.0-bundle-5.0.0.jar; + %jar /buckets///jars/virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///jars/HiveJDBC41.jar; / ``` diff --git a/doc/dialects/impala.md b/doc/dialects/impala.md index 9751c466d..786c4d591 100644 --- a/doc/dialects/impala.md +++ b/doc/dialects/impala.md @@ -47,7 +47,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///ImpalaJDBC41.jar; / ; diff --git a/doc/dialects/oracle.md b/doc/dialects/oracle.md index c6a52e8ae..3043ae260 100644 --- a/doc/dialects/oracle.md +++ b/doc/dialects/oracle.md @@ -1,199 +1,3 @@ # Oracle SQL Dialect -[Oracle Database](https://www.oracle.com/database/) is a proprietary multi-model database management system produced and marketed by Oracle Corporation. It is a database commonly used for running online transaction processing (OLTP), data warehousing (DW) and mixed (OLTP & DW) database workloads. - -## Registering the JDBC Driver in EXAOperation - -First download the [Oracle JDBC driver](https://www.oracle.com/technetwork/database/application-development/jdbc/downloads/index.html). - -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 | `ORACLE` | -| Main | `oracle.jdbc.driver.OracleDriver` | -| Prefix | `jdbc:oracle:thin:` | -| Files | `ojdbc.jar` | - - -## 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 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. - -```sql -CREATE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS - %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-8.0.0-bundle-5.0.0.jar; - %jar /buckets///ojdbc.jar; -/ -; -``` - -## Defining a Named Connection - -Define the connection to Oracle as shown below. - -```sql -CREATE OR REPLACE CONNECTION ORACLE_JDBC_CONNECTION - TO 'jdbc:oracle:thin:@//:/' - USER '' - IDENTIFIED BY ''; -``` - -A quick option to test the `ORACLE_JDBC_CONNECTION` connection is to run an `IMPORT FROM JDBC` query. The connection works, if `42` is returned. - -```sql -IMPORT FROM JDBC AT ORACLE_JDBC_CONNECTION - STATEMENT 'SELECT 42 FROM DUAL'; -``` - -## Creating a Virtual Schema - -Below you see how an Oracle Virtual Schema is created. - -```sql -CREATE VIRTUAL SCHEMA - USING ADAPTER.JDBC_ADAPTER - WITH - SQL_DIALECT = 'ORACLE' - CONNECTION_NAME = 'ORACLE_JDBC_CONNECTION' - SCHEMA_NAME = ''; -``` - -## Using IMPORT FROM ORA Instead of IMPORT FROM JDBC - -Exasol provides the `IMPORT FROM ORA` command for loading data from Oracle. It is possible to create a virtual schema that uses `IMPORT FROM ORA` instead of JDBC to communicate with Oracle. Both options are indented to support the same features. `IMPORT FROM ORA` almost always offers better performance since it is implemented natively. - -This behavior is toggled by the Boolean `IMPORT_FROM_ORA` variable. Note that a JDBC connection to Oracle is still required to fetch metadata. In addition, a "direct" connection to the Oracle database is needed. - -### Deploying the Oracle Instant Client - -To be able to communicate with Oracle, you first need to supply Exasol with the Oracle Instant Client, which can be obtained [directly from Oracle](http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html). Open EXAoperation, visit Software -> "Upload Oracle Instant Client" and select the downloaded package. The latest version of Oracle Instant Client we tested is `instantclient-basic-linux.x64-12.1.0.2.0`. - -### Creating an Oracle Connection - -Having deployed the Oracle Instant Client, a connection to your Oracle database can be set up. - -```sql -CREATE CONNECTION ORA_CONNECTION - TO '(DESCRIPTION = - (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP) - (HOST = ) - (PORT = ))) - (CONNECT_DATA = (SERVER = DEDICATED) - (SERVICE_NAME = )))' - USER '' - IDENTIFIED BY ''; -``` - -This connection can be tested using, e.g., the following SQL expression. - -```sql -IMPORT FROM ORA at ORA_CONNECTION - STATEMENT 'SELECT 42 FROM DUAL'; -``` - -### Creating a Virtual Schema USING an ORA CONNECTION - -Assuming you already setup the JDBC connection `ORACLE_JDBC_CONNECTION` as shown in the previous section, you can continue with creating the virtual schema. - -```sql -CREATE VIRTUAL SCHEMA - USING ADAPTER.JDBC_ADAPTER - WITH - SQL_DIALECT = 'ORACLE' - CONNECTION_NAME = 'ORACLE_JDBC_CONNECTION' - SCHEMA_NAME = '' - IMPORT_FROM_ORA = 'true' - ORA_CONNECTION_NAME = 'ORA_CONNECTION'; -``` - -## Supported Capabilities - -The Oracle dialect does not support all capabilities. A complete list can be found in [OracleSqlDialect.getCapabilities()](../../src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialect.java). - -## Type Mappings and Limitations - -| Orcale Data Type | Supported | Converted Exasol Data Type | Comments | -| -------------------------------------------------------------------------------- | --------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| BINARY\_DOUBLE | ✓ | VARCHAR(2000000) | | -| BINARY\_FLOAT | ✓ | VARCHAR(2000000) | | -| BLOB | × | | | -| BFILE | × | | | -| CHAR \[(size)\] | ✓ | CHAR | | -| CLOB | × | | | -| DATE | ✓ | TIMESTAMP | This data type is only supported for positive year values, i.e., years > 0001 | -| FLOAT \[(p)\] | ✓ | DOUBLE | | -| INTERVAL DAY \[(day\_precision)\] TO SECOND \[(fractional\_seconds\_precision)\] | ✓ | VARCHAR(2000000) | | -| INTERVAL YEAR \[(year\_precision)\] TO MONTH | ✓ | VARCHAR(2000000) | | -| LONG | ✓ | VARCHAR(2000000) | Casted to VARCHAR to prevent a loss of precision. | -| LONG RAW | × | | | -| NCLOB | × | | | -| NCHAR\[(size)\] | ✓ | CHAR | | -| NUMBER \[ (p \[, s\]) \] | ✓ | NUMBER or VARCHAR(2000000) | NUMBER with precision > 36 are casted to VARCHAR to prevent a loss of precision. [*](#Mapping-of-number-types) | -| NVARCHAR2(size) | ✓ | VARCHAR | | -| RAW(size) | × | | | -| ROWID | × | | | -| TIMESTAMP \[(fractional\_seconds\_precision)\] | ✓ | TIMESTAMP | | -| TIMESTAMP \[(fractional\_seconds\_precision)\] WITH TIME ZONE | ✓ | TIMESTAMP | | -| UROWID \[(size)\] | × | | | -| VARCHAR2(size) | ✓ | VARCHAR | | - - - -### Mapping of Number Types: - -`NUMBER`, `NUMBER with precision > 36` and `LONG` are casted to `VARCHAR` to prevent a loss of precision. - -If you want to return a DECIMAL type for these types you can set the property `ORACLE_CAST_NUMBER_TO_DECIMAL_WITH_PRECISION_AND_SCALE` to `,`. -This will cast values of such types to `DECIMAL(,)`. - -For example: - -```java -CREATE VIRTUAL SCHEMA - USING ADAPTER.JDBC_ADAPTER - WITH - SQL_DIALECT = 'ORACLE' - CONNECTION_NAME = 'ORACLE_JDBC_CONNECTION' - SCHEMA_NAME = '' - IMPORT_FROM_ORA = 'true' - ORACLE_CAST_NUMBER_TO_DECIMAL_WITH_PRECISION_AND_SCALE = '2,18' - ORA_CONNECTION_NAME = 'ORA_CONNECTION'; -``` - -Keep in mind that this will yield errors if the data in the Oracle database does not fit into the specified DECIMAL type. - -## Testing information - -In the following matrix you find combinations of JDBC driver and dialect version that we tested. - -| Virtual Schema Version | Oracle Version | Driver Name | Driver Version | -|------------------------|--------------------|---------------------------|----------------| -| 4.0.3 | Oracle XE 11g | ojdbc | 8 | -| 4.0.3 | Oracle XE 11g | instantclient-basic-linux | x64-12.1.0.2.0 | +The Oracle Virtual Schema has been migrated to its [own repository](https://github.com/exasol/oracle-virtual-schema/). \ No newline at end of file diff --git a/doc/dialects/redshift.md b/doc/dialects/redshift.md index eccc7d6e9..a8d70068a 100644 --- a/doc/dialects/redshift.md +++ b/doc/dialects/redshift.md @@ -51,7 +51,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///RedshiftJDBC42-.jar; / ; diff --git a/doc/dialects/saphana.md b/doc/dialects/saphana.md index 1e4828c8b..4d0250521 100644 --- a/doc/dialects/saphana.md +++ b/doc/dialects/saphana.md @@ -47,7 +47,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```sql CREATE JAVA ADAPTER SCRIPT ADAPTER.JDBC_ADAPTER AS %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-8.0.0-bundle-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///ngdbc-.jar; / ; diff --git a/doc/dialects/sql_server.md b/doc/dialects/sql_server.md index 9051a1691..b52af8ed0 100644 --- a/doc/dialects/sql_server.md +++ b/doc/dialects/sql_server.md @@ -46,7 +46,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```sql CREATE OR REPLACE JAVA ADAPTER SCRIPT SCHEMA_FOR_VS_SCRIPT.ADAPTER_SCRIPT_SQLSERVER AS %scriptclass com.exasol.adapter.RequestDispatcher; - %jar /buckets///virtual-schema-dist-8.0.0-bundle-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///mssql-jdbc-.jre8.jar; / ``` diff --git a/doc/dialects/sybase.md b/doc/dialects/sybase.md index c6db065e5..0d8614f3f 100644 --- a/doc/dialects/sybase.md +++ b/doc/dialects/sybase.md @@ -29,7 +29,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///jtds-.jar; / ``` diff --git a/doc/dialects/teradata.md b/doc/dialects/teradata.md index 52880356b..811ced213 100644 --- a/doc/dialects/teradata.md +++ b/doc/dialects/teradata.md @@ -47,7 +47,7 @@ The SQL statement below creates the adapter script, defines the Java class that ```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-5.0.0.jar; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-6.0.0.jar; %jar /buckets///terajdbc4.jar; %jar /buckets///tdgssconfig.jar; / diff --git a/doc/user-guide/dialects.md b/doc/user-guide/dialects.md index 88ce272b0..3a7e73570 100644 --- a/doc/user-guide/dialects.md +++ b/doc/user-guide/dialects.md @@ -10,7 +10,7 @@ Dialect name | Repository [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] | [MySQL][mysql-dialect-doc] | [MySQL Virtual Schema][mysql-virtual-schema-repository] | [Latest release][mysql-virtual-schema-releases] | -[Oracle][oracle-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | +[Oracle][oracle-dialect-doc] | [Virtual Schemas][oracle-virtual-schema-repository] | [Latest release][oracle-virtual-schema-releases] | [PostgreSQL][postgresql-dialect-doc]| [PostgreSQL Virtual Schema][pg-virtual-schema-repository] | [Latest release][pg-virtual-schema-releases] | [Redshift][redshift-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | [SAP HANA][sap-hana-dialect-doc] | [Virtual Schemas][virtual-schemas-repository] | [Latest release][virtual-schemas-releases] | @@ -30,7 +30,7 @@ You can also [develop a custom dialect](../development/developing-sql-dialect/de [hive-dialect-doc]: ../dialects/hive.md [impala-dialect-doc]: ../dialects/impala.md [mysql-dialect-doc]: https://github.com/exasol/mysql-virtual-schema/blob/main/doc/user_guide/mysql_user_guide.md -[oracle-dialect-doc]: ../dialects/oracle.md +[oracle-dialect-doc]: https://github.com/exasol/oracle-virtual-schema/blob/main/doc/user_guide/oracle_user_guide.md [postgresql-dialect-doc]: https://github.com/exasol/postgresql-virtual-schema/blob/main/doc/dialects/postgresql.md [redshift-dialect-doc]: ../dialects/redshift.md [sap-hana-dialect-doc]: ../dialects/saphana.md @@ -44,5 +44,7 @@ You can also [develop a custom dialect](../development/developing-sql-dialect/de [exasol-virtual-schema-releases]: https://github.com/exasol/exasol-virtual-schema/releases [mysql-virtual-schema-repository]: https://github.com/exasol/mysql-virtual-schema [mysql-virtual-schema-releases]: https://github.com/exasol/mysql-virtual-schema/releases +[oracle-virtual-schema-repository]: https://github.com/exasol/oracle-virtual-schema +[oracle-virtual-schema-releases]: https://github.com/exasol/oracle-virtual-schema/releases [pg-virtual-schema-repository]: https://github.com/exasol/postgresql-virtual-schema [pg-virtual-schema-releases]: https://github.com/exasol/postgresql-virtual-schema/releases \ No newline at end of file diff --git a/pom.xml b/pom.xml index c6b450202..c157206f3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.exasol virtual-schema-jdbc-adapter - 5.0.0 + 6.0.0 Virtual Schema JDBC Adapter UTF-8 @@ -104,18 +104,6 @@ ${org.testcontainers.version} test - - org.testcontainers - oracle-xe - ${org.testcontainers.version} - test - - - com.oracle.ojdbc - ojdbc8 - 19.3.0.0 - test - org.apache.hive hive-jdbc diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReader.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReader.java deleted file mode 100644 index c4c0981ac..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReader.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY; - -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 Oracle-specific reading of column metadata. - */ -public class OracleColumnMetadataReader extends BaseColumnMetadataReader { - private static final int ORACLE_TIMESTAMP_WITH_LOCAL_TIME_ZONE = -101; - private static final int ORACLE_TIMESTAMP_WITH_TIME_ZONE = -102; - private static final int ORACLE_BINARY_FLOAT = 100; - private static final int ORACLE_BINARY_DOUBLE = 101; - private static final int INTERVAL_DAY_TO_SECOND = -104; - private static final int INTERVAL_YEAR_TO_MONTH = -103; - static final int ORACLE_MAGIC_NUMBER_SCALE = -127; - - /** - * Create a new instance of the {@link OracleColumnMetadataReader} - * - * @param connection connection to the remote data source - * @param properties user-defined adapter properties - * @param identifierConverter converter between source and Exasol identifiers - */ - public OracleColumnMetadataReader(final Connection connection, final AdapterProperties properties, - final IdentifierConverter identifierConverter) { - super(connection, properties, identifierConverter); - } - - @Override - public DataType mapJdbcType(final JdbcTypeDescription jdbcTypeDescription) { - switch (jdbcTypeDescription.getJdbcType()) { - case Types.DECIMAL: - case Types.NUMERIC: - return mapNumericType(jdbcTypeDescription); - case ORACLE_TIMESTAMP_WITH_TIME_ZONE: - case ORACLE_TIMESTAMP_WITH_LOCAL_TIME_ZONE: - return DataType.createTimestamp(false); - case INTERVAL_YEAR_TO_MONTH: - case INTERVAL_DAY_TO_SECOND: - case ORACLE_BINARY_FLOAT: - case ORACLE_BINARY_DOUBLE: - return DataType.createMaximumSizeVarChar(DataType.ExaCharset.UTF8); - default: - return super.mapJdbcType(jdbcTypeDescription); - } - } - - protected DataType mapNumericType(final JdbcTypeDescription jdbcTypeDescription) { - final int decimalScale = jdbcTypeDescription.getDecimalScale(); - if (decimalScale == ORACLE_MAGIC_NUMBER_SCALE) { - return workAroundNumberWithoutScaleAndPrecision(); - } - final int decimalPrecision = jdbcTypeDescription.getPrecisionOrSize() == 0 - ? DataType.MAX_EXASOL_DECIMAL_PRECISION - : jdbcTypeDescription.getPrecisionOrSize(); - if (decimalPrecision <= DataType.MAX_EXASOL_DECIMAL_PRECISION) { - return DataType.createDecimal(decimalPrecision, decimalScale); - } else { - return workAroundNumberWithoutScaleAndPrecision(); - } - } - - /** - * @return Oracle JDBC driver returns scale -127 if NUMBER data type was specified without scale and precision. - * Convert to VARCHAR. See http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i16209 and - * https://docs.oracle.com/cd/E19501-01/819-3659/gcmaz/ - */ - private DataType workAroundNumberWithoutScaleAndPrecision() { - return getOracleNumberTargetType(); - } - - private DataType getOracleNumberTargetType() { - if (this.properties.containsKey(ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY)) { - return getNumberTypeFromProperty(ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY); - } else { - return DataType.createMaximumSizeVarChar(DataType.ExaCharset.UTF8); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilder.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilder.java deleted file mode 100644 index 1aaf06df6..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilder.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_CONNECTION_NAME_PROPERTY; -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_IMPORT_PROPERTY; - -import com.exasol.ExaConnectionInformation; -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.jdbc.BaseConnectionDefinitionBuilder; - -/** - * This class implements an Oracle-specific connection definition builder. - */ -public class OracleConnectionDefinitionBuilder extends BaseConnectionDefinitionBuilder { - @Override - public String buildConnectionDefinition(final AdapterProperties properties, - final ExaConnectionInformation exaConnectionInformation) { - if (properties.containsKey(ORACLE_IMPORT_PROPERTY)) { - return buildImportFromOraConnectionDefinition(properties); - } else { - return super.buildConnectionDefinition(properties, exaConnectionInformation); - } - } - - private String buildImportFromOraConnectionDefinition(final AdapterProperties properties) { - if (properties.containsKey(ORACLE_CONNECTION_NAME_PROPERTY)) { - return buildOracleConnectionDefinitionFromOracleConnectionOnly(properties); - } else { - throw new IllegalArgumentException("If you enable IMPORT FROM ORA with property \"" + ORACLE_IMPORT_PROPERTY - + "\" you also need to provide the name of an Oracle connection with \"" - + ORACLE_CONNECTION_NAME_PROPERTY + "\"."); - } - } - - private String getOracleConnectionName(final AdapterProperties properties) { - return properties.get(ORACLE_CONNECTION_NAME_PROPERTY); - } - - private String buildOracleConnectionDefinitionFromOracleConnectionOnly(final AdapterProperties properties) { - return "AT " + getOracleConnectionName(properties); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleIdentifier.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleIdentifier.java deleted file mode 100644 index 5d4a78a45..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleIdentifier.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import java.util.Objects; - -import com.exasol.db.Identifier; - -/** - * Represents an identifier in the Oracle database. - */ -public class OracleIdentifier implements Identifier { - private final String id; - - private OracleIdentifier(final String id) { - this.id = id; - } - - /** - * Get the quoted identifier as a {@link String}. - * - * @return quoted identifier - */ - @Override - public String quote() { - return "\"" + this.id + "\""; - } - - /** - * Create a new {@link OracleIdentifier}. - * - * @param id the identifier as {@link String} - * @return new {@link OracleIdentifier} instance - */ - public static OracleIdentifier of(final String id) { - if (validate(id)) { - return new OracleIdentifier(id); - } else { - throw new AssertionError("E-ID-3: Unable to create identifier \"" + id // - + "\" because it contains illegal characters." // - + " For information about valid identifiers, please refer to" // - + " https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm"); - } - } - - private static boolean validate(final String id) { - return !id.contains("\""); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof OracleIdentifier)) { - return false; - } - final OracleIdentifier that = (OracleIdentifier) o; - return Objects.equals(this.id, that.id); - } - - @Override - public int hashCode() { - return Objects.hash(this.id); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleMetadataReader.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleMetadataReader.java deleted file mode 100644 index 9420ad76a..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleMetadataReader.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -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 Oracle-specific database metadata. - */ -public class OracleMetadataReader extends AbstractRemoteMetadataReader { - /** - * Create a new instance of the {@link OracleMetadataReader} - * - * @param connection database connection through which the reader retrieves the metadata from the remote source - * @param properties user-defined properties - */ - public OracleMetadataReader(final Connection connection, final AdapterProperties properties) { - super(connection, properties); - } - - @Override - protected TableMetadataReader createTableMetadataReader() { - return new OracleTableMetadataReader(this.connection, getColumnMetadataReader(), this.properties, - super.getIdentifierConverter()); - } - - @Override - protected ColumnMetadataReader createColumnMetadataReader() { - return new OracleColumnMetadataReader(this.connection, this.properties, getIdentifierConverter()); - } - - @Override - protected IdentifierConverter createIdentifierConverter() { - return BaseIdentifierConverter.createDefault(); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleProperties.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleProperties.java deleted file mode 100644 index 53a23ce1e..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleProperties.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -/** - * This class contains Oracle-specific adapter properties. - */ -public final class OracleProperties { - public static final String ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY = "ORACLE_CAST_NUMBER_TO_DECIMAL_WITH_PRECISION_AND_SCALE"; - public static final String ORACLE_IMPORT_PROPERTY = "IMPORT_FROM_ORA"; - public static final String ORACLE_CONNECTION_NAME_PROPERTY = "ORA_CONNECTION_NAME"; - - private OracleProperties() { - // prevent instantiation - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriter.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriter.java deleted file mode 100644 index 567c47d3b..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriter.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import java.sql.SQLException; - -import com.exasol.adapter.dialects.AbstractQueryRewriter; -import com.exasol.adapter.dialects.SqlDialect; -import com.exasol.adapter.jdbc.ConnectionDefinitionBuilder; -import com.exasol.adapter.jdbc.RemoteMetadataReader; - -/** - * This class implements an Oracle-specific query rewriter. - */ -public class OracleQueryRewriter extends AbstractQueryRewriter { - /** - * Create a new instance of the {@link OracleQueryRewriter}. - * - * @param dialect Oracle SQl dialect - * @param remoteMetadataReader reader for metadata from the remote data source - */ - public OracleQueryRewriter(final SqlDialect dialect, final RemoteMetadataReader remoteMetadataReader) { - super(dialect, remoteMetadataReader); - } - - @Override - protected ConnectionDefinitionBuilder createConnectionDefinitionBuilder() { - return new OracleConnectionDefinitionBuilder(); - } - - @Override - protected String generateImportStatement(final String connectionDefinition, final String pushdownQuery) - throws SQLException { - return "IMPORT FROM ORA " + connectionDefinition + " STATEMENT '" + pushdownQuery.replace("'", "''") + "'"; - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialect.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialect.java deleted file mode 100644 index d0d9c2ff1..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialect.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.AdapterProperties.IS_LOCAL_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.adapter.dialects.oracle.OracleProperties.*; - -import java.sql.SQLException; -import java.util.*; -import java.util.stream.Collectors; - -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.metadata.DataType; -import com.exasol.adapter.sql.*; - -/** - * This class implements the Oracle SQL dialect. - */ -public class OracleSqlDialect extends AbstractSqlDialect { - static final String NAME = "ORACLE"; - private static final Capabilities CAPABILITIES = createCapabilityList(); - - 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, LIMIT_WITH_OFFSET, JOIN, - JOIN_TYPE_INNER, JOIN_TYPE_LEFT_OUTER, JOIN_TYPE_RIGHT_OUTER, JOIN_TYPE_FULL_OUTER, - JOIN_CONDITION_EQUI) - .addPredicate(AND, OR, NOT, EQUAL, NOTEQUAL, LESS, LESSEQUAL, LIKE, LIKE_ESCAPE, REGEXP_LIKE, 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) - .addAggregateFunction(SUM, SUM_DISTINCT, MIN, MAX, AVG, AVG_DISTINCT, MEDIAN, FIRST_VALUE, LAST_VALUE, - STDDEV, STDDEV_DISTINCT, STDDEV_POP, STDDEV_SAMP, VARIANCE, VARIANCE_DISTINCT, VAR_POP, - VAR_SAMP) - .addScalarFunction(CEIL, DIV, FLOOR, SIGN) - .addScalarFunction(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) - .addScalarFunction(ASCII, CHR, INSTR, LENGTH, LOCATE, LOWER, LPAD, LTRIM, REGEXP_INSTR, REGEXP_REPLACE, - REGEXP_SUBSTR, REPEAT, REPLACE, REVERSE, 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, DBTIMEZONE, LOCALTIMESTAMP, NUMTODSINTERVAL, NUMTOYMINTERVAL, - SESSIONTIMEZONE, SYSDATE, SYSTIMESTAMP, CAST, TO_CHAR, TO_DATE, TO_DSINTERVAL, TO_YMINTERVAL, - TO_NUMBER, TO_TIMESTAMP, BIT_AND, BIT_TO_NUM, CASE, NULLIFZERO, ZEROIFNULL) - .build(); - } - - /** - * Create a new instance of the {@link OracleSqlDialect}. - * - * @param connectionFactory factory for the JDBC connection to the remote data source - * @param properties user-defined adapter properties - */ - public OracleSqlDialect(final ConnectionFactory connectionFactory, final AdapterProperties properties) { - super(connectionFactory, properties, Set.of(SCHEMA_NAME_PROPERTY, ORACLE_IMPORT_PROPERTY, - ORACLE_CONNECTION_NAME_PROPERTY, ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY)); - this.omitParenthesesMap.add(ScalarFunction.SYSDATE); - this.omitParenthesesMap.add(ScalarFunction.SYSTIMESTAMP); - } - - @Override - public String getName() { - return NAME; - } - - @Override - public Capabilities getCapabilities() { - return CAPABILITIES; - } - - @Override - public Map getAggregateFunctionAliases() { - return new EnumMap<>(AggregateFunction.class); - } - - @Override - public StructureElementSupport supportsJdbcCatalogs() { - return StructureElementSupport.NONE; - } - - @Override - public StructureElementSupport supportsJdbcSchemas() { - return StructureElementSupport.MULTIPLE; - } - - DataType getOracleNumberTargetType() { - if (this.properties.containsKey(ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY)) { - return getOracleNumberTypeFromProperty(); - } else { - return DataType.createMaximumSizeVarChar(DataType.ExaCharset.UTF8); - } - } - - private DataType getOracleNumberTypeFromProperty() { - final String oraclePrecisionAndScale = this.properties.get(ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY); - final List precisionAndScaleList = Arrays.stream(oraclePrecisionAndScale.split(",")).map(String::trim) - .collect(Collectors.toList()); - return DataType.createDecimal(Integer.parseInt(precisionAndScaleList.get(0)), - Integer.parseInt(precisionAndScaleList.get(1))); - } - - @Override - public SqlNodeVisitor getSqlGenerationVisitor(final SqlGenerationContext context) { - return new OracleSqlGenerationVisitor(this, context); - } - - @Override - // https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm - public String applyQuote(final String identifier) { - return OracleIdentifier.of(identifier).quote(); - } - - @Override - public boolean requiresCatalogQualifiedTableNames(final SqlGenerationContext context) { - return false; - } - - @Override - public boolean requiresSchemaQualifiedTableNames(final SqlGenerationContext context) { - return true; - } - - @Override - public NullSorting getDefaultNullSorting() { - return NullSorting.NULLS_SORTED_HIGH; - } - - @Override - // https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements003.htm - public String getStringLiteral(final String value) { - return super.quoteLiteralStringWithSingleQuote(value); - } - - /** - * Return the type of import the Oracle dialect uses. - * - * @return import type - */ - public ImportType getImportType() { - if (this.properties.isEnabled(IS_LOCAL_PROPERTY)) { - return ImportType.LOCAL; - } else if (this.properties.isEnabled(ORACLE_IMPORT_PROPERTY)) { - return ImportType.ORA; - } else { - return ImportType.JDBC; - } - } - - @Override - protected RemoteMetadataReader createRemoteMetadataReader() { - try { - return new OracleMetadataReader(this.connectionFactory.getConnection(), this.properties); - } catch (final SQLException exception) { - throw new RemoteMetadataReaderException( - "Unable to create Oracle remote metadata reader. Caused by: " + exception.getMessage(), exception); - } - } - - @Override - protected QueryRewriter createQueryRewriter() { - if (this.isImportFromOraEnabled()) { - return new OracleQueryRewriter(this, createRemoteMetadataReader()); - } - return new ImportIntoQueryRewriter(this, createRemoteMetadataReader(), this.connectionFactory); - } - - private boolean isImportFromOraEnabled() { - return this.properties.isEnabled(OracleProperties.ORACLE_IMPORT_PROPERTY); - } - - @Override - public void validateProperties() throws PropertyValidationException { - super.validateProperties(); - checkImportPropertyConsistency(ORACLE_IMPORT_PROPERTY, ORACLE_CONNECTION_NAME_PROPERTY); - validateBooleanProperty(ORACLE_IMPORT_PROPERTY); - validateCastNumberToDecimalProperty(ORACLE_CAST_NUMBER_TO_DECIMAL_PROPERTY); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactory.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactory.java deleted file mode 100644 index 33965517f..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactory.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -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 Oracle SQL dialect. - */ -public class OracleSqlDialectFactory extends AbstractSqlDialectFactory { - @Override - public String getSqlDialectName() { - return OracleSqlDialect.NAME; - } - - @Override - public SqlDialect createSqlDialect(final ConnectionFactory connectionFactory, final AdapterProperties properties) { - return new OracleSqlDialect(connectionFactory, properties); - } -} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitor.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitor.java deleted file mode 100644 index eccce6e6c..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitor.java +++ /dev/null @@ -1,577 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.sql.AggregateFunction.*; -import static com.exasol.adapter.sql.ScalarFunction.*; - -import java.util.*; - -import com.exasol.adapter.AdapterException; -import com.exasol.adapter.dialects.*; -import com.exasol.adapter.metadata.*; -import com.exasol.adapter.sql.*; - -/** - * This class generates SQL queries for the {@link OracleSqlGenerationVisitor}. - */ -public class OracleSqlGenerationVisitor extends SqlGenerationVisitor { - private boolean requiresSelectListAliasesForLimit = false; - private static final String TIMESTAMP_FORMAT = "'YYYY-MM-DD HH24:MI:SS.FF3'"; - private static final List TYPE_NAMES_REQUIRING_CAST = List.of("TIMESTAMP", "INTERVAL", "BINARY_FLOAT", - "BINARY_DOUBLE"); - private final Set aggregateFunctionsCast = EnumSet.noneOf(AggregateFunction.class); - private final Set scalarFunctionsCast = EnumSet.noneOf(ScalarFunction.class); - - /** - * Create a new instance of the {@link OracleSqlGenerationVisitor}. - * - * @param dialect {@link OracleSqlDialect} SQL dialect - * @param context SQL generation context - */ - public OracleSqlGenerationVisitor(final SqlDialect dialect, final SqlGenerationContext context) { - super(dialect, context); - addAggregateFunctions(); - addScalarFunctions(); - } - - private void addScalarFunctions() { - this.scalarFunctionsCast.addAll(Arrays.asList(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)); - } - - private void addAggregateFunctions() { - this.aggregateFunctionsCast.addAll(Arrays.asList(SUM, MIN, MAX, AVG, MEDIAN, FIRST_VALUE, LAST_VALUE, STDDEV, - STDDEV_POP, STDDEV_SAMP, VARIANCE, VAR_POP, VAR_SAMP)); - - } - - Set getAggregateFunctionsCast() { - return this.aggregateFunctionsCast; - } - - Set getScalarFunctionsCast() { - return this.scalarFunctionsCast; - } - - /** - * ORACLE Syntax (before 12c) for LIMIT 10:
- * SELECT LIMIT_SUBSELECT.* FROM ( <query-with-aliases> ) LIMIT_SUBSELECT WHERE ROWNUM <= 30 - * - * ORACLE Syntax (before 12c) for LIMIT 10 OFFSET 20:
- * SELECT c1, c2, ... FROM ( SELECT LIMIT_SUBSELECT.*, ROWNUM ROWNUM_SUB FROM ( <query-with-aliases> ) - * LIMIT_SUBSELECT WHERE ROWNUM <= 30 ) WHERE ROWNUM_SUB > 20 - * - * The ROWNUM filter is evaluated before ORDER BY, which is why we need sub-selects - */ - @Override - public String visit(final SqlStatementSelect select) throws AdapterException { - if (!select.hasLimit()) { - return super.visit(select); - } else { - return getSqlStatementSelectWithLimit(select); - } - } - - private String getSqlStatementSelectWithLimit(final SqlStatementSelect select) throws AdapterException { - final SqlLimit limit = select.getLimit(); - if (limit.hasOffset()) { - return getSqlStatementSelectWithOffset(select, limit); - } else { - return getSqlStatementSelectWithoutOffset(select, limit); - } - } - - private String getSqlStatementSelectWithOffset(final SqlStatementSelect select, final SqlLimit limit) - throws AdapterException { - final StringBuilder builder = new StringBuilder(); - builder.append("SELECT "); - if (select.getSelectList().isRequestAnyColumn()) { - return "1"; - } else if (select.getSelectList().isSelectStar()) { - appendSelectStar(select, builder); - } else { - final int numberOfExpressions = select.getSelectList().getExpressions().size(); - builder.append(String.join(", ", buildAliases(numberOfExpressions))); - } - builder.append(" FROM ( "); - builder.append("SELECT LIMIT_SUBSELECT.*, ROWNUM ROWNUM_SUB FROM ( "); - this.requiresSelectListAliasesForLimit = true; - builder.append(super.visit(select)); - builder.append(" ) LIMIT_SUBSELECT WHERE ROWNUM <= "); - builder.append(limit.getLimit() + limit.getOffset()); - builder.append(" ) WHERE ROWNUM_SUB > "); - builder.append(limit.getOffset()); - return builder.toString(); - } - - private void appendSelectStar(final SqlStatementSelect select, final StringBuilder builder) { - int numberOfColumns = 0; - final List tableMetadata = new ArrayList<>(); - SqlGenerationHelper.addMetadata(select.getFromClause(), tableMetadata); - for (final TableMetadata tableMeta : tableMetadata) { - numberOfColumns += tableMeta.getColumns().size(); - } - builder.append(String.join(", ", buildAliases(numberOfColumns))); - } - - private List buildAliases(final int numSelectListElements) { - final List aliases = new ArrayList<>(numSelectListElements); - for (int i = 0; i < numSelectListElements; i++) { - aliases.add("c" + i); - } - return aliases; - } - - private String getSqlStatementSelectWithoutOffset(final SqlStatementSelect select, final SqlLimit limit) - throws AdapterException { - final StringBuilder builder = new StringBuilder(); - builder.append("SELECT LIMIT_SUBSELECT.* FROM ( "); - builder.append(super.visit(select)); - builder.append(" ) LIMIT_SUBSELECT WHERE ROWNUM <= "); - builder.append(limit.getLimit() + limit.getOffset()); - return builder.toString(); - } - - @Override - public String visit(final SqlSelectList selectList) throws AdapterException { - if (selectList.isRequestAnyColumn()) { - return "1"; - } else { - return getSqlSelectList(selectList); - } - } - - private String getSqlSelectList(final SqlSelectList selectList) throws AdapterException { - final List selectListElements = new ArrayList<>(); - if (selectList.isSelectStar()) { - getSelectStarList(selectList, selectListElements); - } else { - for (final SqlNode node : selectList.getExpressions()) { - selectListElements.add(node.accept(this)); - } - } - if (this.requiresSelectListAliasesForLimit) { - addColumnAliases(selectListElements); - } - return String.join(", ", selectListElements); - } - - private void getSelectStarList(final SqlSelectList selectList, final List selectListElements) - throws AdapterException { - final SqlStatementSelect select = (SqlStatementSelect) selectList.getParent(); - final boolean selectListRequiresCasts = isSelectListRequiresCasts(selectList, selectListElements, select); - if (!this.requiresSelectListAliasesForLimit && !selectListRequiresCasts) { - selectListElements.clear(); - selectListElements.add("*"); - } - } - - private boolean isSelectListRequiresCasts(final SqlSelectList selectList, final List selectListElements, - final SqlStatementSelect select) throws AdapterException { - boolean selectListRequiresCasts = false; - int columnId = 0; - final List tableMetadata = new ArrayList<>(); - SqlGenerationHelper.addMetadata(select.getFromClause(), tableMetadata); - for (final TableMetadata tableMeta : tableMetadata) { - for (final ColumnMetadata columnMeta : tableMeta.getColumns()) { - final SqlColumn sqlColumn = new SqlColumn(columnId, columnMeta); - sqlColumn.setParent(selectList); - selectListRequiresCasts |= nodeRequiresCast(sqlColumn); - selectListElements.add(sqlColumn.accept(this)); - ++columnId; - } - } - return selectListRequiresCasts; - } - - private boolean nodeRequiresCast(final SqlNode node) throws AdapterException { - if (node.getType() == SqlNodeType.COLUMN) { - return checkIfColumnRequiresCast((SqlColumn) node); - } else { - return false; - } - } - - private boolean checkIfColumnRequiresCast(final SqlColumn node) throws AdapterException { - final String typeName = getTypeNameFromColumn(node); - if (typeName.equals("NUMBER")) { - if (node.getMetadata().getType().getExaDataType() == DataType.ExaDataType.VARCHAR) { - return true; - } else { - return checkIfNeedToCastNumberToDecimal(node); - } - } else { - for (final String typeRequiringCast : TYPE_NAMES_REQUIRING_CAST) { - if (typeName.startsWith(typeRequiringCast)) { - return true; - } - } - } - return false; - } - - /** - * This method determines if a NUMBER column needs to be casted to the DECIMAL type specified in the - * oracle_cast_number_to_decimal_with_precision_and_scale property. This is done by checking if the target type is - * the type specified in the property, assuming that this type was set according to the property. This method is not - * exact and will also add CASTs to columns that have the exact same type as specified in the property. - * - * @param column a NUMBER column - * @return true if a cast is necessary for the NUMBER column - */ - private boolean checkIfNeedToCastNumberToDecimal(final SqlColumn column) { - final AbstractSqlDialect dialect = (AbstractSqlDialect) getDialect(); - final DataType columnType = column.getMetadata().getType(); - final DataType castNumberToDecimalType = ((OracleSqlDialect) dialect).getOracleNumberTargetType(); - return (columnType.getPrecision() == castNumberToDecimalType.getPrecision()) - && (columnType.getScale() == castNumberToDecimalType.getScale()); - } - - private void addColumnAliases(final List selectListElements) { - for (int i = 0; i < selectListElements.size(); i++) { - selectListElements.set(i, selectListElements.get(i) + " AS c" + i); - } - } - - // Limit is realized via a {@code ROWNUM} filter in Oracle (< 12c) Oracle 12c introduced nice syntax for limit and - // offset - // functionality: "OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY" - @Override - public String visit(final SqlLimit limit) { - return ""; - } - - @Override - public String visit(final SqlPredicateLikeRegexp predicate) throws AdapterException { - return "REGEXP_LIKE(" + predicate.getLeft().accept(this) + ", " + predicate.getPattern().accept(this) + ")"; - } - - @Override - public String visit(final SqlColumn column) throws AdapterException { - return getColumnProjectionString(column, super.visit(column)); - } - - private String getColumnProjectionString(final SqlColumn column, final String projectionString) - throws AdapterException { - final boolean isDirectlyInSelectList = (column.hasParent() - && (column.getParent().getType() == SqlNodeType.SELECT_LIST)); - if (!isDirectlyInSelectList) { - return projectionString; - } else { - return getProjectionString(column, projectionString); - } - } - - private String getProjectionString(final SqlColumn column, final String projectionString) throws AdapterException { - final AbstractSqlDialect dialect = (AbstractSqlDialect) getDialect(); - final String typeName = getTypeNameFromColumn(column); - if (typeName.startsWith("INTERVAL") || typeName.equals("BINARY_FLOAT") || typeName.equals("BINARY_DOUBLE")) { - return createToChar(projectionString); - } else if (typeName.startsWith("TIMESTAMP") - && (((OracleSqlDialect) dialect).getImportType() == ImportType.JDBC)) { - return "TO_TIMESTAMP(TO_CHAR(" + projectionString + ", " + TIMESTAMP_FORMAT + "), " + TIMESTAMP_FORMAT - + ")"; - } else if (typeName.equals("NUMBER")) { - return getNumberProjectionString(column, projectionString, (OracleSqlDialect) dialect); - } else { - return projectionString; - } - } - - public String createToChar(final String operand) { - return "TO_CHAR(" + operand + ")"; - } - - private String getNumberProjectionString(final SqlColumn column, final String projectionString, - final OracleSqlDialect dialect) { - if (column.getMetadata().getType().getExaDataType() == DataType.ExaDataType.VARCHAR) { - return createToChar(projectionString); - } else { - if (checkIfNeedToCastNumberToDecimal(column)) { - final DataType castNumberToDecimalType = dialect.getOracleNumberTargetType(); - return cast(projectionString, "DECIMAL(" + castNumberToDecimalType.getPrecision() + "," - + castNumberToDecimalType.getScale() + ")"); - } else { - return projectionString; - } - } - } - - private String cast(final String value, final String as) { - return "CAST(" + value + " AS " + as + ")"; - } - - @Override - public String visit(final SqlLiteralExactnumeric literal) { - final String literalString = literal.getValue().toString(); - return getLiteralString(literalString, literal.hasParent(), literal.getParent()); - } - - private String getLiteralString(final String literalString, final boolean b, final SqlNode parent) { - final boolean isDirectlyInSelectList = (b && (parent.getType() == SqlNodeType.SELECT_LIST)); - if (isDirectlyInSelectList) { - return createToChar(literalString); - } - return literalString; - } - - @Override - public String visit(final SqlLiteralDouble literal) { - final String literalString = Double.toString(literal.getValue()); - return getLiteralString(literalString, literal.hasParent(), literal.getParent()); - } - - @Override - public String visit(final SqlFunctionAggregateGroupConcat function) throws AdapterException { - final StringBuilder builder = new StringBuilder(); - builder.append("LISTAGG"); - builder.append("("); - final String expression = function.getArgument().accept(this); - builder.append(expression); - builder.append(", "); - final String separator = function.hasSeparator() ? function.getSeparator().accept(this) : "','"; - builder.append(separator); - builder.append(") WITHIN GROUP(ORDER BY "); - if (function.hasOrderBy()) { - builder.append(getOrderByString(function)); - } else { - builder.append(expression); - } - builder.append(")"); - return builder.toString(); - } - - private String getOrderByString(final SqlFunctionAggregateGroupConcat function) throws AdapterException { - final StringBuilder builder = new StringBuilder(); - 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).equals(false)) { - builder.append(" DESC"); - } - if (function.getOrderBy().nullsLast().get(i).equals(false)) { - builder.append(" NULLS FIRST"); - } - } - return builder.toString(); - } - - @Override - public String visit(final SqlFunctionAggregate function) throws AdapterException { - final boolean isDirectlyInSelectList = (function.hasParent() - && (function.getParent().getType() == SqlNodeType.SELECT_LIST)); - if (isDirectlyInSelectList && this.aggregateFunctionsCast.contains(function.getFunction())) { - // Cast to FLOAT because result set metadata has precision = 0, scale = 0 - return cast(super.visit(function), "FLOAT"); - } - return super.visit(function); - } - - @Override - public String visit(final SqlFunctionScalar function) throws AdapterException { - String sql = super.visit(function); - switch (function.getFunction()) { - case LOCATE: - sql = getLocate(function); - break; - case TRIM: - sql = getTrim(function); - break; - case ADD_DAYS: - case ADD_HOURS: - case ADD_MINUTES: - case ADD_SECONDS: - case ADD_WEEKS: - case ADD_YEARS: - sql = getTimeOrDate(function); - break; - case CURRENT_DATE: - sql = "CURRENT_DATE"; - break; - case CURRENT_TIMESTAMP: - sql = "CURRENT_TIMESTAMP"; - break; - case DBTIMEZONE: - sql = "DBTIMEZONE"; - break; - case LOCALTIMESTAMP: - sql = "LOCALTIMESTAMP"; - break; - case SESSIONTIMEZONE: - sql = "SESSIONTIMEZONE"; - break; - case SYSDATE: - sql = "TO_DATE(SYSDATE)"; - break; - case SYSTIMESTAMP: - sql = "SYSTIMESTAMP"; - break; - case BIT_AND: - sql = sql.replaceFirst("^BIT_AND", "BITAND"); - break; - case BIT_TO_NUM: - sql = sql.replaceFirst("^BIT_TO_NUM", "BIN_TO_NUM"); - break; - case NULLIFZERO: - sql = getSqlFunctionScalar(function, "NULLIF(", ", 0)"); - break; - case ZEROIFNULL: - sql = getSqlFunctionScalar(function, "NVL(", ", 0)"); - break; - case DIV: - sql = getDiv(function); - break; - case COT: - sql = getSqlFunctionScalar(function, "(1 / TAN(", "))"); - break; - case DEGREES: - sql = getSqlFunctionScalar(function, "((", ") * 180 / ACOS(-1))"); - break; - case RADIANS: - sql = getSqlFunctionScalar(function, "((", ") * ACOS(-1) / 180)"); - break; - case REPEAT: - sql = getRepeat(function); - break; - case REVERSE: - sql = getSqlFunctionScalar(function, "REVERSE(TO_CHAR(", "))"); - break; - default: - break; - } - final boolean isDirectlyInSelectList = (function.hasParent() - && (function.getParent().getType() == SqlNodeType.SELECT_LIST)); - if (isDirectlyInSelectList && this.scalarFunctionsCast.contains(function.getFunction())) { - // Cast to FLOAT because result set metadata has precision = 0, scale = 0 - sql = cast(sql, "FLOAT"); - } - return sql; - } - - private String getTrim(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("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(); - } - - private String getLocate(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("INSTR("); - builder.append(argumentsSql.get(1)); - builder.append(", "); - builder.append(argumentsSql.get(0)); - if (argumentsSql.size() > 2) { - builder.append(", "); - builder.append(argumentsSql.get(2)); - } - builder.append(")"); - return builder.toString(); - } - - private String getTimeOrDate(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("("); - builder.append(argumentsSql.get(0)); - builder.append(" + INTERVAL '"); - 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("DAY"); - break; - case ADD_HOURS: - builder.append("HOUR"); - break; - case ADD_MINUTES: - builder.append("MINUTE"); - break; - case ADD_SECONDS: - builder.append("SECOND"); - break; - case ADD_YEARS: - builder.append("YEAR"); - break; - default: - break; - } - builder.append(")"); - return builder.toString(); - } - - private String getSqlFunctionScalar(final SqlFunctionScalar function, final String s, final String s2) - 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(s); - builder.append(argumentsSql.get(0)); - builder.append(s2); - return builder.toString(); - } - - private String getRepeat(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("RPAD(TO_CHAR("); - builder.append(argumentsSql.get(0)); - builder.append("), LENGTH("); - builder.append(argumentsSql.get(0)); - builder.append(") * ROUND("); - builder.append(argumentsSql.get(1)); - builder.append("), "); - builder.append(argumentsSql.get(0)); - builder.append(")"); - return builder.toString(); - } - - 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(" / "); - builder.append(argumentsSql.get(1)); - builder.append(") AS NUMBER(36, 0))"); - return builder.toString(); - } -} diff --git a/src/main/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReader.java b/src/main/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReader.java deleted file mode 100644 index de006854b..000000000 --- a/src/main/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReader.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import java.sql.Connection; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.IdentifierConverter; -import com.exasol.adapter.jdbc.BaseTableMetadataReader; -import com.exasol.adapter.jdbc.ColumnMetadataReader; - -/** - * This class implements a reader for Oracle database metadata. - */ -public class OracleTableMetadataReader extends BaseTableMetadataReader { - private static final String TRASH_BIN_TABLE_NAME_PREFIX = "BIN$"; - - /** - * Create a new {@link OracleTableMetadataReader} instance. - * - * @param connection connection to the remote data source - * @param columnMetadataReader reader to be used to map the metadata of the tables columns - * @param properties user-defined adapter properties - * @param identifierConverter converter between source and Exasol identifiers - */ - public OracleTableMetadataReader(final Connection connection, final ColumnMetadataReader columnMetadataReader, - final AdapterProperties properties, final IdentifierConverter identifierConverter) { - super(connection, columnMetadataReader, properties, identifierConverter); - } - - @Override - public boolean isTableIncludedByMapping(final String tableName) { - return !isTableInTrashBin(tableName); - } - - private boolean isTableInTrashBin(final String tableName) { - return tableName.startsWith(TRASH_BIN_TABLE_NAME_PREFIX); - } -} \ No newline at end of file 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 1753f92aa..e984e3fd0 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 @@ -4,7 +4,6 @@ com.exasol.adapter.dialects.db2.DB2SqlDialectFactory com.exasol.adapter.dialects.generic.GenericSqlDialectFactory com.exasol.adapter.dialects.hive.HiveSqlDialectFactory com.exasol.adapter.dialects.impala.ImpalaSqlDialectFactory -com.exasol.adapter.dialects.oracle.OracleSqlDialectFactory com.exasol.adapter.dialects.redshift.RedshiftSqlDialectFactory com.exasol.adapter.dialects.saphana.SapHanaSqlDialectFactory com.exasol.adapter.dialects.sqlserver.SqlServerSqlDialectFactory diff --git a/src/test/java/com/exasol/adapter/dialects/IntegrationTestConstants.java b/src/test/java/com/exasol/adapter/dialects/IntegrationTestConstants.java index 8e3d028b7..91c2fedfd 100644 --- a/src/test/java/com/exasol/adapter/dialects/IntegrationTestConstants.java +++ b/src/test/java/com/exasol/adapter/dialects/IntegrationTestConstants.java @@ -3,7 +3,7 @@ import java.nio.file.Path; public final class IntegrationTestConstants { - public static final String VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION = "virtual-schema-dist-8.0.0-bundle-5.0.0.jar"; + public static final String VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION = "virtual-schema-dist-8.0.0-bundle-6.0.0.jar"; public static final String EXASOL_DOCKER_IMAGE_REFERENCE = "exasol/docker-db:6.2.11-d1"; public static final Path PATH_TO_VIRTUAL_SCHEMAS_JAR = Path.of("target", VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION); public static final String SCHEMA_EXASOL = "SCHEMA_EXASOL"; diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReaderTest.java deleted file mode 100644 index 7e40fe2c7..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleColumnMetadataReaderTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.metadata.DataType.createMaximumSizeVarChar; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; - -import java.sql.Types; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.BaseIdentifierConverter; -import com.exasol.adapter.jdbc.JdbcTypeDescription; -import com.exasol.adapter.metadata.DataType; - -class OracleColumnMetadataReaderTest { - private OracleColumnMetadataReader columnMetadataReader; - - @BeforeEach - void beforeEach() { - this.columnMetadataReader = createDefaultOracleColumnMetadataReader(); - } - - protected OracleColumnMetadataReader createDefaultOracleColumnMetadataReader() { - return new OracleColumnMetadataReader(null, AdapterProperties.emptyProperties(), - BaseIdentifierConverter.createDefault()); - } - - private JdbcTypeDescription createTypeDescriptionForNumeric(final int precision, final int scale) { - final int octetLength = 10; - return new JdbcTypeDescription(Types.NUMERIC, scale, precision, octetLength, "NUMERIC"); - } - - @Test - void testMapColumnTypeWithMagicScale() { - final int precision = 10; - final int scale = OracleColumnMetadataReader.ORACLE_MAGIC_NUMBER_SCALE; - final JdbcTypeDescription typeDescription = createTypeDescriptionForNumeric(precision, scale); - assertThat(this.columnMetadataReader.mapJdbcType(typeDescription), - equalTo(createMaximumSizeVarChar(DataType.ExaCharset.UTF8))); - } - - @Test - void testMapNumericColumnTypeWithMaximumDecimalPrecision() { - final int precision = DataType.MAX_EXASOL_DECIMAL_PRECISION; - final int scale = 0; - final JdbcTypeDescription typeDescription = createTypeDescriptionForNumeric(precision, scale); - assertThat(this.columnMetadataReader.mapJdbcType(typeDescription), - equalTo(DataType.createDecimal(precision, scale))); - } - - @Test - void testMapColumnTypeWithZeroPrecision() { - final int precision = 0; - final int scale = 0; - final JdbcTypeDescription typeDescription = createTypeDescriptionForNumeric(precision, scale); - assertThat(this.columnMetadataReader.mapJdbcType(typeDescription), - equalTo(DataType.createDecimal(DataType.MAX_EXASOL_DECIMAL_PRECISION, scale))); - } - - private OracleColumnMetadataReader createParameterizedColumnMetadataReader( - final Map rawProperties) { - return new OracleColumnMetadataReader(null, new AdapterProperties(rawProperties), - BaseIdentifierConverter.createDefault()); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilderTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilderTest.java deleted file mode 100644 index 61bd9d187..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleConnectionDefinitionBuilderTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.AdapterProperties.CONNECTION_NAME_PROPERTY; -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_CONNECTION_NAME_PROPERTY; -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_IMPORT_PROPERTY; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import com.exasol.adapter.AdapterProperties; - -class OracleConnectionDefinitionBuilderTest { - private final OracleConnectionDefinitionBuilder connectionDefinitionBuilder = new OracleConnectionDefinitionBuilder(); - - @Test - void testBuildConnectionDefinition() { - final AdapterProperties properties = new AdapterProperties(Map.of( // - ORACLE_IMPORT_PROPERTY, "true", // - ORACLE_CONNECTION_NAME_PROPERTY, "ora_connection", // - CONNECTION_NAME_PROPERTY, "jdbc_connection")); - assertThat(connectionDefinitionBuilder.buildConnectionDefinition(properties, null), - containsString("AT ora_connection")); - } - - @Test - void testBuildConnectionDefinitionMissingPropertyException() { - final AdapterProperties properties = new AdapterProperties(Map.of( // - ORACLE_IMPORT_PROPERTY, "true", // - CONNECTION_NAME_PROPERTY, "ora_connection")); - final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> connectionDefinitionBuilder.buildConnectionDefinition(properties, null)); - assertThat(exception.getMessage(), containsString("If you enable IMPORT FROM ORA with property")); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleIdentifierTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleIdentifierTest.java deleted file mode 100644 index 6318d6f0a..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleIdentifierTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import nl.jqno.equalsverifier.EqualsVerifier; - -class OracleIdentifierTest { - @ParameterizedTest - @ValueSource(strings = { "_myunderscoretable", "123columnone", "テスト", "таблица" }) - void testCreateValidIdentifier(final String identifier) { - assertDoesNotThrow(() -> OracleIdentifier.of(identifier)); - } - - @ParameterizedTest - @ValueSource(strings = { "\"testtable\"", "test\"table" }) - void testCreateInvalidIdentifier(final String identifier) { - assertThrows(AssertionError.class, () -> OracleIdentifier.of(identifier)); - } - - @Test - void testEqualsAndHashContract() { - EqualsVerifier.simple().forClass(OracleIdentifier.class).verify(); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleMetadataReaderTest.java deleted file mode 100644 index eba298428..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleMetadataReaderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.MatcherAssert.assertThat; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.exasol.adapter.AdapterProperties; - -class OracleMetadataReaderTest { - private OracleMetadataReader reader; - - @BeforeEach - void beforeEach() { - this.reader = new OracleMetadataReader(null, AdapterProperties.emptyProperties()); - } - - @Test - void testGetTableMetadataReader() { - assertThat(this.reader.getTableMetadataReader(), instanceOf(OracleTableMetadataReader.class)); - } - - @Test - void testGetColumnMetadataReader() { - assertThat(this.reader.getColumnMetadataReader(), instanceOf(OracleColumnMetadataReader.class)); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriterTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriterTest.java deleted file mode 100644 index df176446e..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleQueryRewriterTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_CONNECTION_NAME_PROPERTY; -import static com.exasol.adapter.dialects.oracle.OracleProperties.ORACLE_IMPORT_PROPERTY; -import static com.exasol.reflect.ReflectionUtils.getMethodReturnViaReflection; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; - -import java.sql.SQLException; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -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.adapter.AdapterException; -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.*; -import com.exasol.adapter.jdbc.ConnectionFactory; -import com.exasol.adapter.sql.TestSqlStatementFactory; - -@ExtendWith(MockitoExtension.class) -public class OracleQueryRewriterTest extends AbstractQueryRewriterTestBase { - @BeforeEach - void beforeEach() { - this.statement = TestSqlStatementFactory.createSelectOneFromDual(); - } - - @Test - void testRewriteToImportFromOraWithConnectionDetailsInProperties( - @Mock final ConnectionFactory connectionFactoryMock) throws AdapterException, SQLException { - final AdapterProperties properties = new AdapterProperties(Map.of( // - ORACLE_IMPORT_PROPERTY, "true", // - ORACLE_CONNECTION_NAME_PROPERTY, "ora_connection")); - final SqlDialectFactory dialectFactory = new OracleSqlDialectFactory(); - final SqlDialect dialect = dialectFactory.createSqlDialect(connectionFactoryMock, properties); - final QueryRewriter queryRewriter = new OracleQueryRewriter(dialect, null); - assertThat(queryRewriter.rewrite(this.statement, EXA_METADATA, properties), - equalTo("IMPORT FROM ORA AT ora_connection STATEMENT 'SELECT TO_CHAR(1) FROM \"DUAL\"'")); - } - - @Test - void testConnectionDefinitionBuilderClass() { - final SqlDialect dialect = new OracleSqlDialect(null, AdapterProperties.emptyProperties()); - final QueryRewriter queryRewriter = new OracleQueryRewriter(dialect, null); - assertThat(getMethodReturnViaReflection(queryRewriter, "createConnectionDefinitionBuilder"), - instanceOf(OracleConnectionDefinitionBuilder.class)); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactoryTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactoryTest.java deleted file mode 100644 index 506353a17..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectFactoryTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.MatcherAssert.assertThat; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.exasol.adapter.AdapterProperties; - -public class OracleSqlDialectFactoryTest { - private OracleSqlDialectFactory factory; - - @BeforeEach - void beforeEach() { - this.factory = new OracleSqlDialectFactory(); - } - - @Test - void testGetName() { - assertThat(this.factory.getSqlDialectName(), equalTo("ORACLE")); - } - - @Test - void testCreateDialect() { - assertThat(this.factory.createSqlDialect(null, AdapterProperties.emptyProperties()), - instanceOf(OracleSqlDialect.class)); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectIT.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectIT.java deleted file mode 100644 index fd161ad90..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectIT.java +++ /dev/null @@ -1,885 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.dialects.IntegrationTestConstants.*; -import static com.exasol.dbbuilder.dialects.exasol.AdapterScript.Language.JAVA; -import static com.exasol.matcher.ResultSetMatcher.matchesResultSet; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.math.BigDecimal; -import java.nio.file.Path; -import java.sql.*; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.*; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.OracleContainer; -import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import com.exasol.adapter.dialects.AbstractIntegrationTest; -import com.exasol.bucketfs.Bucket; -import com.exasol.bucketfs.BucketAccessException; -import com.exasol.containers.ExasolContainer; -import com.exasol.containers.ExasolContainerConstants; -import com.exasol.dbbuilder.dialects.exasol.AdapterScript; -import com.exasol.dbbuilder.dialects.exasol.ConnectionDefinition; -import com.exasol.dbbuilder.dialects.exasol.ExasolObjectFactory; -import com.exasol.dbbuilder.dialects.exasol.ExasolSchema; - -/** - * How to run `OracleSqlDialectIT`: See the documentation > exasolContainer = new ExasolContainer<>( - EXASOL_DOCKER_IMAGE_REFERENCE) // - .withLogConsumer(new Slf4jLogConsumer(LOGGER)); - @Container - private static final OracleContainer oracleContainer = new OracleContainer(ORACLE_CONTAINER_NAME); - private static Statement statementExasol; - - @BeforeAll - static void beforeAll() throws InterruptedException, BucketAccessException, TimeoutException, SQLException { - final String driverName = getPropertyFromFile(RESOURCES_FOLDER_DIALECT_NAME, "driver.name"); - uploadDriverToBucket(driverName, RESOURCES_FOLDER_DIALECT_NAME, exasolContainer.getDefaultBucket()); - uploadVsJarToBucket(exasolContainer.getDefaultBucket()); - uploadInstantClientToBucket(); - final Connection exasolConnection = exasolContainer.createConnectionForUser(exasolContainer.getUsername(), - exasolContainer.getPassword()); - statementExasol = exasolConnection.createStatement(); - final Statement statementOracle = oracleContainer.createConnection("").createStatement(); - createOracleUser(statementOracle); - createOracleTableAllDataTypes(statementOracle); - createOracleTableNumberHandling(statementOracle); - createOracleTableTimestamps(statementOracle); - createTestTablesForJoinTests(oracleContainer.createConnection(""), SCHEMA_ORACLE); - final Integer mappedPort = oracleContainer.getMappedPort(ORACLE_PORT); - final String oracleUsername = oracleContainer.getUsername(); - final String oraclePassword = oracleContainer.getPassword(); - final ExasolObjectFactory exasolFactory = new ExasolObjectFactory(exasolContainer.createConnection("")); - final ExasolSchema schema = exasolFactory.createSchema(SCHEMA_EXASOL); - final AdapterScript adapterScript = createAdapterScript(driverName, schema); - final String jdbcConnectionString = "jdbc:oracle:thin:@//" + DOCKER_IP_ADDRESS + ":" + mappedPort + "/xe"; - final ConnectionDefinition jdbcConnectionDefinition = exasolFactory - .createConnectionDefinition(JDBC_CONNECTION_NAME, jdbcConnectionString, oracleUsername, oraclePassword); - createOraConnection(exasolFactory, mappedPort, oracleUsername, oraclePassword); - exasolFactory.createVirtualSchemaBuilder(VIRTUAL_SCHEMA_JDBC).adapterScript(adapterScript) - .connectionDefinition(jdbcConnectionDefinition).dialectName("ORACLE") - .properties(Map.of("SCHEMA_NAME", SCHEMA_ORACLE)).build(); - exasolFactory.createVirtualSchemaBuilder(VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL).adapterScript(adapterScript) - .connectionDefinition(jdbcConnectionDefinition).dialectName("ORACLE") - .properties(Map.of("SCHEMA_NAME", SCHEMA_ORACLE)).properties(Map.of("SCHEMA_NAME", SCHEMA_ORACLE, - "oracle_cast_number_to_decimal_with_precision_and_scale", "36,1")) - .build(); - exasolFactory.createVirtualSchemaBuilder(VIRTUAL_SCHEMA_ORA).adapterScript(adapterScript) - .connectionDefinition(jdbcConnectionDefinition).dialectName("ORACLE").properties(Map.of("SCHEMA_NAME", - SCHEMA_ORACLE, "IMPORT_FROM_ORA", "true", "ORA_CONNECTION_NAME", ORA_CONNECTION_NAME)) - .build(); - exasolFactory.createVirtualSchemaBuilder(VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL).adapterScript(adapterScript) - .connectionDefinition(jdbcConnectionDefinition).dialectName("ORACLE") - .properties(Map.of("SCHEMA_NAME", SCHEMA_ORACLE, "IMPORT_FROM_ORA", "true", "ORA_CONNECTION_NAME", - ORA_CONNECTION_NAME, "oracle_cast_number_to_decimal_with_precision_and_scale", "36,1")) - .build(); - } - - private static void uploadInstantClientToBucket() - throws InterruptedException, BucketAccessException, TimeoutException { - final Bucket bucket = exasolContainer.getDefaultBucket(); - final String instantClientName = getPropertyFromFile(RESOURCES_FOLDER_DIALECT_NAME, "instant.client.name"); - final String instantClientPath = getPropertyFromFile(RESOURCES_FOLDER_DIALECT_NAME, "instant.client.path"); - bucket.uploadFile(Path.of(instantClientPath, instantClientName), "drivers/oracle/" + instantClientName); - } - - private static void createOracleUser(final Statement statementOracle) throws SQLException { - final String username = SCHEMA_ORACLE; - final String password = SCHEMA_ORACLE; - statementOracle.execute("CREATE USER " + username + " IDENTIFIED BY " + password); - statementOracle.execute("GRANT CONNECT TO " + username); - statementOracle.execute("GRANT CREATE SESSION TO " + username); - statementOracle.execute("GRANT UNLIMITED TABLESPACE TO " + username); - } - - private static void createOracleTableAllDataTypes(final Statement statementOracle) throws SQLException { - final String qualifiedTableName = SCHEMA_ORACLE + "." + TABLE_ORACLE_ALL_DATA_TYPES; - statementOracle.execute("CREATE TABLE " + qualifiedTableName + " (" // - + "c1 char(50), " // - + "c2 nchar(50), " // - + "c3 varchar2(50), " // - + "c4 nvarchar2(50), " // - + "c5 number, " // - + "c_number36 number(36), " // - + "c6 number(38), " // - + "c7 number(10,5), " // - + "c_binfloat binary_float, " // - + "c_bindouble binary_double, " // - + "c10 date, " // - + "c11 timestamp(3), " // - + "c12 timestamp, " // - + "c13 timestamp(9), " // - + "c14 timestamp with time zone, " // - + "c15 timestamp with local time zone, " // - + "c16 interval year to month, " // - + "c17 interval day to second, " // - + "c18 blob, " // - + "c19 clob, " // - + "c20 nclob, " // - + "c_float float, " // - + "c_float126 float(126), " // - + "c_long long " // - + ")"); - statementOracle.execute("INSERT INTO " + qualifiedTableName + " VALUES (" // - + "'aaaaaaaaaaaaaaaaaaaa', " // - + "'bbbbbbbbbbbbbbbbbbbb', " // - + "'cccccccccccccccccccc', " // - + "'dddddddddddddddddddd', " // - + "123456789012345678901234567890123456, " // C5 - + "123456789012345678901234567890123456, " // c_number36 - + "12345678901234567890123456789012345678, " // C6 - + "12345.12345, " // C7 - + "1234.1241723, " // C_BINFLOAT - + "1234987.120871234, " // C_BINDOUBLE - + "TO_DATE('2016-08-19', 'YYYY-MM-DD'), " // C10 - + "TO_TIMESTAMP('2013-03-11 17:30:15.123', 'YYYY-MM-DD HH24:MI:SS.FF'), " // C11 - + "TO_TIMESTAMP('2013-03-11 17:30:15.123456', 'YYYY-MM-DD HH24:MI:SS.FF'), " // C12 - + "TO_TIMESTAMP('2013-03-11 17:30:15.123456789', 'YYYY-MM-DD HH24:MI:SS.FF'), " // C13 - + "TO_TIMESTAMP_TZ('2016-08-19 11:28:05 -08:00', 'YYYY-MM-DD HH24:MI:SS TZH:TZM'), " // C14 - + "TO_TIMESTAMP_TZ('2018-04-30 10:00:05 -08:00', 'YYYY-MM-DD HH24:MI:SS TZH:TZM'), " // C15 - + "'54-2', " // C16 - + "'1 11:12:10.123', " // C17 - + "'0102030405060708090a0b0c0d0e0f', " // C18 - + "'0987asdlfkjq2222qawsf;lkja09ed8q2w;43lkrjasdf09uqaw43lkjra0-98sf[iqjw4,mfas[dpiuj[qa09w44', " // C19 - + "'0987asdlfkjq2222qawsf;lkja09ed8q2w;43lkrjasdf09uqaw43lkjra0-98sf[iqjw4,mfas[dpiuj[qa09w44', " // C20 - + "12345.01982348239, " // c_float - + "12345678.01234567901234567890123456789, " // c_float126 - + "'test long 123' " // long - + ")"); - statementOracle.execute("INSERT INTO " + qualifiedTableName + "(c3, c5, c7, c_binfloat, c17) VALUES (" // - + "'cccccccccccccccccccc', " // C3 - + "1234567890.123456789, " // C5 - + "12355.12345, " // C7 - + "123.12345687987654321, " // C_BINFLOAT - + "'2 02:03:04.123456' " // C17 - + ")"); - } - - private static void createOracleTableNumberHandling(final Statement statementOracle) throws SQLException { - final String qualifiedTableName = SCHEMA_ORACLE + "." + TABLE_ORACLE_NUMBER_HANDLING; - statementOracle.execute("CREATE TABLE " + qualifiedTableName + " (" // - + "a number, " // - + "b number(38, 10), " // - + "c number(36,2) " // - + ")"); - statementOracle.execute("INSERT INTO " + qualifiedTableName + " VALUES (" // - + "1234567890123456789012345678901234.56, " // - + "1234567890123456789012345678.9012345678, " // - + "1234567890123456789012345678901234.56 " // - + ")"); - } - - private static void createOracleTableTimestamps(final Statement statementOracle) throws SQLException { - final String qualifiedTableName = SCHEMA_ORACLE + "." + TABLE_ORACLE_TIMESTAMPS; - statementOracle.execute("CREATE TABLE " + qualifiedTableName + " (" // - + "a timestamp, " // - + "b timestamp with local time zone, " // - + "c timestamp with time zone " // - + ")"); - statementOracle.execute("INSERT INTO " + qualifiedTableName + " VALUES (" // - + "timestamp '2018-01-01 11:00:00', " // - + "timestamp '2018-01-01 11:00:00 +01:00', " // - + "timestamp '2018-01-01 11:00:00 +01:00' " // - + ")"); - } - - private static AdapterScript createAdapterScript(final String driverName, final ExasolSchema schema) { - final String content = "%scriptclass com.exasol.adapter.RequestDispatcher;\n" // - + "%jar /buckets/bfsdefault/default/" + VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION + ";\n" // - + "%jar /buckets/bfsdefault/default/drivers/jdbc/" + driverName + ";\n"; - return schema.createAdapterScript(ADAPTER_SCRIPT_EXASOL, JAVA, content); - } - - private static ConnectionDefinition createOraConnection(final ExasolObjectFactory exasolFactory, - final Integer mappedPort, final String oracleUsername, final String oraclePassword) { - final String oraConnectionString = "(DESCRIPTION =" // - + "(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)" // - + "(HOST = " + DOCKER_IP_ADDRESS + " )" // - + "(PORT = " + mappedPort + ")))" // - + "(CONNECT_DATA = (SERVER = DEDICATED)" // - + "(SERVICE_NAME = xe)))"; - return exasolFactory.createConnectionDefinition(ORA_CONNECTION_NAME, oraConnectionString, oracleUsername, - oraclePassword); - } - - @Override - protected Connection getExasolConnection() throws SQLException { - return exasolContainer.createConnection(""); - } - - @Test - void testCountAll() throws SQLException { - final String qualifiedTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_NUMBER_HANDLING; - final String query = "SELECT COUNT(*) FROM " + qualifiedTableName; - final ResultSet expected = getExpectedResultSet(List.of("x DECIMAL(36,0)"), // - List.of("1")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @Nested - @DisplayName("Number handling test") - class numberHandlingTest { - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testNumberToDecimalThrowsException(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT c5 FROM " + qualifiedTableName; - final SQLException exception = assertThrows(SQLException.class, () -> statementExasol.execute(query)); - assertThat(exception.getMessage(), - containsString("value larger than specified precision allowed for this column")); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testNumber36ToDecimal(final String virtualSchemaName) throws SQLException { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT c_number36 FROM " + qualifiedTableName; - assertAll( - () -> assertExpressionExecutionBigDecimalResult(query, - new BigDecimal("123456789012345678901234567890123456")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C_NUMBER36"), - equalTo("DECIMAL(36,0)"))); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testNumber38ToDecimalThrowsException(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT c6 FROM " + qualifiedTableName; - final SQLException exception = assertThrows(SQLException.class, () -> statementExasol.execute(query)); - assertThat(exception.getMessage(), - containsString("value larger than specified precision allowed for this column")); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testNumber10S5ToDecimal(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C7 FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionBigDecimalResult(query, new BigDecimal("12345.12345")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C7"), equalTo("DECIMAL(10,5)"))); - } - - @Test - void testSelectAllColsNumberFromJDBC() throws SQLException { - final String qualifiedTableNameActual = VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL + "." - + TABLE_ORACLE_NUMBER_HANDLING; - final ResultSet expected = getExpectedResultSet("(A DECIMAL(36,1), B DECIMAL(36,1), C DECIMAL(36,2))", - "(1234567890123456789012345678901234.6, 1234567890123456789012345678.9, 1234567890123456789012345678901234.56)"); - assertThat(statementExasol.executeQuery("SELECT * FROM " + qualifiedTableNameActual), // - matchesResultSet(expected)); - } - - @Test - void testSelectAllColsNumberFromOra() throws SQLException { - final String qualifiedTableNameActual = VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL + "." - + TABLE_ORACLE_NUMBER_HANDLING; - final ResultSet expected = getExpectedResultSet("(A VARCHAR(100), B VARCHAR(100), C VARCHAR(100))", - "('12.3456789012345678901234567890123460E32', '12.3456789012345678901234567890E26', '12.3456789012345678901234567890123456E32')"); - assertThat(statementExasol.executeQuery("SELECT * FROM " + qualifiedTableNameActual), // - matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testNumberDataTypes(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_NUMBER_HANDLING; - assertAll(() -> assertThat(getColumnTypesOfTable(qualifiedTableName, "A"), equalTo("DECIMAL(36,1)")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "B"), equalTo("DECIMAL(36,1)")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C"), equalTo("DECIMAL(36,2)"))); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testSelectOneNumberColumn(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_NUMBER_HANDLING; - assertAll( - () -> assertExpressionExecutionBigDecimalResult("SELECT A FROM " + qualifiedTableName, - new BigDecimal("1234567890123456789012345678901234.6")), - () -> assertExpressionExecutionBigDecimalResult("SELECT B FROM " + qualifiedTableName, - new BigDecimal("1234567890123456789012345678.9")), - () -> assertExpressionExecutionBigDecimalResult("SELECT C FROM " + qualifiedTableName, - new BigDecimal("1234567890123456789012345678901234.56"))); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC_NUMBER_TO_DECIMAL, VIRTUAL_SCHEMA_ORA_NUMBER_TO_DECIMAL }) - void testSelectAllNumberColumnsExplainVirtual(final String virtualSchemaName) throws SQLException { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_NUMBER_HANDLING; - assertExplainVirtual("SELECT * FROM " + qualifiedTableName, - "SELECT CAST(\"A\" AS DECIMAL(36,1)), CAST(\"B\" AS DECIMAL(36,1)), \"C\""); - } - } - - private String getColumnTypesOfTable(final String tableName, final String columnName) throws SQLException { - final ResultSet result = statementExasol.executeQuery("DESCRIBE " + tableName); - while (result.next()) { - if (result.getString("COLUMN_NAME").toUpperCase().equals(columnName)) { - return result.getString("SQL_TYPE").toUpperCase(); - } - } - throw new IllegalArgumentException("Type for column " + columnName + " not found"); - } - - private ResultSet getExpectedResultSet(final String expectedColumnTypes, final String expectedValues) - throws SQLException { - final String qualifiedExpectedTableName = SCHEMA_EXASOL + "." + "EXPECTED"; - statementExasol.execute("CREATE OR REPLACE TABLE " + qualifiedExpectedTableName + expectedColumnTypes); - statementExasol.execute("INSERT INTO " + qualifiedExpectedTableName + " VALUES" + expectedValues); - return statementExasol.executeQuery("SELECT * FROM " + qualifiedExpectedTableName); - } - - private void assertExpressionExecutionBigDecimalResult(final String query, final BigDecimal expectedValue) - throws SQLException { - final ResultSet result = statementExasol.executeQuery(query); - result.next(); - final BigDecimal actualResult = result.getBigDecimal(1); - assertThat(actualResult.stripTrailingZeros(), equalTo(expectedValue)); - } - - private void assertExplainVirtual(final String query, final String expected) throws SQLException { - final ResultSet explainVirtual = statementExasol.executeQuery("EXPLAIN VIRTUAL " + query); - explainVirtual.next(); - final String explainVirtualStringActual = explainVirtual.getString("PUSHDOWN_SQL"); - assertThat(explainVirtualStringActual, containsString(expected)); - } - - @Nested - @DisplayName("Join test") - class JoinTest { - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testInnerJoin(final String virtualSchema) throws SQLException { - final String query = "SELECT * FROM " + virtualSchema + "." + TABLE_JOIN_1 + " a INNER JOIN " - + virtualSchema + "." + TABLE_JOIN_2 + " b ON a.x=b.x"; - final ResultSet expected = getExpectedResultSet( - List.of("x VARCHAR(100)", "y VARCHAR(100)", "a VARCHAR(100)", "b VARCHAR(100)"), // - List.of("'2','bbb', '2','bbb'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testInnerJoinWithProjection(final String virtualSchemaName) throws SQLException { - final String qualifiedJoinTableName1 = virtualSchemaName + "." + TABLE_JOIN_1; - final String qualifiedJoinTableName2 = virtualSchemaName + "." + TABLE_JOIN_2; - final String query = "SELECT b.y || " + qualifiedJoinTableName1 + ".y FROM " + qualifiedJoinTableName1 - + " INNER JOIN " + qualifiedJoinTableName2 + " b ON " + qualifiedJoinTableName1 + ".x=b.x"; - final ResultSet expected = getExpectedResultSet(List.of("y VARCHAR(100)"), // - List.of("'bbbbbb'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testLeftJoin(final String virtualSchemaName) throws SQLException { - final String qualifiedJoinTableName1 = virtualSchemaName + "." + TABLE_JOIN_1; - final String qualifiedJoinTableName2 = virtualSchemaName + "." + TABLE_JOIN_2; - final String query = "SELECT * FROM " + qualifiedJoinTableName1 + " a LEFT OUTER JOIN " - + qualifiedJoinTableName2 + " b ON a.x=b.x ORDER BY a.x"; - final ResultSet expected = getExpectedResultSet( - List.of("x VARCHAR(100)", "y VARCHAR(100)", "a VARCHAR(100)", "b VARCHAR(100)"), // - List.of("'1', 'aaa', null, null", // - "'2', 'bbb', '2', 'bbb'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testRightJoin(final String virtualSchemaName) throws SQLException { - final String qualifiedJoinTableName1 = virtualSchemaName + "." + TABLE_JOIN_1; - final String qualifiedJoinTableName2 = virtualSchemaName + "." + TABLE_JOIN_2; - final String query = "SELECT * FROM " + qualifiedJoinTableName1 + " a RIGHT OUTER JOIN " - + qualifiedJoinTableName2 + " b ON a.x=b.x ORDER BY a.x"; - final ResultSet expected = getExpectedResultSet( - List.of("x VARCHAR(100)", "y VARCHAR(100)", "a VARCHAR(100)", "b VARCHAR(100)"), // - List.of("'2', 'bbb', '2', 'bbb'", // - "null, null, '3', 'ccc'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testFullOuterJoin(final String virtualSchemaName) throws SQLException { - final String qualifiedJoinTableName1 = virtualSchemaName + "." + TABLE_JOIN_1; - final String qualifiedJoinTableName2 = virtualSchemaName + "." + TABLE_JOIN_2; - final String query = "SELECT * FROM " + qualifiedJoinTableName1 + " a FULL OUTER JOIN " - + qualifiedJoinTableName2 + " b ON a.x=b.x ORDER BY a.x"; - final ResultSet expected = getExpectedResultSet( - List.of("x VARCHAR(100)", "y VARCHAR(100)", "a VARCHAR(100)", "b VARCHAR(100)"), // - List.of("1, 'aaa', null, null", // - "'2', 'bbb', '2', 'bbb'", // - "null, null, '3', 'ccc'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testRightJoinWithComplexCondition(final String virtualSchemaName) throws SQLException { - final String qualifiedJoinTableName1 = virtualSchemaName + "." + TABLE_JOIN_1; - final String qualifiedJoinTableName2 = virtualSchemaName + "." + TABLE_JOIN_2; - final String query = "SELECT * FROM " + qualifiedJoinTableName1 + " a RIGHT OUTER JOIN " - + qualifiedJoinTableName2 + " b ON a.x||a.y=b.x||b.y ORDER BY a.x"; - final ResultSet expected = getExpectedResultSet( - List.of("x VARCHAR(100)", "y VARCHAR(100)", "a VARCHAR(100)", "b VARCHAR(100)"), // - List.of("'2', 'bbb', '2', 'bbb'", // - "null, null, '3', 'ccc'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testFullOuterJoinWithComplexCondition(final String virtualSchemaName) throws SQLException { - final String qualifiedJoinTableName1 = virtualSchemaName + "." + TABLE_JOIN_1; - final String qualifiedJoinTableName2 = virtualSchemaName + "." + TABLE_JOIN_2; - final String query = "SELECT * FROM " + qualifiedJoinTableName1 + " a FULL OUTER JOIN " - + qualifiedJoinTableName2 + " b ON a.x-b.x=0 ORDER BY a.x"; - final ResultSet expected = getExpectedResultSet( - List.of("x VARCHAR(100)", "y VARCHAR(100)", "a VARCHAR(100)", "b VARCHAR(100)"), // - List.of("1, 'aaa', null, null", // - "'2', 'bbb', '2', 'bbb'", // - "null, null, '3', 'ccc'")); - assertThat(getActualResultSet(query), matchesResultSet(expected)); - } - } - - @Nested - @DisplayName("Datatype tests") - class DatatypeTest { - @ParameterizedTest - @CsvSource(value = { "VIRTUAL_SCHEMA_JDBC, 12346.12345", // - "VIRTUAL_SCHEMA_ORA, 01.2346123450E4" }) - void testSelectExpression(final String virtualSchemaName, final String expectedColumnValue) { - final String qualifiedTableNameActual = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C7 + 1 FROM " + qualifiedTableNameActual + " ORDER BY 1"; - final String expectedExplainVirtual = "SELECT CAST((\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C7\" + 1) AS FLOAT) FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\" ORDER BY (\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\" + 1)"; - assertAll(() -> assertExpressionExecutionStringResult(query, expectedColumnValue), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - private void assertExpressionExecutionStringResult(final String query, final String expected) - throws SQLException { - final ResultSet result = statementExasol.executeQuery(query); - result.next(); - final String actual = result.getString(1); - MatcherAssert.assertThat(actual, containsString(expected)); - } - - @ParameterizedTest - @CsvSource(value = { "VIRTUAL_SCHEMA_JDBC, 12355.12345", // - "VIRTUAL_SCHEMA_ORA, 01.2355123450E4" }) - void testFilterExpression(final String virtualSchemaName, final String expectedColumnValue) { - final String qualifiedTableNameActual = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C7 FROM " + qualifiedTableNameActual + " WHERE C7 > 12346"; - final String expectedExplainVirtual = "SELECT \"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\" FROM \"" - + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\" WHERE 12346 < \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\""; - assertAll(() -> assertExpressionExecutionStringResult(query, expectedColumnValue), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @ParameterizedTest - @CsvSource(value = { "VIRTUAL_SCHEMA_JDBC, 12345.12345", // - "VIRTUAL_SCHEMA_ORA, 01.2345123450E4" }) - void testAggregateSingleGroup(final String virtualSchemaName, final String expectedColumnValue) { - final String qualifiedTableNameActual = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT min(C7) FROM " + qualifiedTableNameActual; - final String expectedExplainVirtual = "SELECT CAST(MIN(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C7\") AS FLOAT) FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\""; - assertAll(() -> assertExpressionExecutionStringResult(query, expectedColumnValue), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testAggregateGroupByColumnJdbc() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C5, min(C7) FROM " + qualifiedActualTableName + " GROUP BY C5 ORDER BY 1 DESC"; - final ResultSet expected = getExpectedResultSet("(A VARCHAR(100), B VARCHAR(100))", - "('123456789012345678901234567890123456', '12345.12345')," // - + "('1234567890.123456789', '12355.12345')"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT TO_CHAR(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C5\"), CAST(MIN(\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\") AS FLOAT) FROM \"" - + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\" GROUP BY \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C5\" ORDER BY \"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C5\" DESC"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testAggregateGroupByExpressionOra() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_ORA + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C5 + 1, min(C7) FROM " + qualifiedActualTableName - + " GROUP BY C5 + 1 ORDER BY 1 DESC"; - final ResultSet expected = getExpectedResultSet("(A VARCHAR(100), B VARCHAR(100))", - "('12.3456789012345678901234567890123457E34', '01.2345123450E4')," // - + "('12.345678911234567890E8', '01.2355123450E4')"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT CAST((\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C5\" + 1) AS FLOAT), CAST(MIN(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C7\") AS FLOAT) FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\" GROUP BY (\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C5\" + 1) ORDER BY (\"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C5\" + 1) DESC"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testAggregateGroupByTuple() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C_NUMBER36, C5, min(C7) FROM " + qualifiedActualTableName - + " GROUP BY C_NUMBER36, C5 ORDER BY C5 DESC"; - final ResultSet expected = getExpectedResultSet("(A DECIMAL(36,0), B VARCHAR(100), C VARCHAR(100))", - "(123456789012345678901234567890123456, '123456789012345678901234567890123456', '12345.12345')," // - + "(null, '1234567890.123456789', '12355.12345')"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT \"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C_NUMBER36\", TO_CHAR(\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C5\"), CAST(MIN(\"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\") AS FLOAT) FROM \"" + SCHEMA_ORACLE + "\".\"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\" GROUP BY \"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C5\", \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C_NUMBER36\" ORDER BY \"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C5\" DESC"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testAggregateHaving() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C5, min(C7) FROM " + qualifiedActualTableName - + " GROUP BY C5 HAVING MIN(C7) > 12350"; - final ResultSet expected = getExpectedResultSet("(A VARCHAR(100), B VARCHAR(100))", - "('1234567890.123456789', '12355.12345')"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT TO_CHAR(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C5\"), CAST(MIN(\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\") AS FLOAT) FROM \"" - + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\" GROUP BY \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C5\" HAVING 12350 < MIN(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C7\")"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testOrderByColumn(final String virtualSchemaName) { - final String qualifiedTableNameActual = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C1 FROM " + qualifiedTableNameActual + " ORDER BY C1 DESC NULLS LAST"; - final String expectedExplainVirtual = "SELECT \"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C1\" FROM \"" - + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\" ORDER BY \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C1\" DESC NULLS LAST"; - assertAll(() -> assertExpressionExecutionStringResult(query, "aaaaaaaaaaaaaaaaaaaa"), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testOrderByExpressionJdbc() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C7 FROM " + qualifiedActualTableName + " ORDER BY ABS(C7) DESC NULLS FIRST"; - final ResultSet expected = getExpectedResultSet("(A DECIMAL(36,5))", "(12355.12345), (12345.12345)"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT \"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\" FROM \"" - + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\" ORDER BY ABS(\"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\") DESC"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testLimit() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C7 FROM " + qualifiedActualTableName + " ORDER BY C7 LIMIT 2"; - final ResultSet expected = getExpectedResultSet("(A DECIMAL(36,5))", "(12345.12345), (12355.12345)"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT LIMIT_SUBSELECT.* FROM ( SELECT \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\" FROM \"" + SCHEMA_ORACLE + "\".\"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\" ORDER BY \"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C7\" ) LIMIT_SUBSELECT WHERE ROWNUM <= 2"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @Test - void testLimitOffset() throws SQLException { - final String qualifiedActualTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C7 FROM " + qualifiedActualTableName + " ORDER BY C7 LIMIT 1 OFFSET 1"; - final ResultSet expected = getExpectedResultSet("(A DECIMAL(36,5))", "(12355.12345)"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT LIMIT_SUBSELECT.*, ROWNUM ROWNUM_SUB FROM ( SELECT \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C7\" AS c0 FROM \"" + SCHEMA_ORACLE + "\".\"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\" ORDER BY \"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C7\" ) LIMIT_SUBSELECT WHERE ROWNUM <= 2 ) WHERE ROWNUM_SUB > 1"; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @ParameterizedTest - @CsvSource(value = { // - "VIRTUAL_SCHEMA_JDBC, C1, CHAR(50) ASCII, aaaaaaaaaaaaaaaaaaaa", // - "VIRTUAL_SCHEMA_JDBC, C2, CHAR(50) UTF8, bbbbbbbbbbbbbbbbbbbb", // - "VIRTUAL_SCHEMA_JDBC, C3, VARCHAR(50) ASCII, cccccccccccccccccccc", // - "VIRTUAL_SCHEMA_JDBC, C4, VARCHAR(50) UTF8, dddddddddddddddddddd", // - "VIRTUAL_SCHEMA_ORA, C1, CHAR(50) ASCII, aaaaaaaaaaaaaaaaaaaa", // - "VIRTUAL_SCHEMA_ORA, C2, CHAR(50) UTF8, bbbbbbbbbbbbbbbbbbbb", // - "VIRTUAL_SCHEMA_ORA, C3, VARCHAR(50) ASCII, cccccccccccccccccccc", // - "VIRTUAL_SCHEMA_ORA, C4, VARCHAR(50) UTF8, dddddddddddddddddddd" // - }) - void testCharactersColumns(final String virtualSchemaName, final String columnName, - final String expectedColumnType, final String expectedColumnValue) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT " + columnName + " FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionStringResult(query, expectedColumnValue), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, columnName), - equalTo(expectedColumnType))); - } - - @ParameterizedTest - @CsvSource(value = { // - "VIRTUAL_SCHEMA_JDBC, C18", // - "VIRTUAL_SCHEMA_JDBC, C19", // - "VIRTUAL_SCHEMA_JDBC, C20", // - "VIRTUAL_SCHEMA_ORA, C18", // - "VIRTUAL_SCHEMA_ORA, C19", // - "VIRTUAL_SCHEMA_ORA, C20",// - }) - void testBlobColumns(final String virtualSchemaName, final String columnName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT " + columnName + " FROM " + qualifiedTableName; - final SQLException exception = assertThrows(SQLException.class, () -> statementExasol.execute(query)); - assertThat(exception.getMessage(), startsWith("object " + columnName + " not found")); - } - - @ParameterizedTest - @CsvSource(value = { // - "VIRTUAL_SCHEMA_JDBC | C5 | VARCHAR(2000000) UTF8 | 123456789012345678901234567890123456", // - "VIRTUAL_SCHEMA_JDBC | C_NUMBER36 | DECIMAL(36,0) | 123456789012345678901234567890123456", // - "VIRTUAL_SCHEMA_JDBC | C6 | VARCHAR(2000000) UTF8 | 12345678901234567890123456789012345678", // - "VIRTUAL_SCHEMA_JDBC | C7 | DECIMAL(10,5) | 12345.12345", // - "VIRTUAL_SCHEMA_ORA | C5 | VARCHAR(2000000) UTF8 | 123456789012345678901234567890123456", // - "VIRTUAL_SCHEMA_ORA | C_NUMBER36 | DECIMAL(36,0) | 123456789012345678901234567890123456", // - "VIRTUAL_SCHEMA_ORA | C6 | VARCHAR(2000000) UTF8 | 12345678901234567890123456789012345678", // - "VIRTUAL_SCHEMA_ORA | C7 | DECIMAL(10,5) | 12345.12345" // - }, delimiter = '|') - void testNumberColumns(final String virtualSchemaName, final String columnName, final String expectedColumnType, - final String expectedValue) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT " + columnName + " FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionBigDecimalResult(query, new BigDecimal(expectedValue)), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, columnName), - equalTo(expectedColumnType))); - } - - @ParameterizedTest - @CsvSource(value = { // - "VIRTUAL_SCHEMA_JDBC | C_BINFLOAT | VARCHAR(2000000) UTF8 | 1234.1241723", // - "VIRTUAL_SCHEMA_JDBC | C_FLOAT | DOUBLE | 12345.01982348239", // - "VIRTUAL_SCHEMA_JDBC | C_FLOAT126 | DOUBLE | 12345678.01234567901234567890123456789", // - "VIRTUAL_SCHEMA_ORA | C_BINFLOAT | VARCHAR(2000000) UTF8 | 1234.1241723", // - "VIRTUAL_SCHEMA_ORA | C_FLOAT | DOUBLE | 12345.01982348239", // - "VIRTUAL_SCHEMA_ORA | C_FLOAT126 | DOUBLE | 12345678.01234567901234567890123456789" // - }, delimiter = '|') - void testFloatNumbers(final String virtualSchemaName, final String columnName, final String expectedColumnType, - final String expectedValue) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT " + columnName + " FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionFloatResult(query, Float.parseFloat(expectedValue)), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, columnName), - equalTo(expectedColumnType))); - } - - private void assertExpressionExecutionFloatResult(final String query, final float expected) - throws SQLException { - final ResultSet result = statementExasol.executeQuery(query); - result.next(); - final double actualResult = result.getFloat(1); - assertEquals(expected, actualResult, 0.000000001); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testBinaryDouble(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C_BINDOUBLE FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionDoubleResult(query, Double.parseDouble("1234987.120871234")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C_BINDOUBLE"), - equalTo("VARCHAR(2000000) UTF8"))); - } - - private void assertExpressionExecutionDoubleResult(final String query, final double expected) - throws SQLException { - final ResultSet result = statementExasol.executeQuery(query); - result.next(); - final double actualResult = result.getDouble(1); - MatcherAssert.assertThat(actualResult, equalTo(expected)); - } - - @Test - void testLongJdbc() { - final String qualifiedTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C_LONG FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionStringResult(query, "test long 123"), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C_LONG"), - equalTo("VARCHAR(2000000) ASCII"))); - } - - @Test - void testLongOra() { - final String qualifiedTableName = VIRTUAL_SCHEMA_ORA + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C_LONG FROM " + qualifiedTableName; - final SQLException exception = assertThrows(SQLException.class, () -> statementExasol.execute(query)); - assertThat(exception.getMessage(), - containsString("Unknown Oracle OCI column data type (8) found for column 'C_LONG'")); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testDate(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C10 FROM " + qualifiedTableName; - assertAll(() -> assertExpressionExecutionDateResult(query, Date.valueOf("2016-08-19")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C10"), equalTo("TIMESTAMP"))); - } - - private void assertExpressionExecutionDateResult(final String query, final Date expected) throws SQLException { - final ResultSet result = statementExasol.executeQuery(query); - result.next(); - final Date actualResult = result.getDate(1); - MatcherAssert.assertThat(actualResult, equalTo(expected)); - } - - @ParameterizedTest - @CsvSource(value = { // - "C11, 2013-03-11 17:30:15.123", // - "C12, 2013-03-11 17:30:15.123", // - "C13, 2013-03-11 17:30:15.123", // - "C14, 2016-08-19 11:28:05.0", // - "C15, 2018-04-30 19:00:05.0" // - }) - void testTimestampsJdbc(final String columnName, final String expectedColumnValue) { - final String qualifiedTableName = VIRTUAL_SCHEMA_JDBC + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT " + columnName + " FROM " + qualifiedTableName; - final String expectedExplainVirtual = "SELECT TO_TIMESTAMP(TO_CHAR(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"" + columnName + "\", ''YYYY-MM-DD HH24:MI:SS.FF3''), " - + "''YYYY-MM-DD HH24:MI:SS.FF3'') FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\""; - assertAll(() -> assertExpressionExecutionTimestampResult(query, Timestamp.valueOf(expectedColumnValue)), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, columnName), equalTo("TIMESTAMP")), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @ParameterizedTest - @CsvSource(value = { // - "C11, 2013-03-11 17:30:15.123", // - "C12, 2013-03-11 17:30:15.123", // - "C13, 2013-03-11 17:30:15.123", // - "C14, 2016-08-19 19:28:05.0", // - "C15, 2018-04-30 18:00:05.0" // - }) - void testTimestampOra(final String columnName, final String expectedColumnValue) throws SQLException { - statementExasol.execute("ALTER SESSION SET TIME_ZONE = 'UTC'"); - final String qualifiedTableName = VIRTUAL_SCHEMA_ORA + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT " + columnName + " FROM " + qualifiedTableName; - final String expectedExplainVirtual = "SELECT \"" + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"" + columnName - + "\" FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\""; - assertAll(() -> assertExpressionExecutionTimestampResult(query, Timestamp.valueOf(expectedColumnValue)), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, columnName), equalTo("TIMESTAMP")), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - private void assertExpressionExecutionTimestampResult(final String query, final Timestamp expected) - throws SQLException { - final ResultSet result = statementExasol.executeQuery(query); - result.next(); - final Timestamp actual = result.getTimestamp(1); - MatcherAssert.assertThat(actual, equalTo(expected)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testIntervalYear(final String virtualSchemaName) { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C16 FROM " + qualifiedTableName; - final String expectedExplainVirtual = "SELECT TO_CHAR(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C16\") FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\""; - assertAll(() -> assertExpressionExecutionStringResult(query, "+54-02"), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C16"), - equalTo("VARCHAR(2000000) UTF8")), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @ParameterizedTest - @ValueSource(strings = { VIRTUAL_SCHEMA_JDBC, VIRTUAL_SCHEMA_ORA }) - void testIntervalDay(final String virtualSchemaName) throws SQLException { - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_ALL_DATA_TYPES; - final String query = "SELECT C17 FROM " + qualifiedTableName + " ORDER BY 1"; - final ResultSet expected = getExpectedResultSet("(A VARCHAR(2000000) UTF8)", - "('+01 11:12:10.123000'), ('+02 02:03:04.123456')"); - final ResultSet actual = statementExasol.executeQuery(query); - final String expectedExplainVirtual = "SELECT TO_CHAR(\"" + TABLE_ORACLE_ALL_DATA_TYPES - + "\".\"C17\") FROM \"" + SCHEMA_ORACLE + "\".\"" + TABLE_ORACLE_ALL_DATA_TYPES + "\" ORDER BY \"" - + TABLE_ORACLE_ALL_DATA_TYPES + "\".\"C17\""; - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C17"), - equalTo("VARCHAR(2000000) UTF8")), - () -> assertExplainVirtual(query, expectedExplainVirtual)); - } - - @ParameterizedTest - @CsvSource(value = { - "VIRTUAL_SCHEMA_JDBC ! ('2018-01-01 11:00:00.0', '2018-01-01 11:00:00.0', '2018-01-01 11:00:00.000')", // - "VIRTUAL_SCHEMA_ORA ! ('2018-01-01 11:00:00.0', '2018-01-01 10:00:00.0', '2018-01-01 10:00:00.000')" }, // - delimiter = '!') - void testSelectAllTimestampColumns(final String virtualSchemaName, final String expectedColumnValue) - throws SQLException { - statementExasol.execute("ALTER SESSION SET TIME_ZONE = 'UTC'"); - final String qualifiedTableName = virtualSchemaName + "." + TABLE_ORACLE_TIMESTAMPS; - final String query = "SELECT * FROM " + qualifiedTableName; - final ResultSet expected = getExpectedResultSet("(A TIMESTAMP, B TIMESTAMP, C TIMESTAMP)", - expectedColumnValue); - final ResultSet actual = statementExasol.executeQuery(query); - assertAll(() -> assertThat(actual, matchesResultSet(expected)), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "A"), equalTo("TIMESTAMP")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "B"), equalTo("TIMESTAMP")), - () -> assertThat(getColumnTypesOfTable(qualifiedTableName, "C"), equalTo("TIMESTAMP"))); - } - } -} diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectTest.java deleted file mode 100644 index 2dfeb3dd9..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlDialectTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.AdapterProperties.*; -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.adapter.dialects.oracle.OracleProperties.ORACLE_IMPORT_PROPERTY; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; -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.*; -import com.exasol.adapter.jdbc.ConnectionFactory; - -@ExtendWith(MockitoExtension.class) -class OracleSqlDialectTest { - private OracleSqlDialect dialect; - @Mock - private ConnectionFactory connectionFactoryMock; - - @BeforeEach - void beforeEach() { - this.dialect = new OracleSqlDialect(this.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, - LIMIT_WITH_OFFSET, JOIN, JOIN_TYPE_INNER, JOIN_TYPE_LEFT_OUTER, JOIN_TYPE_RIGHT_OUTER, - JOIN_TYPE_FULL_OUTER, JOIN_CONDITION_EQUI)), // - () -> 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, - REGEXP_LIKE, 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_DISTINCT, 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, REGEXP_INSTR, REGEXP_REPLACE, REGEXP_SUBSTR, REPEAT, REPLACE, REVERSE, 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, - DBTIMEZONE, LOCALTIMESTAMP, NUMTODSINTERVAL, NUMTOYMINTERVAL, SESSIONTIMEZONE, SYSDATE, - SYSTIMESTAMP, CAST, TO_CHAR, TO_DATE, TO_DSINTERVAL, TO_YMINTERVAL, TO_NUMBER, - TO_TIMESTAMP, BIT_AND, BIT_TO_NUM, CASE, NULLIFZERO, ZEROIFNULL))); - } - - @CsvSource({ "FALSE, FALSE, JDBC", // - "TRUE, FALSE, LOCAL", // - "FALSE, TRUE, ORA" }) - @ParameterizedTest - void testGetImportTypeLocal(final String local, final String fromOracle, final String expectedImportType) { - final OracleSqlDialect dialect = new OracleSqlDialect(null, - new AdapterProperties(Map.of(IS_LOCAL_PROPERTY, local, // - ORACLE_IMPORT_PROPERTY, fromOracle))); - assertThat(dialect.getImportType().toString(), equalTo(expectedImportType)); - } - - @Test - void testCheckOracleSpecificPropertyConsistencyInvalidDialect() { - final SqlDialect sqlDialect = new OracleSqlDialect(null, - new AdapterProperties(Map.of(SQL_DIALECT_PROPERTY, "ORACLE", // - CONNECTION_NAME_PROPERTY, "MY_CONN", // - "ORACLE_CAST_NUMBER_TO_DECIMAL_WITH_PRECISION_AND_SCALE", "MY_CONN"))); - assertThrows(PropertyValidationException.class, sqlDialect::validateProperties); - } - - @Test - void testValidateCatalogProperty() { - final SqlDialect sqlDialect = new OracleSqlDialect(null, new AdapterProperties(Map.of( // - SQL_DIALECT_PROPERTY, "ORACLE", // - CONNECTION_NAME_PROPERTY, "MY_CONN", // - CATALOG_NAME_PROPERTY, "MY_CATALOG"))); - final PropertyValidationException exception = assertThrows(PropertyValidationException.class, - sqlDialect::validateProperties); - MatcherAssert.assertThat(exception.getMessage(), containsString( - "The dialect ORACLE does not support CATALOG_NAME property. Please, do not set the \"CATALOG_NAME\" property.")); - } - - @Test - void testValidateSchemaProperty() throws PropertyValidationException { - final AdapterProperties adapterProperties = new AdapterProperties(Map.of( // - SQL_DIALECT_PROPERTY, "ORACLE", // - CONNECTION_NAME_PROPERTY, "MY_CONN", // - SCHEMA_NAME_PROPERTY, "MY_SCHEMA")); - final SqlDialect sqlDialect = new OracleSqlDialect(null, adapterProperties); - sqlDialect.validateProperties(); - } - - @Test - void testQueryRewriterClassWithImportFromOra() { - this.dialect = new OracleSqlDialect(this.connectionFactoryMock, - this.getAdapaterPropertiesWithImportFromOracle()); - assertThat(this.dialect.createQueryRewriter(), instanceOf(OracleQueryRewriter.class)); - } - - private AdapterProperties getAdapaterPropertiesWithImportFromOracle() { - final Map properties = new HashMap<>(); - properties.put(OracleProperties.ORACLE_IMPORT_PROPERTY, Boolean.TRUE.toString()); - return new AdapterProperties(properties); - } - - @Test - void testQueryRewriterClassWhitImportInto() { - assertThat(this.dialect.createQueryRewriter(), instanceOf(ImportIntoQueryRewriter.class)); - } - - @CsvSource({ "tableName, \"tableName\"", // - "table 'Name, \"table 'Name\"" // - }) - @ParameterizedTest - void testApplyQuote(final String identifier, final String expected) { - assertThat(this.dialect.applyQuote(identifier), equalTo(expected)); - } - - @CsvSource({ "\"tableName\"", "table\"Name", "table name\"" }) - @ParameterizedTest - void testApplyQuoteThrowsException(final String identifier) { - assertThrows(AssertionError.class, () -> this.dialect.applyQuote(identifier)); - } - - @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")); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitorTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitorTest.java deleted file mode 100644 index 6dcc82567..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleSqlGenerationVisitorTest.java +++ /dev/null @@ -1,407 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.dialects.VisitorAssertions.assertSqlNodeConvertedToAsterisk; -import static com.exasol.adapter.dialects.VisitorAssertions.assertSqlNodeConvertedToOne; -import static com.exasol.adapter.sql.AggregateFunction.*; -import static com.exasol.adapter.sql.ScalarFunction.*; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.junit.Assert.assertEquals; - -import java.math.BigDecimal; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -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.junit.jupiter.MockitoExtension; - -import com.exasol.adapter.AdapterException; -import com.exasol.adapter.AdapterProperties; -import com.exasol.adapter.dialects.*; -import com.exasol.adapter.metadata.*; -import com.exasol.adapter.sql.*; -import com.exasol.sql.SqlNormalizer; - -@ExtendWith(MockitoExtension.class) -class OracleSqlGenerationVisitorTest { - private OracleSqlGenerationVisitor visitor; - - @BeforeEach - void beforeEach() { - final SqlDialect dialect = new OracleSqlDialectFactory().createSqlDialect(null, - AdapterProperties.emptyProperties()); - final SqlGenerationContext context = new SqlGenerationContext("test_catalog", "test_schema", false); - this.visitor = new OracleSqlGenerationVisitor(dialect, context); - } - - @Test - void testGetAggregateFunctionsCast() { - assertThat(this.visitor.getAggregateFunctionsCast(), containsInAnyOrder(SUM, MIN, MAX, AVG, MEDIAN, FIRST_VALUE, - LAST_VALUE, STDDEV, STDDEV_POP, STDDEV_SAMP, VARIANCE, VAR_POP, VAR_SAMP)); - } - - @Test - void testGetScalarFunctionsCast() { - assertThat(this.visitor.getScalarFunctionsCast(), - containsInAnyOrder(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)); - } - - @Test - void testVisitSqlStatementSelect() throws AdapterException { - final SqlSelectList selectList = SqlSelectList.createAnyValueSelectList(); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("test_table_name", tableMetadata); - final SqlStatementSelect sqlStatementSelect = SqlStatementSelect.builder().selectList(selectList) - .fromClause(fromClause).build(); - assertThat(this.visitor.visit(sqlStatementSelect), - equalTo("SELECT 1 FROM \"test_schema\".\"test_table_name\"")); - } - - @Test - void testVisitSqlStatementSelectWithLimitAnyValue() throws AdapterException { - final SqlSelectList selectList = SqlSelectList.createAnyValueSelectList(); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("", tableMetadata); - final SqlLimit limit = new SqlLimit(10, 3); - final SqlStatementSelect sqlStatementSelect = SqlStatementSelect.builder().selectList(selectList) - .fromClause(fromClause).limit(limit).build(); - - assertThat(this.visitor.visit(sqlStatementSelect), equalTo("1")); - } - - @Test - void testVisitSqlStatementSelectWithLimitSelectStar() throws AdapterException { - final SqlSelectList selectList = SqlSelectList.createSelectStarSelectList(); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("test_table_name", tableMetadata); - final SqlLimit limit = new SqlLimit(10, 3); - final SqlStatementSelect sqlStatementSelect = SqlStatementSelect.builder().selectList(selectList) - .fromClause(fromClause).limit(limit).build(); - assertThat(this.visitor.visit(sqlStatementSelect), - equalTo("SELECT FROM ( SELECT LIMIT_SUBSELECT.*, ROWNUM " - + "ROWNUM_SUB FROM ( SELECT FROM \"test_schema\".\"test_table_name\" ) LIMIT_SUBSELECT WHERE " - + "ROWNUM <= 13 ) WHERE ROWNUM_SUB > 3")); - } - - @Test - void testVisitSqlStatementSelectWithLimitRegularSelectList() throws AdapterException { - final SqlSelectList selectList = SqlSelectList - .createRegularSelectList(Arrays.asList(new SqlLiteralBool(true), new SqlLiteralString("string"))); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("test_table_name", tableMetadata); - final SqlLimit limit = new SqlLimit(10, 3); - final SqlStatementSelect sqlStatementSelect = SqlStatementSelect.builder().selectList(selectList) - .fromClause(fromClause).limit(limit).build(); - assertThat(this.visitor.visit(sqlStatementSelect), - equalTo("SELECT c0, c1 FROM ( SELECT LIMIT_SUBSELECT.*, ROWNUM " - + "ROWNUM_SUB FROM ( SELECT true AS c0, 'string' AS c1 FROM \"test_schema\".\"test_table_name\"" - + " ) LIMIT_SUBSELECT WHERE ROWNUM <= 13 ) WHERE ROWNUM_SUB > 3")); - } - - @Test - void testVisitSqlStatementSelectWithLimitRegularSelectListWithoutOffset() throws AdapterException { - final SqlSelectList selectList = SqlSelectList - .createRegularSelectList(Arrays.asList(new SqlLiteralBool(true), new SqlLiteralString("string"))); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("test_table_name", tableMetadata); - final SqlLimit limit = new SqlLimit(10); - final SqlStatementSelect sqlStatementSelect = SqlStatementSelect.builder().selectList(selectList) - .fromClause(fromClause).limit(limit).build(); - assertThat(this.visitor.visit(sqlStatementSelect), - equalTo("SELECT LIMIT_SUBSELECT.* FROM ( SELECT true, 'string' FROM \"test_schema\"" - + ".\"test_table_name\" ) LIMIT_SUBSELECT WHERE ROWNUM <= 10")); - } - - @Test - void testVisitSqlSelectListRequiresAnyColumn() throws AdapterException { - final SqlSelectList sqlSelectList = SqlSelectList.createAnyValueSelectList(); - assertSqlNodeConvertedToOne(sqlSelectList, this.visitor); - } - - @Test - void testVisitSqlSelectListSelectStar() throws AdapterException { - final SqlSelectList selectList = createSqlSelectStarListWithOneColumn( - "{\"jdbcDataType\":16, \"typeName\":\"BOOLEAN\"}", DataType.createBool()); - assertSqlNodeConvertedToAsterisk(selectList, this.visitor); - } - - @CsvSource({ "NUMBER", "INTERVAL", "BINARY_FLOAT", "BINARY_DOUBLE" }) - @ParameterizedTest - void testVisitSqlSelectListSelectStarCastToChar(final String dataType) throws AdapterException { - final SqlSelectList selectList = createSqlSelectStarListWithOneColumn( - "{\"jdbcDataType\":2, \"typeName\":\"" + dataType + "\"}", - DataType.createVarChar(50, DataType.ExaCharset.UTF8)); - assertThat(this.visitor.visit(selectList), equalTo("TO_CHAR(\"test_column\")")); - } - - 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 testVisitSqlSelectListSelectStarWithTimestamp() throws AdapterException { - final SqlSelectList selectList = createSqlSelectStarListWithOneColumn( - "{\"jdbcDataType\":2, \"typeName\":\"TIMESTAMP\"}", - DataType.createVarChar(50, DataType.ExaCharset.UTF8)); - assertThat(this.visitor.visit(selectList), equalTo( - "TO_TIMESTAMP(TO_CHAR(\"test_column\", 'YYYY-MM-DD HH24:MI:SS.FF3'), 'YYYY-MM-DD HH24:MI:SS.FF3')")); - } - - @Test - void testVisitSqlSelectListSelectStarNumberCastToDecimal() throws AdapterException { - final SqlSelectList selectList = SqlSelectList.createSelectStarSelectList(); - final List columns = new ArrayList<>(); - columns.add(ColumnMetadata.builder().name("test_column") - .adapterNotes("{\"jdbcDataType\":2, \"typeName\":\"NUMBER\"}").type(DataType.createDouble()).build()); - final TableMetadata tableMetadata = new TableMetadata("", "", columns, ""); - final SqlTable fromClause = new SqlTable("", tableMetadata); - final SqlNode select = SqlStatementSelect.builder().selectList(selectList).fromClause(fromClause).build(); - selectList.setParent(select); - assertThat(this.visitor.visit(selectList), equalTo("CAST(\"test_column\" AS DECIMAL(0,0))")); - } - - @Test - void testVisitSqlSelectListRegularSelectList() throws AdapterException { - final SqlSelectList selectList = SqlSelectList - .createRegularSelectList(Arrays.asList(new SqlLiteralBool(true), new SqlLiteralString("string"))); - final TableMetadata tableMetadata = new TableMetadata("", "", Collections.emptyList(), ""); - final SqlTable fromClause = new SqlTable("", tableMetadata); - final SqlNode select = SqlStatementSelect.builder().selectList(selectList).fromClause(fromClause).build(); - selectList.setParent(select); - assertThat(this.visitor.visit(selectList), equalTo("true, 'string'")); - } - - @Test - void testVisitSqlPredicateLikeRegexp() throws AdapterException { - final SqlPredicateLikeRegexp sqlSelectList = new SqlPredicateLikeRegexp(new SqlLiteralString("abcd"), - new SqlLiteralString("a_d")); - assertThat(this.visitor.visit(sqlSelectList), equalTo("REGEXP_LIKE('abcd', 'a_d')")); - } - - @Test - void testVisitSqlLiteralExactnumeric() { - final SqlLiteralExactnumeric literalExactnumeric = new SqlLiteralExactnumeric(new BigDecimal("5.9")); - assertThat(this.visitor.visit(literalExactnumeric), equalTo("5.9")); - } - - @Test - void testVisitSqlLiteralExactnumericInSelectList() { - final SqlSelectList selectList = SqlSelectList.createSelectStarSelectList(); - final SqlLiteralExactnumeric literalExactnumeric = new SqlLiteralExactnumeric(new BigDecimal("5.9")); - literalExactnumeric.setParent(selectList); - assertThat(this.visitor.visit(literalExactnumeric), equalTo("TO_CHAR(5.9)")); - } - - @Test - void testVisitSqlLiteralDouble() { - final SqlLiteralDouble literalDouble = new SqlLiteralDouble(10.6); - assertThat(this.visitor.visit(literalDouble), equalTo("10.6")); - } - - @Test - void testVisitSqlLiteralDoubleInSelectList() { - final SqlSelectList selectList = SqlSelectList.createSelectStarSelectList(); - final SqlLiteralDouble literalDouble = new SqlLiteralDouble(10.6); - literalDouble.setParent(selectList); - assertThat(this.visitor.visit(literalDouble), equalTo("TO_CHAR(10.6)")); - } - - @Test - void testVisitSqlFunctionAggregateGroupConcat() throws AdapterException { - final SqlFunctionAggregateGroupConcat aggregateGroupConcat = SqlFunctionAggregateGroupConcat - .builder(new SqlLiteralDouble(10.5)).separator(new SqlLiteralString("'")).build(); - assertThat(this.visitor.visit(aggregateGroupConcat), - equalTo("LISTAGG(10.5, '''') WITHIN GROUP(ORDER BY 10.5)")); - } - - @Test - void testVisitSqlFunctionAggregateGroupConcatWithOrderBy() throws AdapterException { - 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, Stream.of(false, true).collect(Collectors.toList()), - Stream.of(false, true).collect(Collectors.toList())); - final SqlFunctionAggregateGroupConcat aggregateGroupConcat = SqlFunctionAggregateGroupConcat - .builder(new SqlLiteralDouble(10.5)).separator(new SqlLiteralString("'")).orderBy(orderBy) - .distinct(true).build(); - assertThat(this.visitor.visit(aggregateGroupConcat), equalTo( - "LISTAGG(10.5, '''') WITHIN GROUP(ORDER BY \"test_column\" DESC NULLS FIRST, \"test_column2\")")); - } - - @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 testVisitSqlFunctionAggregateInSelectList() 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, false); - final SqlNode selectList = SqlSelectList.createSelectStarSelectList(); - sqlFunctionAggregate.setParent(selectList); - assertThat(this.visitor.visit(sqlFunctionAggregate), equalTo("CAST(AVG(\"test_column\") AS FLOAT)")); - } - - @Test - void testVisitSqlFunctionScalarLocateThreeArguments() throws AdapterException { - final List arguments = new ArrayList<>(); - arguments.add(new SqlLiteralString("ab ")); - arguments.add(new SqlLiteralString("ab cdef")); - arguments.add(new SqlLiteralString("ab cdef rty")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(LOCATE, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("INSTR('ab cdef', 'ab ', 'ab cdef rty')")); - } - - @Test - void testVisitSqlFunctionScalarTrimOneArgument() throws AdapterException { - final List arguments = new ArrayList<>(); - arguments.add(new SqlLiteralString("test")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(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(TRIM, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("TRIM('ab' FROM 'ab cdef')")); - } - - @CsvSource({ "ADD_DAYS, '10' DAY", // - "ADD_HOURS, '10' HOUR", // - "ADD_MINUTES, '10' MINUTE", // - "ADD_SECONDS, '10' SECOND", // - "ADD_YEARS, '10' YEAR", // - "ADD_WEEKS, '70' DAY" }) - @ParameterizedTest - void testVisitSqlFunctionScalarAddDateValues(final ScalarFunction scalarFunction, final String expected) - throws AdapterException { - final ColumnMetadata columnMetadata = ColumnMetadata.builder().name("test_column") - .adapterNotes("{\"jdbcDataType\":93, " + "\"typeName\":\"TIMESTAMP\"}") - .type(DataType.createChar(20, DataType.ExaCharset.UTF8)).build(); - final List arguments = List.of(new SqlColumn(1, columnMetadata), - new SqlLiteralExactnumeric(new BigDecimal(10))); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(scalarFunction, arguments); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("(\"test_column\" + INTERVAL " + expected + ")")); - } - - @CsvSource({ "CURRENT_DATE, CURRENT_DATE", // - "CURRENT_TIMESTAMP, CURRENT_TIMESTAMP", // - "DBTIMEZONE, DBTIMEZONE", // - "LOCALTIMESTAMP, LOCALTIMESTAMP", // - "SESSIONTIMEZONE, SESSIONTIMEZONE", // - "SYSDATE, TO_DATE(SYSDATE)", // - "SYSTIMESTAMP, SYSTIMESTAMP" }) - @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 : NVL('left', 0)", // - "DIV : CAST(FLOOR('left' / 'right') AS NUMBER(36, 0))", // - "COT : (1 / TAN('left'))", // - "DEGREES : (('left') * 180 / ACOS(-1))", // - "RADIANS : (('left') * ACOS(-1) / 180)", // - "REPEAT : RPAD(TO_CHAR('left'), LENGTH('left') * ROUND('right'), 'left')", // - "REVERSE : REVERSE(TO_CHAR('left'))" // - }, 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 testVisitSqlFunctionScalarInSelectList() throws AdapterException { - final SqlSelectList selectList = SqlSelectList.createSelectStarSelectList(); - final List arguments = List.of(new SqlLiteralString("test"), new SqlLiteralString("")); - final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(TANH, arguments); - sqlFunctionScalar.setParent(selectList); - assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("CAST(TANH('test', '') AS FLOAT)")); - } - - @Test - void testSqlGeneratorWithLimit() throws AdapterException { - final String expectedSql = "SELECT LIMIT_SUBSELECT.* FROM ( " + // - " 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\" " + // - ") LIMIT_SUBSELECT WHERE ROWNUM <= 10"; // - final SqlStatementSelect testSqlNode = (SqlStatementSelect) DialectTestData.getTestSqlNode(); - final String actualSql = this.visitor.visit(testSqlNode); - assertEquals(SqlNormalizer.normalizeSql(expectedSql), SqlNormalizer.normalizeSql(actualSql)); - } - - @Test - void testSqlGeneratorWithLimitOffset() throws AdapterException { - final String expectedSql = "SELECT c0, c1 FROM (" + // - " SELECT LIMIT_SUBSELECT.*, ROWNUM ROWNUM_SUB FROM ( " + // - " SELECT \"USER_ID\" AS c0, COUNT(\"URL\") AS c1 " + // - " FROM \"test_schema\".\"CLICKS\"" + // - " WHERE 1 < \"USER_ID\"" + // - " GROUP BY \"USER_ID\"" + // - " HAVING 1 < COUNT(\"URL\")" + // - " ORDER BY \"USER_ID\"" + // - " ) LIMIT_SUBSELECT WHERE ROWNUM <= 15 " + // - ") WHERE ROWNUM_SUB > 5"; - final SqlStatementSelect testSqlNode = (SqlStatementSelect) DialectTestData.getTestSqlNode(); - testSqlNode.getLimit().setOffset(5); - final String actualSql = this.visitor.visit(testSqlNode); - assertEquals(SqlNormalizer.normalizeSql(expectedSql), SqlNormalizer.normalizeSql(actualSql)); - } - - @Test - void testSqlGeneratorWithSelectStarAndOffset() throws AdapterException { - SqlStatementSelect node = (SqlStatementSelect) DialectTestData.getTestSqlNode(); - node.getLimit().setOffset(5); - node = SqlStatementSelect.builder().selectList(SqlSelectList.createSelectStarSelectList()) - .fromClause(node.getFromClause()).whereClause(node.getWhereClause()).groupBy(node.getGroupBy()) - .having(node.getHaving()).orderBy(node.getOrderBy()).limit(node.getLimit()).build(); - final String expectedSql = "SELECT c0, c1 FROM (" + // - " SELECT LIMIT_SUBSELECT.*, ROWNUM ROWNUM_SUB FROM ( " + // - " SELECT \"USER_ID\" AS c0, \"URL\" AS c1 " + // - " FROM \"test_schema\".\"CLICKS\"" + // - " WHERE 1 < \"USER_ID\"" + // - " GROUP BY \"USER_ID\"" + // - " HAVING 1 < COUNT(\"URL\")" + // - " ORDER BY \"USER_ID\"" + // - " ) LIMIT_SUBSELECT WHERE ROWNUM <= 15 " + // - ") WHERE ROWNUM_SUB > 5"; - final String actualSql = this.visitor.visit(node); - assertEquals(SqlNormalizer.normalizeSql(expectedSql), SqlNormalizer.normalizeSql(actualSql)); - } -} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReaderTest.java deleted file mode 100644 index df6d799e2..000000000 --- a/src/test/java/com/exasol/adapter/dialects/oracle/OracleTableMetadataReaderTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.exasol.adapter.dialects.oracle; - -import static com.exasol.adapter.jdbc.TableMetadataMockUtils.*; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -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.AdapterProperties; -import com.exasol.adapter.dialects.BaseIdentifierConverter; -import com.exasol.adapter.jdbc.ColumnMetadataReader; -import com.exasol.adapter.jdbc.TableMetadataReader; -import com.exasol.adapter.metadata.DataType; -import com.exasol.adapter.metadata.TableMetadata; - -@ExtendWith(MockitoExtension.class) -class OracleTableMetadataReaderTest { - private TableMetadataReader reader; - @Mock - private ResultSet tablesMock; - @Mock - private ColumnMetadataReader columnMetadataReaderMock; - - @BeforeEach - void beforeEach() { - this.reader = new OracleTableMetadataReader(null, this.columnMetadataReaderMock, - AdapterProperties.emptyProperties(), BaseIdentifierConverter.createDefault()); - } - - @CsvSource({ "ANY_TABLE_NAME, true", "BIN$FOO, false" }) - @ParameterizedTest - void testIsTableIncludedByMapping(final String tableName, final boolean expectedIncluded) { - assertThat(this.reader.isTableIncludedByMapping(tableName), equalTo(expectedIncluded)); - } - - @Test - void testTablesInTrashBinAreNotMapped() throws SQLException { - mockTableCount(this.tablesMock, 3); - mockTableName(this.tablesMock, TABLE_A, TABLE_B, "BIN$TRASHED"); - mockTableWithColumnsOfType(this.tablesMock, this.columnMetadataReaderMock, TABLE_A, DataType.createBool()); - mockTableWithColumnsOfType(this.tablesMock, this.columnMetadataReaderMock, TABLE_B, DataType.createBool()); - final List tableNames = this.reader.mapTables(this.tablesMock, Optional.empty()) // - .stream() // - .map(TableMetadata::getName) // - .collect(Collectors.toList()); - assertThat(tableNames, containsInAnyOrder(TABLE_A, TABLE_B)); - } -} \ No newline at end of file diff --git a/src/test/resources/integration/driver/oracle/oracle.properties b/src/test/resources/integration/driver/oracle/oracle.properties deleted file mode 100644 index 7f6b53b4b..000000000 --- a/src/test/resources/integration/driver/oracle/oracle.properties +++ /dev/null @@ -1,4 +0,0 @@ -driver.name=ojdbc8.jar -driver.path=src/test/resources/integration/driver/oracle -instant.client.name=instantclient-basic-linux.x64-12.1.0.2.0.zip -instant.client.path=src/test/resources/integration/driver/oracle \ No newline at end of file diff --git a/src/test/resources/integration/driver/oracle/settings.cfg b/src/test/resources/integration/driver/oracle/settings.cfg deleted file mode 100644 index 3af31a4ce..000000000 --- a/src/test/resources/integration/driver/oracle/settings.cfg +++ /dev/null @@ -1,6 +0,0 @@ -DRIVERNAME=ORACLE -JAR=ojdbc8.jar -DRIVERMAIN=oracle.jdbc.driver.OracleDriver -PREFIX=jdbc:oracle:thin: -FETCHSIZE=100000 -INSERTSIZE=-1