diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afcee50..78d62a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,8 @@ on: jobs: matrix-build: name: Build and test with Java ${{ matrix.java }} + permissions: + contents: read strategy: matrix: java: ['17', '21'] @@ -31,7 +33,7 @@ jobs: key: ${{ runner.os }}-sonar-${{ hashFiles('**/*.gradle') }} restore-keys: ${{ runner.os }}-sonar- - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/actions/wrapper-validation@v3 - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v4 @@ -47,23 +49,14 @@ jobs: run: | ./gradlew clean build --info \ --exclude-task integrationTest \ - -PjavaVersion=${{matrix.java}} \ - -Dsonar.gradle.skipCompile=true - - - name: Publish Test Report for Java ${{ matrix.java }} - uses: scacap/action-surefire-report@v1 - if: ${{ always() && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' }} - with: - report_paths: '**/build/test-results/*/TEST-*.xml' - github_token: ${{ secrets.GITHUB_TOKEN }} + -PjavaVersion=${{matrix.java}} - name: Sonar analysis if: ${{ env.DEFAULT_JAVA == matrix.java && env.SONAR_TOKEN != null }} run: | ./gradlew sonar --info \ --exclude-task integrationTest \ - -Dsonar.token=$SONAR_TOKEN \ - -Dsonar.gradle.skipCompile=true + -Dsonar.token=$SONAR_TOKEN env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index de2633d..6f63d23 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,8 +5,8 @@ on: branches: [main] pull_request: branches: [main] - schedule: - - cron: '0 7 * * 6' + #schedule: + # - cron: '0 7 * * 6' jobs: analyze: @@ -15,6 +15,9 @@ jobs: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true + permissions: + contents: read + security-events: write steps: - name: Checkout repository @@ -29,7 +32,7 @@ jobs: java-version: 17 cache: 'gradle' - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/actions/wrapper-validation@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index f1821e0..6ad1edc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.7.0] - unreleased +- [PR #22](https://github.com/itsallcode/simple-jdbc/pull/22): Added `module-info.java` + ## [0.6.1] - 2024-01-14 - [PR #16](https://github.com/itsallcode/simple-jdbc/pull/16): Improve test coverage diff --git a/build.gradle b/build.gradle index a59f999..a534c49 100644 --- a/build.gradle +++ b/build.gradle @@ -5,9 +5,9 @@ plugins { id 'jacoco-report-aggregation' id 'signing' id 'maven-publish' - id 'org.sonarqube' version '4.4.1.3373' - id "io.github.gradle-nexus.publish-plugin" version "1.3.0" - id 'com.github.ben-manes.versions' version '0.50.0' + id 'org.sonarqube' version '5.0.0.4638' + id "io.github.gradle-nexus.publish-plugin" version "2.0.0" + id 'com.github.ben-manes.versions' version '0.51.0' } group 'org.itsallcode' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd49..e644113 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e09..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..25da30d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/settings.gradle b/settings.gradle index 3e49826..14aea87 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,18 +7,18 @@ dependencyResolutionManagement { } versionCatalogs { libs { - library('assertj', 'org.assertj:assertj-core:3.25.1') + library('assertj', 'org.assertj:assertj-core:3.25.3') library('h2', 'com.h2database:h2:2.2.224') library('junitPioneer', 'org.junit-pioneer:junit-pioneer:2.2.0') - library('equalsverifier', 'nl.jqno.equalsverifier:equalsverifier:3.15.6') + library('equalsverifier', 'nl.jqno.equalsverifier:equalsverifier:3.16.1') library('tostringverifier', 'com.jparams:to-string-verifier:1.4.8') library('hamcrest', 'org.hamcrest:hamcrest-all:1.3') library('hamcrestResultSetMatcher', 'com.exasol:hamcrest-resultset-matcher:1.6.3') - library('mockito', 'org.mockito:mockito-core:5.8.0') - library('mockitoJunit', 'org.mockito:mockito-junit-jupiter:5.8.0') - library('slf4jLogger', 'org.slf4j:slf4j-jdk14:2.0.11') - library('exasolJdbc', 'com.exasol:exasol-jdbc:7.1.20') - library('exasolTestcontainers', 'com.exasol:exasol-testcontainers:7.0.0') + library('mockito', 'org.mockito:mockito-core:5.11.0') + library('mockitoJunit', 'org.mockito:mockito-junit-jupiter:5.11.0') + library('slf4jLogger', 'org.slf4j:slf4j-jdk14:2.0.13') + library('exasolJdbc', 'com.exasol:exasol-jdbc:24.1.0') + library('exasolTestcontainers', 'com.exasol:exasol-testcontainers:7.1.0') } } } diff --git a/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java b/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java index f34dc55..920a666 100644 --- a/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java +++ b/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java @@ -4,16 +4,15 @@ import static org.junit.jupiter.api.Assertions.assertAll; import java.math.BigDecimal; +import java.sql.Date; import java.sql.JDBCType; import java.time.Instant; import java.time.LocalDate; import java.util.stream.Stream; import org.itsallcode.jdbc.resultset.SimpleResultSet; -import org.itsallcode.jdbc.resultset.generic.ColumnValue; -import org.itsallcode.jdbc.resultset.generic.Row; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.itsallcode.jdbc.resultset.generic.*; +import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -23,7 +22,7 @@ class ExasolTypeTest { - private static final ExasolContainer container = new ExasolContainer<>("8.24.0") + private static final ExasolContainer container = new ExasolContainer<>("8.27.0") .withRequiredServices(ExasolService.JDBC).withReuse(true); @BeforeAll @@ -44,14 +43,19 @@ SimpleConnection connect() { @ParameterizedTest @MethodSource("testTypes") void genericRowType(final TypeTest test) { - try (SimpleResultSet result = connect() + try (final SimpleResultSet result = connect() .query("select cast('" + test.value() + "' as " + test.type() + ")")) { final ColumnValue columnValue = result.toList().get(0).get(0); final Object value = columnValue.value(); assertAll( () -> assertThat(value.getClass()).isEqualTo(test.expectedValue().getClass()), () -> assertThat(value).isEqualTo(test.expectedValue()), - () -> assertThat(columnValue.type().jdbcType()).isEqualTo(test.expectedType())); + () -> assertThat(columnValue.type().jdbcType()).as("jdbc type") + .isEqualTo(test.expectedType()), + () -> assertThat(columnValue.type().typeName()).as("type name") + .isEqualTo(test.expectedTypeName()), + () -> assertThat(columnValue.type().className()).as("type class name") + .isEqualTo(test.expectedClassName())); } } @@ -63,7 +67,10 @@ void genericRowNullValue(final TypeTest test) { final ColumnValue value = result.toList().get(0).get(0); assertAll( () -> assertThat(value.value()).isNull(), - () -> assertThat(value.type().jdbcType()).isEqualTo(test.expectedType())); + () -> assertThat(value.type().jdbcType()).as("jdbc type").isEqualTo(test.expectedType()), + () -> assertThat(value.type().typeName()).as("type name").isEqualTo(test.expectedTypeName()), + () -> assertThat(value.type().className()).as("type class name") + .isEqualTo(test.expectedClassName())); } } @@ -85,44 +92,81 @@ void resultSetValueTypes(final TypeTest test) { static Stream testTypes() { return Stream.of( typeTest("2023-11-25 16:18:46", "timestamp", Instant.parse("2023-11-25T16:18:46.0Z"), - JDBCType.TIMESTAMP), - typeTest("2023-11-25", "date", LocalDate.parse("2023-11-25"), JDBCType.DATE), - typeTest("5-3", "INTERVAL YEAR TO MONTH", "+05-03", JDBCType.VARCHAR), - typeTest("2 12:50:10.123", "INTERVAL DAY TO SECOND", "+02 12:50:10.123", JDBCType.VARCHAR), - typeTest("POINT(1 2)", "GEOMETRY", "POINT (1 2)", JDBCType.VARCHAR), - typeTest("550e8400-e29b-11d4-a716-446655440000", "HASHTYPE", "550e8400e29b11d4a716446655440000", - JDBCType.CHAR), - typeTest("text", "VARCHAR(10)", "text", JDBCType.VARCHAR), - typeTest("text", "CHAR(10)", "text ", JDBCType.CHAR), - typeTest("123.456", "DECIMAL", 123L, JDBCType.BIGINT), - typeTest("123", "SHORTINT", 123, JDBCType.INTEGER), - typeTest("123", "SMALLINT", 123, JDBCType.INTEGER), - typeTest("123", "TINYINT", (short) 123, JDBCType.SMALLINT), - typeTest("123", "DECIMAL(4,0)", (short) 123, JDBCType.SMALLINT), - typeTest("123", "DECIMAL(5,0)", 123, JDBCType.INTEGER), - typeTest("123", "DECIMAL(9,0)", 123, JDBCType.INTEGER), - typeTest("123", "DECIMAL(10,0)", 123L, JDBCType.BIGINT), - typeTest("123", "DECIMAL(18,0)", 123L, JDBCType.BIGINT), - typeTest("123", "DECIMAL(19,0)", BigDecimal.valueOf(123), JDBCType.DECIMAL), - typeTest("123", "INT", 123L, JDBCType.BIGINT), - typeTest("123", "BIGINT", BigDecimal.valueOf(123), JDBCType.DECIMAL), - typeTest("123", "DEC", 123L, JDBCType.BIGINT), - typeTest("123", "NUMERIC", 123L, JDBCType.BIGINT), - typeTest("123.457", "DECIMAL(6,3)", BigDecimal.valueOf(123.457d), JDBCType.DECIMAL), - typeTest("123.458", "DOUBLE", 123.458d, JDBCType.DOUBLE), - typeTest("123.458", "FLOAT", 123.458d, JDBCType.DOUBLE), - typeTest("123.458", "NUMBER", 123.458d, JDBCType.DOUBLE), - typeTest("123.458", "REAL", 123.458d, JDBCType.DOUBLE), - typeTest("123.458", "DOUBLE PRECISION", 123.458d, JDBCType.DOUBLE), - typeTest("true", "BOOLEAN", true, JDBCType.BOOLEAN)); + JDBCType.TIMESTAMP, "TIMESTAMP", "java.sql.TimeStamp"), + typeTest("2023-11-25 16:18:46", "timestamp with local time zone", + Instant.parse("2023-11-25T16:18:46.0Z"), + JDBCType.TIMESTAMP, "TIMESTAMP", "java.sql.TimeStamp"), + typeTest("2023-11-25", "date", LocalDate.parse("2023-11-25"), JDBCType.DATE, "DATE", + Date.class), + typeTest("5-3", "INTERVAL YEAR TO MONTH", "+05-03", JDBCType.VARCHAR, + "INTERVAL YEAR TO MONTH", + String.class), + typeTest("2 12:50:10.123", "INTERVAL DAY TO SECOND", "+02 12:50:10.123", + JDBCType.VARCHAR, + "INTERVAL DAY TO SECOND", String.class), + typeTest("POINT(1 2)", "GEOMETRY", "POINT (1 2)", JDBCType.VARCHAR, "GEOMETRY", + String.class), + typeTest("550e8400-e29b-11d4-a716-446655440000", "HASHTYPE", + "550e8400e29b11d4a716446655440000", + JDBCType.CHAR, "HASHTYPE", String.class), + typeTest("text", "VARCHAR(10)", "text", JDBCType.VARCHAR, "VARCHAR", String.class), + typeTest("text", "CHAR(10)", "text ", JDBCType.CHAR, "CHAR", String.class), + typeTest("123.456", "DECIMAL", 123L, JDBCType.BIGINT, "BIGINT", Long.class), + typeTest("123", "SHORTINT", 123, JDBCType.INTEGER, "INTEGER", Integer.class), + typeTest("123", "SMALLINT", 123, JDBCType.INTEGER, "INTEGER", Integer.class), + typeTest("123", "TINYINT", (short) 123, JDBCType.SMALLINT, "SMALLINT", Short.class), + typeTest("123", "DECIMAL(4,0)", (short) 123, JDBCType.SMALLINT, "SMALLINT", Short.class), + typeTest("123", "DECIMAL(5,0)", 123, JDBCType.INTEGER, "INTEGER", Integer.class), + typeTest("123", "DECIMAL(9,0)", 123, JDBCType.INTEGER, "INTEGER", Integer.class), + typeTest("123", "DECIMAL(10,0)", 123L, JDBCType.BIGINT, "BIGINT", Long.class), + typeTest("123", "DECIMAL(18,0)", 123L, JDBCType.BIGINT, "BIGINT", Long.class), + typeTest("123", "DECIMAL(19,0)", BigDecimal.valueOf(123), JDBCType.DECIMAL, "DECIMAL", + BigDecimal.class), + typeTest("123", "INT", 123L, JDBCType.BIGINT, "BIGINT", Long.class), + typeTest("123", "BIGINT", BigDecimal.valueOf(123), JDBCType.DECIMAL, "DECIMAL", + BigDecimal.class), + typeTest("123", "DEC", 123L, JDBCType.BIGINT, "BIGINT", Long.class), + typeTest("123", "NUMERIC", 123L, JDBCType.BIGINT, "BIGINT", Long.class), + typeTest("123.457", "DECIMAL(6,3)", BigDecimal.valueOf(123.457d), JDBCType.DECIMAL, "DECIMAL", + BigDecimal.class), + typeTest("123.458", "DOUBLE", 123.458d, JDBCType.DOUBLE, "DOUBLE PRECISION", + Double.class), + typeTest("123.458", "FLOAT", 123.458d, JDBCType.DOUBLE, "DOUBLE PRECISION", + Double.class), + typeTest("123.458", "NUMBER", 123.458d, JDBCType.DOUBLE, "DOUBLE PRECISION", + Double.class), + typeTest("123.458", "REAL", 123.458d, JDBCType.DOUBLE, "DOUBLE PRECISION", + Double.class), + typeTest("123.458", "DOUBLE PRECISION", 123.458d, JDBCType.DOUBLE, "DOUBLE PRECISION", + Double.class), + typeTest("true", "BOOLEAN", true, JDBCType.BOOLEAN, "BOOLEAN", Boolean.class)); + } + + @Test + void countStarResultType() { + try (final SimpleConnection connection = connect(); + final SimpleResultSet result = connection + .query("select count(*) from (select 1 from dual)")) { + final Row row = result.toList().get(0); + final ColumnMetaData columnMetaData = row.columns().get(0); + assertThat(columnMetaData.type().jdbcType()).isEqualTo(JDBCType.BIGINT); + } + } + + private static Arguments typeTest(final String value, final String type, final Object expectedValue, + final JDBCType expectedType, final String expectedTypeName, final Class expectedClass) { + return typeTest(value, type, expectedValue, expectedType, expectedTypeName, expectedClass.getName()); } private static Arguments typeTest(final String value, final String type, final Object expectedValue, - final JDBCType expectedType) { - return Arguments.of(new TypeTest(value, type, expectedValue, expectedType)); + final JDBCType expectedType, final String expectedTypeName, final String expectedClassName) { + return Arguments + .of(new TypeTest(value, type, expectedValue, expectedType, expectedTypeName, + expectedClassName)); } - record TypeTest(String value, String type, Object expectedValue, JDBCType expectedType) { + record TypeTest(String value, String type, Object expectedValue, JDBCType expectedType, String expectedTypeName, + String expectedClassName) { } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..41bed2b --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,19 @@ +/** + * Module containing a simple wrapper for the JDBC API. + */ + +module org.itsallcode.jdbc { + exports org.itsallcode.jdbc; + exports org.itsallcode.jdbc.identifier; + exports org.itsallcode.jdbc.resultset; + exports org.itsallcode.jdbc.resultset.generic; + exports org.itsallcode.jdbc.dialect; + + requires java.logging; + requires transitive java.sql; + + uses org.itsallcode.jdbc.dialect.DbDialect; + + provides org.itsallcode.jdbc.dialect.DbDialect + with org.itsallcode.jdbc.dialect.ExasolDialect, org.itsallcode.jdbc.dialect.H2Dialect; +}